@martintrojer/mu 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +343 -0
- package/README.md +189 -0
- package/dist/cli.js +11260 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +3130 -0
- package/dist/index.js +6312 -0
- package/dist/index.js.map +1 -0
- package/docs/ARCHITECTURE.md +481 -0
- package/docs/ROADMAP.md +542 -0
- package/docs/USAGE_GUIDE.md +1631 -0
- package/docs/VISION.md +440 -0
- package/docs/VOCABULARY.md +349 -0
- package/package.json +76 -0
- package/skills/mu/SKILL.md +523 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/db.ts","../src/logs.ts","../src/detect.ts","../src/tmux.ts","../src/reconcile.ts","../src/snapshots.ts","../src/tasks/errors.ts","../src/workstream.ts","../src/exporting.ts","../src/archives.ts","../src/vcs.ts","../src/workspace.ts","../src/output.ts","../src/agents/spawn.ts","../src/agents/errors.ts","../src/tasks/status.ts","../src/tasks/wait.ts","../src/tasks/lifecycle.ts","../src/tasks/claim.ts","../src/tasks.ts","../src/agents/adopt.ts","../src/agents.ts","../src/cli/tasks/queries.ts","../src/cli/tasks/edit.ts","../src/cli/tasks/claim.ts","../src/cli/tasks/edges.ts","../src/cli/tasks/lifecycle.ts","../src/cli/tasks/tree.ts","../src/cli/tasks/wire.ts","../src/cli/agents.ts","../src/cli/archive.ts","../src/cli/doctor.ts","../src/cli/handle.ts","../src/importing.ts","../src/cli/log.ts","../src/cli/snapshot.ts","../src/cli/sql.ts","../src/cli/state.ts","../src/tracks.ts","../src/cli/workspace.ts","../src/cli/workstream.ts","../src/cli/format.ts"],"sourcesContent":["#!/usr/bin/env node\n// mu — command-line interface.\n//\n// 10 verbs + mission control, each registered via commander as a thin\n// wrapper around the corresponding programmatic function in src/. The\n// real work happens in agents.ts, tasks.ts, tracks.ts, db.ts, tmux.ts;\n// this file is just argument parsing, output formatting, and error-to-\n// exit-code translation.\n//\n// Layout: cli.ts is the wiring root. Pure rendering helpers live in\n// `src/cli/format.ts`; typed-error → exit-code mapping + the `handle`\n// wrapper live in `src/cli/handle.ts` (extracted by\n// review_cli_ts_past_refactor_signal). cli.ts re-exports their public\n// surface so existing tests + cli/* importers don't need to change\n// import paths. The exit-code catalogue is documented at the top of\n// `src/cli/handle.ts`.\n\nimport { readFileSync, realpathSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath, pathToFileURL } from \"node:url\";\nimport { Command, InvalidArgumentError } from \"commander\";\n\nimport { AgentNotInWorkstreamError, type AgentRow, getAgentByPane } from \"./agents.js\";\nimport { wireAgentCommands, wireSelfCommands } from \"./cli/agents.js\";\nimport { wireArchiveCommands } from \"./cli/archive.js\";\nimport { wireDoctorCommand } from \"./cli/doctor.js\";\nimport {\n NameAmbiguousError,\n UsageError,\n emitParseError,\n findCommandForArgv,\n handle,\n} from \"./cli/handle.js\";\nimport { wireLogCommand } from \"./cli/log.js\";\nimport { wireSnapshotCommands } from \"./cli/snapshot.js\";\nimport { wireSqlCommand } from \"./cli/sql.js\";\nimport { cmdState, wireStateCommands } from \"./cli/state.js\";\nimport { wireTaskCommands } from \"./cli/tasks.js\";\nimport { wireWorkspaceCommands } from \"./cli/workspace.js\";\nimport { wireWorkstreamCommands } from \"./cli/workstream.js\";\nimport type { Db } from \"./db.js\";\nimport {\n TASK_STATUS_LIST,\n TaskNotInWorkstreamError,\n type TaskRow,\n type TaskStatus,\n isTaskStatus,\n} from \"./tasks.js\";\nimport { tmux } from \"./tmux.js\";\nimport { RESERVED_WORKSTREAM_PREFIX } from \"./workstream.js\";\n\n// ─── Re-exports for back-compat (cli/* + test imports) ────────────────\n//\n// Pure rendering helpers and the typed-error catalogue live in\n// `src/cli/format.ts` and `src/cli/handle.ts` respectively. cli.ts is\n// the canonical import path for every other module — re-export so the\n// 30-ish import sites keep working without churn.\n\nexport {\n IDLE_GLYPH,\n colorStatus,\n formatAgentsTable,\n formatReadyTable,\n formatTaskListTable,\n formatTracks,\n formatWorkspacesTable,\n formatWorkstreamsTable,\n printLogRow,\n relTime,\n statusIcon,\n truncate,\n truncateFront,\n} from \"./cli/format.js\";\nexport { NameAmbiguousError, UsageError, classifyError, handle } from \"./cli/handle.js\";\n\n// ─── Workstream resolution ─────────────────────────────────────────────\n\n/**\n * Resolve the active workstream. Order:\n * 1. --workstream <name> flag\n * 2. $MU_SESSION env var\n * 3. Current tmux session name (with `mu-` prefix stripped)\n *\n * Throws UsageError if none of the above produce a name.\n */\nexport async function resolveWorkstream(explicit?: string): Promise<string> {\n if (explicit) return explicit;\n if (process.env.MU_SESSION) return process.env.MU_SESSION;\n if (process.env.TMUX) {\n try {\n const name = (await tmux([\"display-message\", \"-p\", \"#S\"])).trim();\n if (name.startsWith(RESERVED_WORKSTREAM_PREFIX))\n return name.slice(RESERVED_WORKSTREAM_PREFIX.length);\n } catch {\n // fall through\n }\n }\n throw new UsageError(\n \"workstream required: pass --workstream <name>, set $MU_SESSION, or run inside an mu-<name> tmux session\",\n );\n}\n\n/** Like resolveWorkstream but returns null instead of throwing on miss.\n * Used by the read-permissive verbs (mu log, mu state, bare mu)\n * where 'no workstream' is a legitimate state to render. */\nexport async function resolveOptionalWorkstream(): Promise<string | null> {\n try {\n return await resolveWorkstream(undefined);\n } catch {\n return null;\n }\n}\n\n// ─── Qualified entity refs (cross-workstream verb args) ────────────────\n//\n// Every verb that takes an entity name (mu task show <id>, mu agent\n// send <name>, mu workspace path <agent>) accepts EITHER:\n// - bare `<name>` → resolves via current workstream context\n// - qualified `<ws>/<name>` → resolves directly, no -w needed\n// Implemented as a parse-at-CLI-entry helper so the SDK signatures\n// stay workstream-explicit (no entity ref shape leaks below cli.ts).\n// See verb_arg_qualified_workstream_name and the OUTPUT_LABELS_AUDIT.\n\nexport interface ParsedRef {\n /** Workstream prefix when the input was `<ws>/<name>`; undefined for\n * bare names. */\n workstream?: string;\n /** The bare name (everything after the first '/' if qualified, else\n * the whole input). */\n name: string;\n}\n\n/** Split `<ws>/<name>` on the FIRST '/'. Bare input → workstream=undefined.\n * Names today are restricted to [a-z0-9_-] (slugify / agent name validator)\n * so '/' is unambiguous: no entity name can contain it. */\nexport function parseQualifiedRef(raw: string): ParsedRef {\n const slash = raw.indexOf(\"/\");\n if (slash === -1) return { name: raw };\n return { workstream: raw.slice(0, slash), name: raw.slice(slash + 1) };\n}\n\n/** Sync glue: parse a qualified ref and (if qualified) push the\n * workstream onto opts.workstream. Throws UsageError when the caller\n * passed BOTH `--workstream A` AND `B/<name>` and they disagree.\n * Returns the bare name for downstream use. Mutates opts. */\nexport function applyQualifiedRef(raw: string, opts: { workstream?: string }): string {\n const parsed = parseQualifiedRef(raw);\n if (parsed.workstream === undefined) return raw;\n if (opts.workstream !== undefined && opts.workstream !== parsed.workstream) {\n throw new UsageError(\n `qualified ref ${JSON.stringify(raw)} (workstream=${parsed.workstream}) conflicts with --workstream ${opts.workstream}`,\n );\n }\n opts.workstream = parsed.workstream;\n return parsed.name;\n}\n\n/** Per-entity table+key+error mapping for findEntityWorkstreams /\n * resolveEntityRef. Centralised so adding a new entity is one row. */\nconst ENTITY_TABLES = {\n task: { table: \"tasks\", keyCol: \"local_id\" },\n agent: { table: \"agents\", keyCol: \"name\" },\n // workspace: lookup is by agent name (vcs_workspaces has agent_id\n // FK; the operator-facing key is the agent name in `agents`).\n workspace: { table: \"agents\", keyCol: \"name\" },\n} as const;\n\nexport type EntityKind = keyof typeof ENTITY_TABLES;\n\n/** List the workstream names where `entity` exists. Used for ambiguity\n * detection when the bare-name caller has no resolved -w context. */\nexport function findEntityWorkstreams(db: Db, kind: EntityKind, name: string): string[] {\n const { table, keyCol } = ENTITY_TABLES[kind];\n const rows = db\n .prepare(\n `SELECT ws.name AS workstream FROM ${table} t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE t.${keyCol} = ?\n ORDER BY ws.name`,\n )\n .all(name) as { workstream: string }[];\n return rows.map((r) => r.workstream);\n}\n\n/**\n * One-stop ref resolver for verbs taking an entity name + optional -w.\n *\n * Pipeline:\n * 1. Parse `<ws>/<name>` (qualified form) — sets opts.workstream\n * from the prefix and returns the bare name.\n * 2. Resolve workstream from --workstream / $MU_SESSION / tmux\n * session (the standard chain in resolveWorkstream).\n * 3. When no workstream resolves AND the bare name lives in ≥2\n * workstreams: throw NameAmbiguousError listing the candidates.\n * When it lives in exactly 1: use that workstream (still bare).\n * When it lives in 0: rethrow the original UsageError so the\n * operator sees the canonical 'workstream required' message.\n *\n * Returns `{ name, workstream }`. The verb then uses `name` for the\n * entity lookup and `workstream` for downstream SDK calls; opts.workstream\n * is mutated to match (so existing helpers like assertTaskInWorkstream\n * still work as the second-line check).\n */\nexport async function resolveEntityRef(\n db: Db,\n raw: string,\n opts: { workstream?: string },\n kind: EntityKind,\n): Promise<{ name: string; workstream: string }> {\n const name = applyQualifiedRef(raw, opts);\n try {\n const workstream = await resolveWorkstream(opts.workstream);\n return { name, workstream };\n } catch (err) {\n // Bare-name + no resolved context: try ambiguity disambiguation.\n if (!(err instanceof UsageError)) throw err;\n const matches = findEntityWorkstreams(db, kind, name);\n if (matches.length >= 2) throw new NameAmbiguousError(name, matches, kind);\n if (matches.length === 1) {\n const only = matches[0];\n if (only !== undefined) {\n opts.workstream = only;\n return { name, workstream: only };\n }\n }\n throw err;\n }\n}\n\n/** Standard --status validation: case-insensitive, returns the\n * canonical TaskStatus or throws UsageError naming every legal\n * value. Centralised so adding a status updates every CLI surface\n * at once (the OPEN | IN_PROGRESS | CLOSED list used to drift\n * across `mu task list --status`, `mu task wait --status`, the\n * --help text, and error messages). Source list lives in\n * src/tasks.ts as TASK_STATUS_LIST. */\nexport function parseStatusOption(raw: string, flag = \"--status\"): TaskStatus {\n const upper = raw.toUpperCase();\n if (!isTaskStatus(upper)) {\n throw new UsageError(`${flag} must be one of ${TASK_STATUS_LIST} (got ${JSON.stringify(raw)})`);\n }\n return upper;\n}\n\n/** Multi-status flag parser for the `--status <s...>` shape used by\n * `mu task list / task next / approve list` (per\n * task_list_multi_status_union). Composes parseCsvFlag (repeat OR\n * comma-separated OR mix) with per-element parseStatusOption\n * validation, then dedups case-insensitively (parseStatusOption\n * upper-cases). Returns `undefined` when the flag is absent or\n * resolves to an empty array — semantically \"no filter\", matching\n * today's no-`--status` shape. Throws UsageError naming the offending\n * element on the first invalid value (so the operator sees which one\n * failed, not just \"some value was wrong\"). */\nexport function parseStatusesOption(\n values: readonly string[] | undefined,\n flag = \"--status\",\n): TaskStatus[] | undefined {\n const fragments = parseCsvFlag(values);\n if (fragments.length === 0) return undefined;\n const seen = new Set<TaskStatus>();\n const out: TaskStatus[] = [];\n for (const raw of fragments) {\n const status = parseStatusOption(raw, flag);\n if (seen.has(status)) continue;\n seen.add(status);\n out.push(status);\n }\n return out;\n}\n\n// ─── Self-resolution (current pane → AgentRow) ─────────────────────────\n\n/**\n * Resolve \"the agent running this process\" by reading `$TMUX_PANE` and\n * looking up the matching agent row. Returns null when `$TMUX_PANE` is\n * unset or the pane isn't a managed agent — the lenient variant used\n * by verbs that have a sensible fallback (the calling agent name when\n * resolvable, else a generic 'user' / 'orchestrator'). `resolveSelf`\n * wraps this with the strict throwing variant for verbs that genuinely\n * require a managed-pane caller.\n */\nexport function resolveSelfOptional(db: Db): AgentRow | null {\n const paneId = process.env.TMUX_PANE;\n if (!paneId) return null;\n return getAgentByPane(db, paneId) ?? null;\n}\n\n/**\n * Strict variant of `resolveSelfOptional`: throws UsageError with a\n * helpful message if `$TMUX_PANE` is unset or the pane isn't a\n * managed agent. Used by `mu me` / `mu me tasks` / `mu me next` to give\n * an LLM-in-a-pane zero-config self-identification. Lives in cli.ts\n * so cli/agents.ts and cli/tasks.ts can both import it without a\n * lateral cli/* dependency.\n */\nexport function resolveSelf(db: Db): AgentRow {\n const paneId = process.env.TMUX_PANE;\n if (!paneId) {\n throw new UsageError(\n \"$TMUX_PANE is not set; this verb only works inside an mu-spawned tmux pane (or any tmux pane, but the pane has to be a managed agent)\",\n );\n }\n const agent = resolveSelfOptional(db);\n if (!agent) {\n throw new UsageError(\n `pane ${paneId} is not a managed agent. Use \\`mu agent list\\` to see managed panes, or \\`mu agent spawn\\` to register a new one.`,\n );\n }\n return agent;\n}\n\n// ─── Shared SDK-CLI bridge helpers (used by cli/*.ts) ──────────────\n//\n// These were extracted from inline-with-task-verbs in cli.ts; the\n// task verbs moved to src/cli/tasks.ts but the helpers stay here so\n// every cli/*.ts module can import them from one canonical location.\n\nfunction roiOf(t: TaskRow): number {\n return t.effortDays > 0 ? t.impact / t.effortDays : Number.POSITIVE_INFINITY;\n}\n\nexport function byRoiDesc(a: TaskRow, b: TaskRow): number {\n return roiOf(b) - roiOf(a);\n}\n\n// ─── Task list --sort ──────────────────────────────────────────────\n//\n// Four keys; default is `roi` (preserves prior behaviour). The two\n// time-based keys (`recency` = updated_at DESC, `age` = created_at ASC)\n// are the surface for \"what did I touch most recently\" and \"what's\n// gone stale\" — neither was queryable before. `id` is the boring\n// tiebreaker default for `mu task list` (local_id ASC).\nexport const TASK_SORT_KEYS = [\"roi\", \"recency\", \"age\", \"id\"] as const;\nexport type TaskSortKey = (typeof TASK_SORT_KEYS)[number];\n\nexport function isTaskSortKey(s: string): s is TaskSortKey {\n return (TASK_SORT_KEYS as readonly string[]).includes(s);\n}\n\nexport function parseSortOption(raw: string, flag = \"--sort\"): TaskSortKey {\n if (!isTaskSortKey(raw)) {\n throw new UsageError(\n `${flag} must be one of ${TASK_SORT_KEYS.join(\" | \")} (got ${JSON.stringify(raw)})`,\n );\n }\n return raw;\n}\n\n/** Sort a copy of `tasks` by `key`. Pure (does not mutate input). */\nexport function sortTasks(tasks: readonly TaskRow[], key: TaskSortKey): TaskRow[] {\n const out = tasks.slice();\n switch (key) {\n case \"roi\":\n return out.sort(byRoiDesc);\n case \"recency\":\n // updated_at DESC: most-recently-touched first.\n return out.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));\n case \"age\":\n // created_at ASC: oldest first (\"what's gone stale\").\n return out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));\n case \"id\":\n return out.sort((a, b) => a.name.localeCompare(b.name));\n }\n}\n\n/** Which timestamp basis the table's relative-time column should use\n * for the active sort, or `null` if no time column should be shown. */\nexport function relTimeBasisForSort(key: TaskSortKey): \"updatedAt\" | \"createdAt\" | null {\n if (key === \"recency\") return \"updatedAt\";\n if (key === \"age\") return \"createdAt\";\n return null;\n}\n\n/**\n * Decorate a TaskRow (or array of them) with a computed `roi` field for\n * JSON output. ROI is a CLI-rendering concern (the table view computes\n * it inline; see formatTaskListTable) but JSON consumers were getting\n * raw rows with no ROI at all, which broke `mu task next --json | jq\n * 'sort_by(.roi)'` and similar. We keep `TaskRow` itself ROI-free so\n * the SDK contract stays minimal; the decorator lives only in the JSON\n * emit path.\n *\n * `roi` is a plain JSON number when finite; for effortDays=0 the field\n * is omitted (JSON has no Infinity literal and `null` would be a lie).\n * Callers can detect the infinity case via `effortDays === 0`.\n */\nfunction withRoi<T extends TaskRow>(task: T): T & { roi?: number } {\n if (task.effortDays > 0) {\n return { ...task, roi: task.impact / task.effortDays };\n }\n return task;\n}\n\nexport function withRoiAll<T extends TaskRow>(tasks: T[]): (T & { roi?: number })[] {\n return tasks.map(withRoi);\n}\n\n// ─── Workstream-scope assertions ──────────────────────────────────────\n\n/**\n * Generic workstream-scope assertion. The two typed wrappers\n * (`assertAgentInWorkstream`, `assertTaskInWorkstream`) share this\n * shape: SELECT the `workstream` column from `<table>` WHERE\n * `<keyCol>` = key, and if the row exists with a non-matching\n * workstream throw a typed `*NotInWorkstreamError`. No-op when\n * `expectedWs` is undefined or the row doesn't exist (downstream\n * handlers raise the matching `*NotFoundError`).\n *\n * Doing the lookup directly via raw SQL (rather than through the\n * typed `getAgent` / `getTask`) keeps the helper decoupled from\n * each row's full schema — it only ever needs the one column. The\n * typed errors are constructed by `errFactory` so each caller keeps\n * its specific error class and exit-code mapping.\n */\nexport function assertEntityInWorkstream<E extends Error>(\n db: Db,\n table: string,\n keyCol: string,\n keyVal: string,\n expectedWs: string | undefined,\n errFactory: (keyVal: string, expectedWs: string, actualWs: string | null) => E,\n): void {\n if (!expectedWs) return;\n // v5: per-scope unique TEXT names mean a bare\n // `WHERE keyCol = ? LIMIT 1` could pick the wrong workstream's row\n // and falsely raise NotInWorkstreamError when the expected workstream\n // ALSO has a row of this name (bug_v5_name_clash_silent_misroute).\n // Fast path: if a row exists in the expected workstream, we're done.\n const inScope = db\n .prepare(\n `SELECT 1 AS hit FROM ${table} t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE t.${keyCol} = ? AND ws.name = ?\n LIMIT 1`,\n )\n .get(keyVal, expectedWs) as { hit: number } | undefined;\n if (inScope) return;\n // Slow path: name doesn't exist in expected ws — check whether it\n // exists anywhere else so we can raise the typed mismatch error\n // (rather than letting downstream raise the generic NotFoundError).\n // Pick any other workstream's row deterministically.\n const elsewhere = db\n .prepare(\n `SELECT ws.name AS workstream FROM ${table} t\n LEFT JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE t.${keyCol} = ?\n ORDER BY ws.name\n LIMIT 1`,\n )\n .get(keyVal) as { workstream: string | null } | undefined;\n if (elsewhere) {\n throw errFactory(keyVal, expectedWs, elsewhere.workstream);\n }\n}\n\n/**\n * Sister of `assertTaskInWorkstream` for verbs that target an agent\n * by name. Agent names are per-workstream unique\n * (`UNIQUE(workstream_id, name)` in src/db.ts), so the same name can\n * exist in multiple workstreams; `-w` is the scope check that turns a\n * wrong-target verb into a clear `AgentNotInWorkstreamError` (exit 4)\n * instead of silently operating on a same-named agent in another\n * workstream. No-op when `workstream` is undefined or the agent\n * doesn't exist (downstream handler raises `AgentNotFoundError`).\n */\nexport function assertAgentInWorkstream(\n db: Db,\n agentName: string,\n workstream: string | undefined,\n): void {\n // agents.workstream is NOT NULL in the schema, so `actual` is\n // never null in practice; the `?? \"—\"` is defence-in-depth for the\n // typed-error contract (which expects a non-null actual).\n assertEntityInWorkstream(\n db,\n \"agents\",\n \"name\",\n agentName,\n workstream,\n (n, exp, actual) => new AgentNotInWorkstreamError(n, exp, actual ?? \"—\"),\n );\n}\n\n/**\n * Sister of `assertAgentInWorkstream` for verbs that target a single\n * task by ID. Globally-unique task IDs mean these verbs could ignore\n * the flag, but accepting it gives the operator a sanity check (\"yes,\n * I think this task is in that workstream\") and raises a clear\n * `TaskNotInWorkstreamError` instead of silently acting on the task\n * they didn't mean. No-op when `workstream` is undefined or the task\n * doesn't exist (downstream handler raises `TaskNotFoundError`).\n */\nexport function assertTaskInWorkstream(\n db: Db,\n taskId: string,\n workstream: string | undefined,\n): void {\n // tasks.workstream is NOT NULL in the schema; see assertAgentInWorkstream.\n assertEntityInWorkstream(\n db,\n \"tasks\",\n \"local_id\",\n taskId,\n workstream,\n (id, exp, actual) => new TaskNotInWorkstreamError(id, exp, actual ?? \"—\"),\n );\n}\n\n// ─── Numeric arg parser (for --impact, --effort-days) ────────────────\n\nexport function parsePositiveNumber(value: string): number {\n const n = Number.parseFloat(value);\n if (Number.isNaN(n) || n <= 0) {\n throw new InvalidArgumentError(`expected a positive number, got ${JSON.stringify(value)}`);\n }\n return n;\n}\n\nexport function parseImpact(value: string): number {\n const n = Number.parseInt(value, 10);\n if (Number.isNaN(n) || n < 1 || n > 100) {\n throw new InvalidArgumentError(`expected 1..100, got ${JSON.stringify(value)}`);\n }\n return n;\n}\n\nexport function parseLines(value: string): number {\n const n = Number.parseInt(value, 10);\n if (Number.isNaN(n) || n < 0) {\n throw new InvalidArgumentError(`expected a non-negative integer, got ${JSON.stringify(value)}`);\n }\n return n;\n}\n\n// Parses a non-negative integer (0 is valid). Used for --since which\n// uses 0 as the \"replay everything\" cursor.\nexport function parseNonNegativeInt(value: string): number {\n const n = Number.parseInt(value, 10);\n if (Number.isNaN(n) || n < 0) {\n throw new InvalidArgumentError(`expected a non-negative integer, got ${JSON.stringify(value)}`);\n }\n return n;\n}\n\n// ─── Program definition ───────────────────────────────────────────────\n//\n// Three namespaces (`workstream`, `agent`, `task`) plus three top-level\n// utilities (`sql`, `doctor`, and the bare `mu` mission-control default).\n//\n// Every flag is declared on the subcommand that consumes it — there is\n// NO root --workstream that subcommands inherit via optsWithGlobals(),\n// which previously was the source of \"flag at the wrong level\" bugs.\n//\n// Bare `mu` (mission control) takes no flags. To target a different\n// workstream than the one the current shell is in, use `MU_SESSION=foo\n// mu` or `cd` into that workstream's tmux session.\n\n// Reusable workstream flag declaration. Each subcommand that needs it\n// gets its own copy via `.option(...WORKSTREAM_OPT)` so there is no\n// cross-command leakage.\nexport const WORKSTREAM_OPT = [\n \"-w, --workstream <name>\",\n \"workstream (defaults to $MU_SESSION or the current tmux session minus mu- prefix)\",\n] as const;\n\n// Reusable --json flag for every read verb. Output shape is documented\n// per-verb but follows a consistent pattern: collections → JSON arrays;\n// single entities → JSON objects. Empty results print `[]` (collections)\n// or `null` (single-entity reads with no match — currently none, since\n// every \"single\" verb errors on miss). Pretty-printing is OFF; one\n// document per line so output is grep/jq friendly.\nexport const JSON_OPT = [\"--json\", \"emit machine-readable JSON instead of a table\"] as const;\n\n/**\n * Canonical multi-value-flag parser. Every commander variadic flag\n * (`--foo <vals...>`) in mu is post-processed through this helper so\n * the operator can use repeated-flag form, comma-separated form, or\n * any mix:\n *\n * --foo a --foo b --foo c → [\"a\", \"b\", \"c\"]\n * --foo a,b,c → [\"a\", \"b\", \"c\"]\n * --foo a,b --foo c → [\"a\", \"b\", \"c\"]\n *\n * Whitespace inside fragments is trimmed; empty fragments (consecutive\n * commas, leading/trailing comma, an entirely-empty value) are\n * dropped. Idempotent: the helper is safe to apply twice.\n *\n * Convention codified by cli_audit_plurality_uniformity (v0.3). See\n * docs/USAGE_GUIDE.md \"CLI conventions\".\n */\nexport function parseCsvFlag(values: readonly string[] | undefined): string[] {\n if (!values) return [];\n return values.flatMap((v) =>\n v\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean),\n );\n}\n\n/** Stable JSON output: one line, no trailing newline beyond console.log's.\n * Exported so cli/*.ts modules can use the same single-source-of-truth\n * formatter. */\nexport function emitJson(value: unknown): void {\n console.log(JSON.stringify(value));\n}\n\n/** Wrap a collection in mu's canonical `{items, count}` envelope.\n * audit_json_envelope_uniformity: every collection-read verb (list,\n * next, owned-by, notes, search, orphans, commits, ...) emits this\n * shape so a future sibling field (`baseRef`, `totalAcrossPages`,\n * ...) can be added without breaking every caller. Pre-1.0\n * breaking versus the pre-audit bare-array shape.\n *\n * Carve-out: `mu sql --json` keeps bare-array rows (it's the escape\n * hatch; row shape is per-query, not part of the typed contract).\n * `mu log --tail --json` keeps NDJSON (one object per line) since\n * it's a stream, not a collection. */\nexport function emitJsonCollection<T>(items: readonly T[]): void {\n emitJson({ items, count: items.length });\n}\n\n/**\n * Read the package version from the shipped package.json. Works for\n * both source mode (src/cli.ts → ../package.json) and bundled mode\n * (dist/cli.js → ../package.json), since both layouts have package.json\n * exactly one directory up. Avoids hand-bumping a string literal in\n * code on every release.\n */\nfunction readPackageVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n const pkgPath = join(here, \"..\", \"package.json\");\n const raw = readFileSync(pkgPath, \"utf8\");\n const parsed = JSON.parse(raw) as { version?: unknown };\n return typeof parsed.version === \"string\" ? parsed.version : \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\nexport function buildProgram(): Command {\n const program = new Command();\n program\n .name(\"mu\")\n .description(\n \"Persistent crew of AI agents in tmux panes coordinated through a built-in task DAG.\",\n )\n .version(readPackageVersion())\n .helpOption(\"-h, --help\")\n // .showHelpAfterError() removed (audit_cli_validation_uniformity):\n // emitError() in handle.ts now owns help-on-error emission for both\n // commander mistakes and handler-thrown UsageErrors. Without this\n // removal, commander would print its own \"\\n<help>\" appendix to\n // stderr before throwing CommanderError, leaving us with a duplicate\n // help dump (commander's then ours). We suppress commander's stderr\n // entirely in applyExitOverride below; this comment is the load-\n // bearing explanation of why we don't reach for showHelpAfterError().\n // Sort the Commands list (NOT the Options list) alphabetically in\n // every --help screen. Commander v14 inherits configureHelp via\n // copyInheritedSettings() when subcommands are created with\n // .command(), but we also walk the tree below for certainty —\n // future subcommand groups added via .addCommand() do not inherit.\n .configureHelp({ sortSubcommands: true })\n // Without this, `mu task list --json` would bind --json to the\n // program (where we declare it for the bare `mu --json` mission-\n // control case) instead of the `list` subcommand. With it,\n // options before a subcommand bind to the program; options after\n // bind to the subcommand. Subcommands inherit it automatically.\n .enablePositionalOptions()\n // Default action when no subcommand is given: mission control.\n // Workstream resolves via the standard chain (-w > $MU_SESSION >\n // current tmux session); when none of those resolve, falls back\n // to a workstreams-discovery view instead of erroring. Accepts\n // --json so scripts can drive the same picture programmatically.\n // Bare `mu` is an alias for `mu state --mission` (the stripped\n // 5-col glance card). Per merge_state_into_hud_render_mode (v0.3):\n // route through cmdState with mission=true so there's exactly one\n // implementation of the glance render. -w accepts the same\n // variadic shape every other render mode does.\n .option(\n \"-w, --workstream <names...>\",\n \"workstream(s) to render (repeat or comma-separate; or both; defaults to $MU_SESSION or current tmux session)\",\n )\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as { workstream?: string[]; json?: boolean };\n return handle((db) => cmdState(db, { ...opts, mission: true }), this as Command)();\n });\n\n wireWorkstreamCommands(program);\n wireArchiveCommands(program);\n wireAgentCommands(program);\n wireSelfCommands(program);\n wireWorkspaceCommands(program);\n wireTaskCommands(program);\n wireLogCommand(program);\n wireStateCommands(program);\n wireSqlCommand(program);\n wireSnapshotCommands(program);\n wireDoctorCommand(program);\n applyAlphabeticalHelpSort(program);\n // audit_cli_validation_uniformity: every node in the command tree\n // must call exitOverride() so commander throws CommanderError\n // instead of calling process.exit() itself. Lets the parseAsync()\n // catch route every operator-error through emitError() with the\n // failing subcommand's --help (human) or `usage` JSON (--json).\n applyExitOverride(program);\n return program;\n}\n\n// Recursively set sortSubcommands on every command in the tree. Belt\n// and braces over the root .configureHelp() call: guarantees the sort\n// holds regardless of how a subcommand was attached (.command() vs\n// .addCommand()) and regardless of inheritance behaviour across\n// commander versions. Preserves any other help-configuration keys\n// already set on a subcommand.\nfunction applyAlphabeticalHelpSort(cmd: Command): void {\n cmd.configureHelp({ ...cmd.configureHelp(), sortSubcommands: true });\n for (const sub of cmd.commands) {\n applyAlphabeticalHelpSort(sub);\n }\n}\n\n/** Recursively call exitOverride() on every command in the tree.\n * Without this, commander writes its error to stderr and calls\n * process.exit() inline — we never see the error and our emitError()\n * contract (--help on usage errors, --json envelopes, exit-2\n * uniformity) doesn't apply.\n *\n * We ALSO swallow commander's writeErr so commander never prints the\n * unformatted \"error: ...\" / help combo before our handler runs. The\n * captured text is discarded; emitError() re-emits message + help in\n * mu's format. Help displays via --help still flow through writeOut\n * (commander.helpDisplayed code, which classifyCommanderError maps to\n * exit 0 with no further output). */\nfunction applyExitOverride(cmd: Command): void {\n cmd.exitOverride();\n cmd.configureOutput({\n writeOut: (s) => process.stdout.write(s),\n writeErr: () => {\n // Swallow commander's pre-throw stderr; emitError re-emits.\n },\n });\n for (const sub of cmd.commands) {\n applyExitOverride(sub);\n }\n}\n\n// ─── Entry point ───────────────────────────────────────────────────────\n\n// When invoked as `mu …` from the shell, parse argv. When imported (e.g.\n// from tests), do nothing — buildProgram() is exported for direct use.\n//\n// Symlink-safe: when installed via `npm install -g .` the `mu` binary\n// is a symlink (`/opt/homebrew/bin/mu → .../dist/cli.js`). `process.argv[1]`\n// is the symlink path as given; `import.meta.url` is Node's resolved\n// path (symlinks followed). Compare resolved-to-resolved by realpath-\n// ing argv[1] first — otherwise the entry-point check fails silently\n// and `mu --version` produces no output.\nif (isMainEntrypoint()) {\n const program = buildProgram();\n try {\n await program.parseAsync(process.argv);\n } catch (err) {\n // CommanderError (parse-time) lands here because every command in\n // the tree had .exitOverride() applied. Locate the deepest matching\n // subcommand from argv so emitError can render its --help (human)\n // or `usage` JSON (--json).\n const failingCmd = findCommandForArgv(program, process.argv.slice(2));\n const exitCode = emitParseError(err, failingCmd);\n process.exit(exitCode);\n }\n}\n\nfunction isMainEntrypoint(): boolean {\n const argv1 = process.argv[1];\n if (!argv1) return false;\n try {\n const resolved = realpathSync(argv1);\n return import.meta.url === pathToFileURL(resolved).href;\n } catch {\n // realpath can fail for non-file argv[1]. Fall back to the naive\n // check, which works when no symlink is involved.\n return import.meta.url === pathToFileURL(argv1).href;\n }\n}\n","// mu — DB module.\n//\n// Opens ~/.mu/mu.db (or MU_DB_PATH override), enables WAL + foreign keys,\n// applies the schema idempotently, and exposes the live Database handle.\n//\n// Schema (see CHANGELOG.md §\"Schema\"):\n// - 8 tables: workstreams, agents, tasks, task_edges, task_notes,\n// agent_logs, vcs_workspaces, snapshots\n// (+5 v6 archive_* tables; -1 approvals dropped in v7)\n// - 1 meta table: schema_version (single row, integer)\n// - 3 views: ready, blocked, goals\n//\n// v5 (this version) is the surrogate-INTEGER-PK shape per\n// docs/ARCHITECTURE.md § Surrogate-PK + SDK-boundary discipline.\n// Every entity table has an INTEGER PK; FKs reference INTEGER ids;\n// the operator-facing TEXT name is per-scope unique via\n// UNIQUE (<scope_id>, <name>).\n//\n// IMPORTANT: src/db.ts knows ONLY the v5 shape. Pre-v5 DBs are\n// rejected at openDb time with SchemaTooOldError; the operator\n// recovers the one-shot v4→v5 migration script from git history\n// (`git log --all --diff-filter=D -- scripts/migrate-v4-to-v5.ts`).\n// The old in-process forward-only migration ladder (v1→v2, v2→v3,\n// v3→v4) was removed in schema_v5_drop_migrations_ts: with the\n// loud-fail hook below catching every pre-v5 DB before openDb\n// returns, none of those migration paths could ever run.\n\nimport { mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport Database, { type Database as DatabaseType } from \"better-sqlite3\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\n\nexport type Db = DatabaseType;\n\nexport interface OpenDbOptions {\n /**\n * Absolute path to the SQLite file. Defaults to MU_DB_PATH env var or\n * the XDG state path (see `defaultDbPath`). Use a per-test temp path\n * in tests.\n */\n path?: string;\n\n /**\n * If true, opens the DB read-only. Used by `mu sql` and similar read-only\n * surfaces to enforce no-mutation guarantees at the connection level.\n */\n readonly?: boolean;\n}\n\n/**\n * Resolve the canonical mu state directory:\n * MU_STATE_DIR > $XDG_STATE_HOME/mu > ~/.local/state/mu\n */\nexport function defaultStateDir(): string {\n if (process.env.MU_STATE_DIR) return process.env.MU_STATE_DIR;\n const stateHome = process.env.XDG_STATE_HOME ?? join(homedir(), \".local\", \"state\");\n return join(stateHome, \"mu\");\n}\n\n/**\n * Resolve the canonical DB path:\n * MU_DB_PATH > <state-dir>/mu.db\n */\nexport function defaultDbPath(): string {\n if (process.env.MU_DB_PATH) return process.env.MU_DB_PATH;\n return join(defaultStateDir(), \"mu.db\");\n}\n\n/**\n * Per-workstream artifact directory: <state-dir>/workstreams/<workstream>/\n *\n * Created lazily by callers. 0.1.0 doesn't write to it yet — reserved\n * for future snapshots / tracing logs / forensic pane captures. The DB\n * stays canonical and shared; this directory is only for things that\n * naturally don't need cross-workstream queries.\n */\nexport function workstreamStateDir(workstream: string): string {\n return join(defaultStateDir(), \"workstreams\", workstream);\n}\n\n/**\n * Open the mu database. Creates the parent directory and applies the schema\n * idempotently on every open. Safe to call from many short-lived processes\n * concurrently — WAL mode handles cross-process writes.\n */\nexport function openDb(options: OpenDbOptions = {}): Db {\n const path = options.path ?? defaultDbPath();\n mkdirSync(dirname(path), { recursive: true });\n\n const db = new Database(path, { readonly: options.readonly ?? false });\n\n if (!options.readonly) {\n db.pragma(\"journal_mode = WAL\");\n db.pragma(\"foreign_keys = ON\");\n // Detect schema version BEFORE applySchema so a real v<5 DB is not\n // silently stamped as v5 by the CREATE-IF-NOT-EXISTS in applySchema.\n const detectedVersion = detectExistingSchemaVersion(db);\n if (detectedVersion !== null && detectedVersion < MIN_ACCEPTED_SCHEMA_VERSION) {\n // Loud-fail: refuse to touch a pre-v5 DB. The operator\n // restores the one-shot migrator from git history and retries.\n // (See docs/ARCHITECTURE.md § Surrogate-PK + SDK-boundary\n // discipline for the v5 substrate; the migrator was deleted\n // in the post-landing cleanup per the temp-impl-artifact rule.)\n // v5 DBs are forward-bumped to v6 in `applySchema` (purely\n // additive change).\n try {\n db.close();\n } catch {\n // best effort\n }\n throw new SchemaTooOldError(detectedVersion, MIN_ACCEPTED_SCHEMA_VERSION);\n }\n applySchema(db);\n } else {\n db.pragma(\"foreign_keys = ON\");\n }\n\n return db;\n}\n\n/**\n * Thrown by openDb when the on-disk DB is at a schema version older\n * than v5. v5 dropped the in-process forward migrator; the one-shot\n * v4→v5 migration script lives in git history (recover via\n * `git log --all --diff-filter=D -- scripts/migrate-v4-to-v5.ts`).\n *\n * Maps to exit code 4 (conflict) in cli.ts handle().\n */\n// ─── Resolve helpers (operator-facing name -> surrogate id) ───────────\n//\n// docs/ARCHITECTURE.md § Surrogate-PK + SDK-boundary discipline:\n//\n// PUBLIC SDK functions take operator-facing names (workstream + local\n// id + agent name). Internal helpers take surrogate ids. Resolution\n// happens at the public-function entry, exactly once.\n//\n// These helpers throw typed errors mapped to the same exit codes that\n// the previous \"row not found\" paths surfaced. Errors live next to\n// their domain modules (TaskNotFoundError in src/tasks/errors.ts,\n// AgentNotFoundError in src/agents/errors.ts, WorkstreamNameInvalidError\n// in src/workstream.ts) so the resolve functions just import + throw.\n//\n// We import them lazily via dynamic require to avoid an import cycle\n// (workstream/agents/tasks all import from db.ts). Each resolve helper\n// throws a TS Error subclass whose `.name` matches the canonical typed\n// error a consumer would expect.\n\nexport class WorkstreamNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"WorkstreamNotFoundError\";\n constructor(public readonly workstream: string) {\n super(`no such workstream: ${workstream}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List workstreams\", command: \"mu workstream list\" },\n {\n intent: \"Initialise this workstream\",\n command: `mu workstream init ${this.workstream}`,\n },\n ];\n }\n}\n\n/** Resolve a workstream name to its INTEGER surrogate id. Throws\n * WorkstreamNotFoundError on miss. Pure: no auto-create — callers\n * that want the auto-create-or-resolve semantics use\n * `ensureWorkstream` from src/workstream.ts (which returns void;\n * follow up with `resolveWorkstreamId` if the id is needed).\n */\nexport function resolveWorkstreamId(db: Db, workstream: string): number {\n const row = db.prepare(\"SELECT id FROM workstreams WHERE name = ?\").get(workstream) as\n | { id: number }\n | undefined;\n if (!row) throw new WorkstreamNotFoundError(workstream);\n return row.id;\n}\n\n/** Resolve a workstream name to its id, returning null on miss instead\n * of throwing. Useful for read paths that want to early-return [] on\n * a non-existent workstream (e.g. listTasks). */\nexport function tryResolveWorkstreamId(db: Db, workstream: string): number | null {\n const row = db.prepare(\"SELECT id FROM workstreams WHERE name = ?\").get(workstream) as\n | { id: number }\n | undefined;\n return row ? row.id : null;\n}\n\n/** Resolve a (workstream_id, local_id) pair to the task's surrogate id.\n * Throws an Error tagged 'TaskNotFoundError' on miss (callers in\n * src/tasks*.ts wrap with the proper typed error class — but a bare\n * caller still gets a meaningful message). */\nexport function resolveTaskId(db: Db, workstreamId: number, localId: string): number {\n const row = db\n .prepare(\"SELECT id FROM tasks WHERE workstream_id = ? AND local_id = ?\")\n .get(workstreamId, localId) as { id: number } | undefined;\n if (!row) {\n const err = new Error(`no such task in workstream: ${localId}`);\n (err as Error & { name: string }).name = \"TaskNotFoundError\";\n throw err;\n }\n return row.id;\n}\n\n/** Resolve a (workstream_id, agent_name) pair to the agent's surrogate\n * id. Throws an Error tagged 'AgentNotFoundError' on miss. */\nexport function resolveAgentId(db: Db, workstreamId: number, name: string): number {\n const row = db\n .prepare(\"SELECT id FROM agents WHERE workstream_id = ? AND name = ?\")\n .get(workstreamId, name) as { id: number } | undefined;\n if (!row) {\n const err = new Error(`no such agent in workstream: ${name}`);\n (err as Error & { name: string }).name = \"AgentNotFoundError\";\n throw err;\n }\n return row.id;\n}\n\nexport class SchemaTooOldError extends Error implements HasNextSteps {\n override readonly name = \"SchemaTooOldError\";\n constructor(\n public readonly detectedVersion: number,\n public readonly requiredVersion: number,\n ) {\n super(\n `Detected v${detectedVersion} schema; v${requiredVersion} is required. The one-shot v4→v5 migration script (scripts/migrate-v4-to-v5.ts) was deleted post-landing; recover it from git history and run it once, then retry your command.`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Recover the one-shot v4→v5 migration script from git history\",\n command: \"git log --all --diff-filter=D -- scripts/migrate-v4-to-v5.ts | head\",\n },\n {\n intent: \"Then run it once against the DB\",\n command:\n \"git show <commit>:scripts/migrate-v4-to-v5.ts > /tmp/migrate.ts && npx tsx /tmp/migrate.ts\",\n },\n {\n intent: \"Then retry the original command\",\n command: \"# (your original mu invocation)\",\n },\n {\n intent: \"Inspect the on-disk DB version\",\n command: `sqlite3 \"$MU_DB_PATH\" 'SELECT version FROM schema_version'`,\n },\n ];\n }\n}\n\n/**\n * Sniff an existing DB's schema version BEFORE applySchema runs, so we\n * can distinguish:\n * - Brand-new DB: no tables at all -> returns null (fresh, will be\n * stamped to CURRENT_SCHEMA_VERSION by applySchema).\n * - Pre-versioning DB (had v1 tables before schema_version existed):\n * workstreams exists, schema_version doesn't -> returns 1.\n * - Already-versioned DB: schema_version row present -> returns its\n * value.\n */\nfunction detectExistingSchemaVersion(db: Db): number | null {\n const hasVersionTable = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'\")\n .get() as { name: string } | undefined;\n if (hasVersionTable) {\n const row = db.prepare(\"SELECT version FROM schema_version WHERE id = 1\").get() as\n | { version: number }\n | undefined;\n return row?.version ?? null;\n }\n // No schema_version table. Check whether any of the original v1\n // tables exist; if so this is a pre-versioning v1 DB.\n const hasWorkstreams = db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table' AND name='workstreams'\")\n .get() as { name: string } | undefined;\n if (hasWorkstreams) return 1;\n return null;\n}\n\n/** Test seam: ensure a workstream's artifact dir exists. Unused today. */\nexport function ensureWorkstreamStateDir(workstream: string): string {\n const path = workstreamStateDir(workstream);\n mkdirSync(path, { recursive: true });\n return path;\n}\n\n/**\n * Apply the schema. Idempotent: tables use CREATE TABLE IF NOT EXISTS;\n * views are dropped and recreated so the latest definition always wins.\n *\n * For fresh DBs this writes the current schema shape and stamps\n * schema_version = CURRENT_SCHEMA_VERSION. For existing DBs this is a\n * no-op for the table CREATEs (IF NOT EXISTS) but DOES recreate the\n * views. Pre-v5 DBs never reach this function — openDb's loud-fail\n * hook rejects them with SchemaTooOldError first.\n *\n * v5 → v6 in-place bump: v6 was purely additive (5 new archive_*\n * tables; no existing column / FK / view touched). The CREATE TABLE\n * IF NOT EXISTS blocks above create the new tables on a v5 DB.\n *\n * v6 → v7 in-place bump: v7 is destructive — drops the `approvals`\n * table (zero usage in 200+ task dogfood; anti-anticipatory pruning\n * per VISION.md \"no traits with zero implementors\"). The DROP runs\n * BEFORE the version stamp so a partial migration doesn't leave a\n * v7-stamped DB with the v6 table still present. Gated on the\n * detected pre-bump version so it's a one-shot for v6 DBs and a\n * harmless `IF EXISTS` no-op for fresh v7 DBs.\n */\nfunction applySchema(db: Db): void {\n // Sniff the recorded version BEFORE the schema CREATEs land — needed\n // to decide whether the v6 → v7 destructive migration runs (only on\n // a DB that's at v6 or older but ≥ v5, the openDb floor).\n const preBumpVersion = detectExistingSchemaVersion(db);\n db.exec(CURRENT_SCHEMA);\n // v6 → v7 destructive migration: drop the approvals table on any\n // pre-v7 DB. IF EXISTS so a fresh v7 DB (no approvals table ever\n // created) is a no-op too. The DROP must precede the version\n // stamp below: a partial migration that crashed mid-DROP would\n // re-run on next open instead of silently leaving the table.\n if (preBumpVersion !== null && preBumpVersion < 7) {\n db.exec(\"DROP INDEX IF EXISTS idx_approvals_status\");\n db.exec(\"DROP INDEX IF EXISTS idx_approvals_workstream\");\n db.exec(\"DROP TABLE IF EXISTS approvals\");\n }\n // Stamp the version on a fresh DB. INSERT OR IGNORE so we don't\n // overwrite the version on an existing v5+ DB.\n db.prepare(\"INSERT OR IGNORE INTO schema_version (id, version) VALUES (1, ?)\").run(\n CURRENT_SCHEMA_VERSION,\n );\n // Forward-additive bump for in-place transitions (v5 → v6 archive\n // tables, v6 → v7 approvals removal). Guarded by `version < ?` so a\n // future open against a same-or-newer DB doesn't accidentally\n // downgrade.\n db.prepare(\"UPDATE schema_version SET version = ? WHERE id = 1 AND version < ?\").run(\n CURRENT_SCHEMA_VERSION,\n CURRENT_SCHEMA_VERSION,\n );\n}\n\n/** The schema version a fresh DB starts at. v7 drops the\n * `approvals` table on top of v6 (which added 5 archive_* tables\n * on top of v5's surrogate-PK substrate; docs/ARCHITECTURE.md §\n * Surrogate-PK + SDK-boundary discipline). The refusal floor is\n * v5 — pre-v5 DBs throw `SchemaTooOldError`; v5 → v6 → v7 DBs\n * are forward-bumped in place by `applySchema`. */\nexport const CURRENT_SCHEMA_VERSION = 7;\n\n/** The lowest schema version `openDb` will accept. v5 / v6 DBs are\n * forward-bumped to the current version in place (v5 → v6 added\n * archive tables; v6 → v7 dropped the approvals table). Pre-v5\n * DBs throw `SchemaTooOldError`. */\nconst MIN_ACCEPTED_SCHEMA_VERSION = 5;\n\n/** Tables a healthy DB must contain. Single source of truth so\n * `mu doctor` and any other consumer don't drift. Adding a new table\n * = one new entry here AND a CREATE TABLE in CURRENT_SCHEMA. (Schema\n * changes that aren't compatible with prior schemas bump\n * CURRENT_SCHEMA_VERSION and ship with a one-shot script under\n * scripts/ (the v4→v5 transition was the canonical example\n * before the script was deleted post-landing). */\nexport const EXPECTED_TABLES: readonly string[] = [\n \"agent_logs\",\n \"agents\",\n \"archived_edges\",\n \"archived_events\",\n \"archived_notes\",\n \"archived_tasks\",\n \"archives\",\n \"schema_version\",\n \"snapshots\",\n \"task_edges\",\n \"task_notes\",\n \"tasks\",\n \"vcs_workspaces\",\n \"workstreams\",\n];\n\n// ─── View DDL — single source of truth ────────────────────────────────\n//\n// The three views (ready, blocked, goals) get DROPped + CREATEd by\n// applySchema on every openDb. Each constant is self-contained:\n// DROP IF EXISTS + CREATE. Running DROP twice in a row is harmless,\n// so callers that already DROP up-front can still re-execute these\n// without churn.\n//\n// Exported as named constants so consumers can reference the canonical\n// shape (e.g. one-shot migration scripts under scripts/) without\n// duplicating SQL.\n\nexport const READY_VIEW_SQL = `\nDROP VIEW IF EXISTS ready;\nCREATE VIEW ready AS\n SELECT t.*\n FROM tasks t\n WHERE t.status = 'OPEN'\n AND NOT EXISTS (\n SELECT 1\n FROM task_edges e\n JOIN tasks b ON e.from_task_id = b.id\n WHERE e.to_task_id = t.id\n AND b.status <> 'CLOSED'\n );\n`;\n\nexport const BLOCKED_VIEW_SQL = `\nDROP VIEW IF EXISTS blocked;\nCREATE VIEW blocked AS\n SELECT t.*\n FROM tasks t\n WHERE t.status = 'OPEN'\n AND EXISTS (\n SELECT 1\n FROM task_edges e\n JOIN tasks b ON e.from_task_id = b.id\n WHERE e.to_task_id = t.id\n AND b.status <> 'CLOSED'\n );\n`;\n\n// A goal is an active endpoint of the DAG — a task with no dependents\n// that we're still working toward. CLOSED, REJECTED, and DEFERRED are\n// all excluded: a finished/abandoned/parked leaf is not an active goal.\n// (REJECTED and DEFERRED still BLOCK dependents per the views above\n// — they're terminal/parked from the perspective of 'what's a goal',\n// but they don't satisfy a blocked-by edge: only CLOSED does that.)\nexport const GOALS_VIEW_SQL = `\nDROP VIEW IF EXISTS goals;\nCREATE VIEW goals AS\n SELECT t.*\n FROM tasks t\n WHERE t.status NOT IN ('CLOSED', 'REJECTED', 'DEFERRED')\n AND NOT EXISTS (\n SELECT 1 FROM task_edges WHERE from_task_id = t.id\n );\n`;\n\n// ─── v5 SCHEMA ────────────────────────────────────────────────────────\n//\n// Per docs/ARCHITECTURE.md § Surrogate-PK + SDK-boundary discipline.\n// Every entity table has:\n// - INTEGER PRIMARY KEY AUTOINCREMENT (surrogate identity)\n// - <scope>_id INTEGER NOT NULL REFERENCES <parent>(id) ON DELETE CASCADE\n// - <name> TEXT NOT NULL (operator-facing, mutable)\n// - UNIQUE (<scope>_id, <name>)\n//\n// Foreign keys are INTEGER. Renames become single-row UPDATEs (no\n// cascade chain). The TEXT name is just an attribute. snapshots is\n// the documented exception (intentionally NO FK on workstream so a\n// destroy snapshot outlives its workstream).\n\nconst CURRENT_SCHEMA = `\n-- ─── Schema versioning ────────────────────────────────────────────────\n--\n-- Single-row table tracking which schema version this DB is at. Migrations\n-- read and update this; the row is INSERT-OR-IGNOREd by applySchema with\n-- the current version on a fresh DB.\nCREATE TABLE IF NOT EXISTS schema_version (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n version INTEGER NOT NULL\n);\n\n-- ─── Tables ───────────────────────────────────────────────────────────\n\n-- workstreams: top of the hierarchy. name stays globally unique\n-- because it IS a tmux session name; no <scope_id> column because\n-- there's no parent.\nCREATE TABLE IF NOT EXISTS workstreams (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT UNIQUE NOT NULL,\n created_at TEXT NOT NULL -- ISO 8601\n);\n\n-- agents: one row per managed pane. Per-workstream unique on name.\nCREATE TABLE IF NOT EXISTS agents (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n workstream_id INTEGER NOT NULL REFERENCES workstreams (id) ON DELETE CASCADE,\n name TEXT NOT NULL, -- per-workstream unique\n cli TEXT NOT NULL DEFAULT 'pi',\n pane_id TEXT NOT NULL,\n status TEXT NOT NULL,\n role TEXT NOT NULL DEFAULT 'full-access',\n tab TEXT,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE (workstream_id, name),\n CHECK (status IN (\n 'spawning', 'busy', 'needs_input', 'needs_permission',\n 'free', 'unreachable', 'terminated'\n )),\n CHECK (role IN ('full-access', 'read-only'))\n);\n\nCREATE INDEX IF NOT EXISTS idx_agents_workstream ON agents (workstream_id);\nCREATE INDEX IF NOT EXISTS idx_agents_status ON agents (status);\n\n-- tasks: per-workstream unique on local_id (TRULY local now —\n-- different workstreams may reuse the same local_id).\nCREATE TABLE IF NOT EXISTS tasks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n workstream_id INTEGER NOT NULL REFERENCES workstreams (id) ON DELETE CASCADE,\n local_id TEXT NOT NULL, -- per-workstream unique\n title TEXT NOT NULL,\n status TEXT NOT NULL DEFAULT 'OPEN',\n -- OPEN | IN_PROGRESS | CLOSED | REJECTED | DEFERRED — see VOCABULARY.md.\n impact INTEGER NOT NULL,\n effort_days REAL NOT NULL,\n owner_id INTEGER REFERENCES agents (id) ON DELETE SET NULL,\n created_at TEXT NOT NULL,\n updated_at TEXT NOT NULL,\n UNIQUE (workstream_id, local_id),\n CHECK (impact BETWEEN 1 AND 100),\n CHECK (effort_days > 0),\n CHECK (status IN ('OPEN', 'IN_PROGRESS', 'CLOSED', 'REJECTED', 'DEFERRED'))\n);\n\nCREATE INDEX IF NOT EXISTS idx_tasks_workstream ON tasks (workstream_id);\nCREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks (status);\nCREATE INDEX IF NOT EXISTS idx_tasks_owner ON tasks (owner_id);\n\n-- task_edges: composite PK by pair. INTEGER FKs into tasks.id.\nCREATE TABLE IF NOT EXISTS task_edges (\n from_task_id INTEGER NOT NULL REFERENCES tasks (id) ON DELETE CASCADE,\n to_task_id INTEGER NOT NULL REFERENCES tasks (id) ON DELETE CASCADE,\n created_at TEXT NOT NULL,\n PRIMARY KEY (from_task_id, to_task_id),\n CHECK (from_task_id <> to_task_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_task_edges_to ON task_edges (to_task_id);\n\n-- task_notes: append-only context. author stays free-text\n-- (\"orchestrator\", \"user\", \"π - mu\", \"system\") — not always a\n-- registered agent.\nCREATE TABLE IF NOT EXISTS task_notes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n task_id INTEGER NOT NULL REFERENCES tasks (id) ON DELETE CASCADE,\n author TEXT,\n content TEXT NOT NULL,\n created_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_task_notes_task ON task_notes (task_id);\n\n-- agent_logs: append-only timeline. source stays free-text (\"system\",\n-- \"user\", \"orchestrator\", or any agent name) — not an FK relation.\n-- workstream_id is nullable (a future machine-wide event might exist)\n-- but every current emitter sets it; CASCADE on workstream destroy.\nCREATE TABLE IF NOT EXISTS agent_logs (\n seq INTEGER PRIMARY KEY AUTOINCREMENT,\n workstream_id INTEGER REFERENCES workstreams (id) ON DELETE CASCADE,\n source TEXT NOT NULL,\n kind TEXT NOT NULL DEFAULT 'message',\n payload TEXT NOT NULL,\n created_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_agent_logs_seq ON agent_logs (seq);\nCREATE INDEX IF NOT EXISTS idx_agent_logs_ws_seq ON agent_logs (workstream_id, seq);\nCREATE INDEX IF NOT EXISTS idx_agent_logs_source ON agent_logs (source);\n\n-- vcs_workspaces: one isolated working copy per agent.\n-- UNIQUE (agent_id) enforces the 1:1 invariant; workstream_id is\n-- denormalised for query convenience. path is UNIQUE because two\n-- agents pointing at the same on-disk workspace would defeat the\n-- purpose.\nCREATE TABLE IF NOT EXISTS vcs_workspaces (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n agent_id INTEGER NOT NULL UNIQUE REFERENCES agents (id) ON DELETE CASCADE,\n workstream_id INTEGER NOT NULL REFERENCES workstreams (id) ON DELETE CASCADE,\n backend TEXT NOT NULL CHECK (backend IN ('jj', 'sl', 'git', 'none')),\n path TEXT NOT NULL UNIQUE,\n parent_ref TEXT,\n created_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_vcs_workspaces_workstream ON vcs_workspaces (workstream_id);\n\n-- snapshots: documented exception. NO FK on workstream — a destroy\n-- snapshot must outlive its workstream. workstream column stays TEXT\n-- so the snapshot remains readable even after every reference is gone.\nCREATE TABLE IF NOT EXISTS snapshots (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n workstream TEXT,\n label TEXT NOT NULL,\n db_path TEXT NOT NULL,\n schema_version INTEGER NOT NULL,\n created_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_snapshots_created_at ON snapshots (created_at);\nCREATE INDEX IF NOT EXISTS idx_snapshots_workstream ON snapshots (workstream);\n\n-- ─── v6 archive tables (additive on top of v5) ────────────────────\n--\n-- 5 new tables landed in v6 to back the mu archive verb (cross-workstream\n-- preservation of CLOSED/REJECTED/DEFERRED tasks before destroy).\n-- Additive only: no existing column / FK / view touched. The v5 → v6\n-- transition is in-place via applySchema (no separate migration\n-- script). See docs/VOCABULARY.md § archive for terminology.\n--\n-- Design constraint: archives outlive workstreams. archives.label is\n-- globally unique (NOT per-workstream), and archived_tasks columns\n-- that refer to the source workstream are TEXT (not FKs) so the\n-- destroyed workstream's name stays readable post-destroy.\n\n-- archives: one row per operator-named archive bucket. label is\n-- globally unique because archives outlive workstreams (an archive\n-- whose label was scoped to a workstream would lose its name when\n-- the workstream is destroyed).\nCREATE TABLE IF NOT EXISTS archives (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n label TEXT UNIQUE NOT NULL,\n description TEXT,\n created_at TEXT NOT NULL,\n last_added_at TEXT NOT NULL -- bumped on every successful add (additive accumulation invariant)\n);\n\n-- archived_tasks: snapshot of a task at archive time. source_workstream\n-- is intentionally TEXT (the source workstream may be destroyed after\n-- archive); owner_name is snapshotted for the same reason. The\n-- (archive_id, source_workstream, original_local_id) UNIQUE is the\n-- idempotency lever for mu archive add re-runs.\nCREATE TABLE IF NOT EXISTS archived_tasks (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n archive_id INTEGER NOT NULL REFERENCES archives (id) ON DELETE CASCADE,\n source_workstream TEXT NOT NULL,\n original_local_id TEXT NOT NULL,\n title TEXT NOT NULL,\n status TEXT NOT NULL,\n impact INTEGER NOT NULL,\n effort_days REAL NOT NULL,\n owner_name TEXT,\n archived_at_status TEXT NOT NULL,\n archived_at TEXT NOT NULL,\n original_created_at TEXT NOT NULL,\n original_updated_at TEXT NOT NULL,\n UNIQUE (archive_id, source_workstream, original_local_id)\n);\n\nCREATE INDEX IF NOT EXISTS idx_archived_tasks_archive ON archived_tasks (archive_id);\nCREATE INDEX IF NOT EXISTS idx_archived_tasks_source ON archived_tasks (archive_id, source_workstream);\n\n-- archived_edges: composite PK by pair of archived_tasks ids.\n-- archive_id is denormalised so a CASCADE on the archive cleans every\n-- edge in one shot.\nCREATE TABLE IF NOT EXISTS archived_edges (\n archive_id INTEGER NOT NULL REFERENCES archives (id) ON DELETE CASCADE,\n from_archived_id INTEGER NOT NULL REFERENCES archived_tasks (id) ON DELETE CASCADE,\n to_archived_id INTEGER NOT NULL REFERENCES archived_tasks (id) ON DELETE CASCADE,\n PRIMARY KEY (archive_id, from_archived_id, to_archived_id)\n);\n\n-- archived_notes: snapshot of task_notes for archived tasks. author\n-- stays free-text, mirroring task_notes.\nCREATE TABLE IF NOT EXISTS archived_notes (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n archive_id INTEGER NOT NULL REFERENCES archives (id) ON DELETE CASCADE,\n archived_task_id INTEGER NOT NULL REFERENCES archived_tasks (id) ON DELETE CASCADE,\n author TEXT,\n content TEXT NOT NULL,\n created_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_archived_notes_task ON archived_notes (archived_task_id);\n\n-- archived_events: snapshot of kind='event' rows from agent_logs for\n-- the source workstream at archive time. Only events (not the full\n-- message log; that's recoverable via snapshot+undo if ever needed).\nCREATE TABLE IF NOT EXISTS archived_events (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n archive_id INTEGER NOT NULL REFERENCES archives (id) ON DELETE CASCADE,\n source_workstream TEXT NOT NULL,\n seq INTEGER NOT NULL,\n source TEXT NOT NULL,\n payload TEXT NOT NULL,\n created_at TEXT NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_archived_events_archive ON archived_events (archive_id, source_workstream);\n\n-- ─── Views (always replaced so the latest definition wins) ────────────\n-- See READY_VIEW_SQL / BLOCKED_VIEW_SQL / GOALS_VIEW_SQL above for the\n-- canonical DDL — interpolated here so applySchema is one db.exec().\n${READY_VIEW_SQL}\n${BLOCKED_VIEW_SQL}\n${GOALS_VIEW_SQL}\n`;\n","// mu — agent_logs: append-only timeline of activity in a workstream.\n//\n// Three roles in one table:\n// 1. Manual broadcasts (`mu log \"...\"` from a shell or agent pane)\n// 2. System events (auto-emitted by every state-changing verb;\n// wired in a follow-up commit so this surface is reviewable\n// first)\n// 3. External script entries via `mu log --as ...`\n//\n// The seq column (AUTOINCREMENT INTEGER PK) is the cursor. A tail\n// subscriber stores the last seq it saw and re-queries with\n// `seq > <last>`; AUTOINCREMENT guarantees seq never recycles even\n// after deletes, so the cursor is durable.\n\nimport { type Db, tryResolveWorkstreamId } from \"./db.js\";\n\nexport type LogKind = \"message\" | \"event\" | \"broadcast\" | string;\n\nexport interface LogRow {\n /** Monotonic AUTOINCREMENT id. Use as the cursor for `--since`. */\n seq: number;\n /** Workstream this entry belongs to, or `null` for machine-wide. */\n workstreamName: string | null;\n /** Free TEXT: agent name, \"system\", \"user\", or anything a caller picks. */\n source: string;\n /** Free TEXT: \"message\" (default), \"event\" (auto state changes),\n * \"broadcast\" (explicit cross-agent), or any caller-defined value. */\n kind: LogKind;\n /** Free utf-8 string. May be JSON if the kind suggests structure. */\n payload: string;\n /** ISO 8601 timestamp set at insert time. */\n createdAt: string;\n}\n\ninterface RawLogRow {\n seq: number;\n /** Joined from workstreams.name. Null when workstream_id is NULL. */\n workstream: string | null;\n source: string;\n kind: string;\n payload: string;\n created_at: string;\n}\n\n/** SELECT clause for joining workstream_id back to the operator-facing\n * workstream name. Used by every read path so the JS-side row shape\n * is operator-facing TEXT names (not surrogate ids). */\nconst SELECT_LOG_COLS = `\n l.seq AS seq,\n ws.name AS workstream,\n l.source AS source,\n l.kind AS kind,\n l.payload AS payload,\n l.created_at AS created_at\n`;\n\nconst LOG_FROM_JOIN = \"FROM agent_logs l LEFT JOIN workstreams ws ON ws.id = l.workstream_id\";\n\nfunction rowFromDb(row: RawLogRow): LogRow {\n return {\n seq: row.seq,\n workstreamName: row.workstream,\n source: row.source,\n kind: row.kind,\n payload: row.payload,\n createdAt: row.created_at,\n };\n}\n\nexport interface AppendLogOptions {\n /** Workstream this entry belongs to. `null` for machine-wide. */\n workstream: string | null;\n /** Who emitted this. Agent name, \"system\", \"user\", or arbitrary. */\n source: string;\n /** Defaults to \"message\". */\n kind?: LogKind;\n /** Free utf-8. Multi-line allowed. */\n payload: string;\n}\n\n/**\n * Append a log entry. Returns the inserted row (with assigned `seq`).\n * Constant-time. Single INSERT; safe to call from any state-changing\n * verb without a transaction wrapper.\n */\nexport function appendLog(db: Db, opts: AppendLogOptions): LogRow {\n const kind = opts.kind ?? \"message\";\n const createdAt = new Date().toISOString();\n // Resolve workstream name -> surrogate id. Null stays null. We do NOT\n // throw on a missing workstream here — an event payload may legitimately\n // reference a workstream the row for which is being concurrently dropped\n // (e.g. workstream destroy emits its own log row with workstream=null\n // for exactly this reason). Best-effort resolution.\n const workstreamId =\n opts.workstream === null ? null : tryResolveWorkstreamId(db, opts.workstream);\n const result = db\n .prepare(\n `INSERT INTO agent_logs (workstream_id, source, kind, payload, created_at)\n VALUES (?, ?, ?, ?, ?)`,\n )\n .run(workstreamId, opts.source, kind, opts.payload, createdAt);\n return {\n seq: Number(result.lastInsertRowid),\n workstreamName: opts.workstream,\n source: opts.source,\n kind,\n payload: opts.payload,\n createdAt,\n };\n}\n\nexport interface ListLogsOptions {\n /** Filter by workstream. `undefined` = every workstream + machine-wide.\n * `null` = ONLY machine-wide entries. */\n workstream?: string | null;\n /** Strictly > this seq. Use to resume a tail. */\n since?: number;\n /** Cap the result. With `since`, returns the FIRST N matching (oldest\n * first). Without `since`, returns the LAST N (most recent),\n * re-sorted oldest-first. */\n limit?: number;\n source?: string;\n kind?: string;\n}\n\n/**\n * List log entries. Always returns oldest-first. Use `since` for\n * cursor-based reads (the canonical tail pattern); use `limit` alone\n * for \"show me the most recent N\" reads.\n */\nexport function listLogs(db: Db, opts: ListLogsOptions = {}): LogRow[] {\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (opts.workstream === null) {\n conditions.push(\"l.workstream_id IS NULL\");\n } else if (opts.workstream !== undefined) {\n // Resolve once; if the workstream doesn't exist the result set is empty.\n const wsId = tryResolveWorkstreamId(db, opts.workstream);\n if (wsId === null) return [];\n conditions.push(\"l.workstream_id = ?\");\n params.push(wsId);\n }\n if (opts.since !== undefined) {\n conditions.push(\"l.seq > ?\");\n params.push(opts.since);\n }\n if (opts.source !== undefined) {\n conditions.push(\"l.source = ?\");\n params.push(opts.source);\n }\n if (opts.kind !== undefined) {\n conditions.push(\"l.kind = ?\");\n params.push(opts.kind);\n }\n\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n\n // Two query shapes:\n // - When `since` is set, ascending order is what we want directly.\n // - When `limit` is set without `since`, fetch the most-recent N\n // (descending) then reverse so the caller still sees oldest-first.\n if (opts.limit !== undefined && opts.since === undefined) {\n const rowsDesc = db\n .prepare(`SELECT ${SELECT_LOG_COLS} ${LOG_FROM_JOIN} ${where} ORDER BY l.seq DESC LIMIT ?`)\n .all(...params, opts.limit) as RawLogRow[];\n return rowsDesc.reverse().map(rowFromDb);\n }\n\n let sql = `SELECT ${SELECT_LOG_COLS} ${LOG_FROM_JOIN} ${where} ORDER BY l.seq ASC`;\n if (opts.limit !== undefined) {\n sql += \" LIMIT ?\";\n params.push(opts.limit);\n }\n const rows = db.prepare(sql).all(...params) as RawLogRow[];\n return rows.map(rowFromDb);\n}\n\n/**\n * Return the latest seq currently in the table (or 0 if empty). Used\n * by `mu log --tail` to start the cursor at \"now\" so the subscriber\n * only sees NEW entries unless they explicitly pass `--since 0`.\n */\nexport function latestSeq(db: Db): number {\n const row = db.prepare(\"SELECT MAX(seq) AS s FROM agent_logs\").get() as { s: number | null };\n return row.s ?? 0;\n}\n\n/**\n * One-line helper for state-changing SDK functions to auto-emit a\n * `kind='event'` log entry. Called AFTER the mutation succeeds, only\n * when the mutation actually produced a change (no-ops stay quiet).\n *\n * `source` defaults to 'system' since this is the auto-emission path;\n * a different source means \"a specific agent caused this\" and is set\n * by callers like `claimTask` (source = the claiming agent).\n */\nexport function emitEvent(\n db: Db,\n workstream: string | null,\n payload: string,\n source = \"system\",\n): void {\n appendLog(db, { workstream, source, kind: \"event\", payload });\n}\n\n// ─── claim-event structured prefix ─────────────────────────────────\n//\n// `task claim` events are the one place where a state-changing verb\n// emits TWO actors per row: the agent recorded as `source`, and the\n// `actor=` field that may differ on the --self anonymous-claim path\n// (where source == actor but tasks.owner stays NULL). The original\n// payload was free prose (`task claim foo by bar (was owner=...)`)\n// and the consumer (lastClaimActor below) prefix-matched the prose\n// — brittle: any rename silently nulled out the attribution.\n//\n// The fix keeps the prose suffix for human readability but prepends\n// a tab-delimited structured prefix that lastClaimActor parses\n// robustly. Format:\n//\n// task.claim<TAB><localId><TAB>actor=<actor><TAB>self=<0|1><TAB><prose>\n//\n// The trailing prose still starts with `task claim <localId> ...` so\n// the HUD's verb colourer (which strips the structured prefix via\n// displayEventPayload before colouring) keeps working unchanged.\n//\n// See: review_code_last_claim_actor_brittle.\n\n/** Structured-prefix sentinel used by claim event payloads. The dot\n * distinguishes it from the prose `task claim ...` tail. */\nexport const CLAIM_EVENT_PREFIX = \"task.claim\";\n\n/** Build the structured payload for a `task claim` event. */\nexport function formatClaimEvent(opts: {\n localId: string;\n actor: string;\n anonymous: boolean;\n prose: string;\n}): string {\n const self = opts.anonymous ? \"1\" : \"0\";\n return `${CLAIM_EVENT_PREFIX}\\t${opts.localId}\\tactor=${opts.actor}\\tself=${self}\\t${opts.prose}`;\n}\n\n/** Strip the structured `task.claim` prefix and return the human-prose\n * tail. For non-claim payloads, returns the input unchanged. Used by\n * `mu log` and HUD render so the user sees the prose, not the\n * delimiter-noise. */\nexport function displayEventPayload(payload: string): string {\n if (!payload.startsWith(`${CLAIM_EVENT_PREFIX}\\t`)) return payload;\n // task.claim<TAB><id><TAB>actor=...<TAB>self=...<TAB><prose>\n // Split into 5 fields; the prose may itself contain tabs (it doesn't\n // today, but be defensive: rejoin with TAB so we never lose data).\n const parts = payload.split(\"\\t\");\n if (parts.length < 5) return payload;\n return parts.slice(4).join(\"\\t\");\n}\n\n/** Parse the actor= field out of a structured claim payload. Returns\n * null when the payload isn't a claim event or is malformed. */\nexport function parseClaimEventActor(payload: string): string | null {\n if (!payload.startsWith(`${CLAIM_EVENT_PREFIX}\\t`)) return null;\n for (const field of payload.split(\"\\t\")) {\n if (field.startsWith(\"actor=\")) return field.slice(\"actor=\".length);\n }\n return null;\n}\n\n/**\n * Find the actor of the most recent `task claim <id>` event for a\n * given task. Used to surface 'who's working on this' when\n * `tasks.owner IS NULL` (the --self anonymous-claim path). Returns\n * null when no claim event exists for this task.\n *\n * Implementation: indexed lookup on (workstream, seq) with a LIKE\n * against the structured prefix. Unbounded — the previous limit=100\n * ceiling silently dropped attribution on long-lived workstreams.\n * The structured prefix (CLAIM_EVENT_PREFIX) makes the match\n * robust against payload-prose churn.\n */\nexport function lastClaimActor(db: Db, workstream: string, localId: string): string | null {\n // localId is validated by isValidTaskId — alnum + `_` + `-`. The\n // `_` is a LIKE wildcard, so escape it (and `%` and `\\` for\n // completeness, even though they can't appear in a valid id).\n const escaped = localId.replace(/[\\\\%_]/g, (c) => `\\\\${c}`);\n const pattern = `${CLAIM_EVENT_PREFIX}\\t${escaped}\\t%`;\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return null;\n const row = db\n .prepare(\n `SELECT payload FROM agent_logs\n WHERE workstream_id = ? AND kind = 'event' AND payload LIKE ? ESCAPE '\\\\'\n ORDER BY seq DESC LIMIT 1`,\n )\n .get(wsId, pattern) as { payload: string } | undefined;\n if (!row) return null;\n return parseClaimEventActor(row.payload);\n}\n\n/**\n * Canonical list of two-token verb prefixes that `emitEvent` callers\n * use as the leading words of a payload. Single source of truth: the\n * HUD's event-tail colourer (src/cli/state.ts colorEventPayload) reads\n * this so it can never drift away from the actual emitter sites.\n *\n * Maintenance contract: when you add an `emitEvent(...)` call whose\n * payload starts with a new two-word verb, add the verb here. A\n * regression test in test/hud.test.ts walks every entry and asserts\n * the HUD recognises it; the test fails if you add an emitter without\n * adding its verb here.\n *\n * Audit (2026-05): every `emitEvent` callsite under src/ produces a\n * payload that starts with one of these. Verified by\n * `grep -rn emitEvent src/ | grep -v import`.\n */\nexport const EVENT_VERB_PREFIXES: readonly string[] = [\n // src/tasks.ts + src/tasks/*.ts\n \"task add\",\n \"task note\",\n \"task status\",\n // `task claim` is the prose-tail of a `task.claim\\t...` structured\n // payload (see CLAIM_EVENT_PREFIX above); displayEventPayload\n // strips the structured prefix before the HUD colourer runs, so\n // the prose tail starting with `task claim` still matches.\n \"task claim\",\n \"task release\",\n \"task update\",\n \"task delete\",\n \"task reap\",\n \"task block\",\n \"task unblock\",\n \"task reparent\",\n // src/agents.ts + src/agents/*.ts\n \"agent spawn\",\n \"agent close\",\n \"agent free\",\n \"agent adopt\",\n // src/tasks/wait.ts — emitted when --stuck-after fires (alive +\n // assigned + no recent progress; idle_assigned_agent_detection).\n \"agent stalled\",\n // src/workspace.ts\n \"workspace create\",\n \"workspace free\",\n \"workspace refresh\",\n // src/workstream.ts\n \"workstream init\",\n \"workstream destroy\",\n \"workstream export\",\n \"workstream import\",\n // src/archives.ts — v6 archive verbs. Machine-wide events\n // (workstream=null) because archives outlive workstreams.\n \"archive create\",\n \"archive delete\",\n \"archive add\",\n \"archive remove\",\n // src/exporting.ts — archive export emits the bucket-render summary\n // as a machine-wide event (workstream=null; the export spans every\n // source-ws in the archive).\n \"archive export\",\n];\n","// mu — Pi status detector.\n//\n// Derives an agent's runtime status (busy / needs_input / needs_permission)\n// from its tmux pane scrollback. 0.1.0 is pi-only — we know pi's exact\n// markers from its source. Heterogeneous CLI support (claude, codex) is\n// on the roadmap and will land alongside ground-truth scrollback fixtures.\n//\n// Algorithm (cribbed from a prior internal multi-agent runtime's per-CLI detector):\n//\n// 1. Take the last TAIL_WINDOW_LINES (100) lines of scrollback.\n// 2. Strip trailing blank lines (TUIs pad blanks below content).\n// 3. From what remains, take the last TAIL_LINES (20).\n// 4. If a permission pattern is present in the tail → needs_permission.\n// 5. Else if a busy pattern is present → busy.\n// 6. Else → needs_input.\n//\n// The tail-window is what makes detection robust: pi's startup banner\n// contains \"to interrupt\" in its keybindings hint list. By only looking\n// at the bottom of the pane, we ignore stale matches that have scrolled\n// out of view.\n\n/**\n * The full agent lifecycle status. Most values are scrollback-derived\n * (`DetectedStatus`); the rest are set by the lifecycle layer.\n */\nexport type AgentStatus =\n | \"spawning\"\n | \"busy\"\n | \"needs_input\"\n | \"needs_permission\"\n | \"free\"\n | \"unreachable\"\n | \"terminated\";\n\n/** Status that can be inferred from pane scrollback. */\nexport type DetectedStatus = \"busy\" | \"needs_input\" | \"needs_permission\";\n\n/**\n * Number of lines from the bottom of the scrollback to consider.\n * Larger than TAIL_LINES so we can strip trailing blanks first without\n * running out of content.\n */\nconst TAIL_WINDOW_LINES = 100;\n\n/**\n * After stripping trailing blanks from the window, look at this many lines.\n * Narrow enough that a stale prompt one screen up doesn't match.\n */\nconst TAIL_LINES = 20;\n\n/**\n * Pi prints `Working... (Esc to interrupt)` via its loading animation while\n * streaming an LLM response or running a tool. We require the closing paren\n * (`to interrupt)`) to distinguish from pi's startup banner, which renders\n * the same hint as `<key> to interrupt` (no parens) in a list of\n * keybindings. Without the paren, a fresh pane with the banner still\n * visible would false-positive busy.\n */\nconst PI_BUSY_PATTERNS: readonly string[] = [\"to interrupt)\"];\n\n/**\n * Fallback busy signal: any character in the Unicode Braille block\n * (U+2800–U+28FF). Every TUI spinner library worth using cycles a\n * subset of these glyphs (⠇⠏⠙⠧⠷⠿⠟⠋⠈ …) every ~80ms while\n * blocking work runs. They essentially never appear in agent prose\n * output, so the false-positive risk is tiny.\n *\n * This catches every wrapper around pi (pi-meta + solo, claude-code,\n * codex …) whose chrome differs from vanilla pi enough that the\n * `to interrupt)` literal isn't in the tail — surfaced by the\n * multi-agent dogfood when pi-meta workers all classified as\n * `needs_input` mid-work. Tracked in roadmap-v0-2\n * `bug_status_detector_pi_solo_misclassifies`.\n */\nconst BRAILLE_SPINNER_RE = /[\\u2800-\\u28FF]/;\n\n/**\n * Pi's confirm / select / input dialogs render footer hints like\n * `(Esc to cancel, Enter to submit)`. Both `to submit)` (with closing\n * paren) and `to cancel,` (with the comma artifact of being the first of\n * the parenthesised pair) only appear inside dialog footers — not in\n * prose, not in the banner.\n */\nconst PI_PERMISSION_PATTERNS: readonly string[] = [\"to submit)\", \"to cancel,\"];\n\n/**\n * Run the detector against pane scrollback and return the inferred\n * status. Permission overrides busy.\n */\nexport function detectPiStatus(scrollback: string): DetectedStatus {\n const tail = extractTail(scrollback);\n if (matchesAny(tail, PI_PERMISSION_PATTERNS)) return \"needs_permission\";\n if (matchesAny(tail, PI_BUSY_PATTERNS)) return \"busy\";\n if (BRAILLE_SPINNER_RE.test(tail)) return \"busy\";\n return \"needs_input\";\n}\n\n/**\n * Public for tests — extract the tail window the detector actually\n * inspects. Take last TAIL_WINDOW_LINES, strip trailing blanks, take\n * last TAIL_LINES.\n */\nexport function extractTail(scrollback: string): string {\n const lines = scrollback.split(\"\\n\");\n const window = lines.slice(-TAIL_WINDOW_LINES);\n // Strip trailing blank lines (Codex/pi pad with blanks below content).\n let end = window.length;\n while (end > 0 && (window[end - 1] ?? \"\").trim() === \"\") {\n end--;\n }\n const start = Math.max(0, end - TAIL_LINES);\n return window.slice(start, end).join(\"\\n\");\n}\n\nfunction matchesAny(text: string, patterns: readonly string[]): boolean {\n for (const pattern of patterns) {\n if (text.includes(pattern)) return true;\n }\n return false;\n}\n","// mu — tmux substrate.\n//\n// Single source of truth for all tmux interactions. Every tmux invocation\n// goes through `tmux(args)`, which wraps execa and produces structured\n// `TmuxError`s carrying args + stderr.\n//\n// The send protocol is the bracketed-paste sequence (canonical\n// implementation lives in `sendToPane` below):\n// 1. copy-mode -q (silent if not in copy mode)\n// 2. set-buffer (load text into a uniquely named buffer)\n// 3. paste-buffer -p -d -r (bracketed paste, delete buffer, preserve LF)\n// 4. delay (MU_SEND_DELAY_MS, default 500)\n// 5. send-keys Enter\n//\n// Naive `tmux send-keys \"<text>\"` is broken: characters like /, ?, f get\n// interpreted by the agent's TUI (Claude, Codex, less, vim) or by tmux's\n// copy mode if the user has scrolled up. Use `sendToPane()`.\n\nimport { execa } from \"execa\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\n\n// ─── Error type ────────────────────────────────────────────────────────\n\nexport class TmuxError extends Error implements HasNextSteps {\n constructor(\n public readonly args: readonly string[],\n public readonly stderr: string,\n public readonly stdout: string,\n public readonly exitCode: number | null,\n ) {\n const detail = stderr.trim() || stdout.trim() || \"no output\";\n super(`tmux ${args.join(\" \")} failed (exit ${exitCode}): ${detail}`);\n this.name = \"TmuxError\";\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"Run health check\", command: \"mu doctor\" },\n {\n intent: \"Verify tmux is running and reachable\",\n command: \"tmux info | head\",\n },\n {\n intent: \"Check the failing tmux command in isolation\",\n command: `tmux ${this.args.join(\" \")}`,\n },\n ];\n }\n}\n\n/**\n * Thrown when a verb references a tmux pane id that doesn't exist on\n * the running tmux server. Distinct from TmuxError (which wraps any\n * tmux command failure) so callers can map it to a specific exit code\n * (`mu` maps it to 5 — substrate failure — alongside other tmux\n * issues, but the message is more actionable than a raw tmux stderr).\n */\nexport class PaneNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"PaneNotFoundError\";\n constructor(public readonly paneId: string) {\n super(`tmux pane not found: ${paneId}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: `Verify the pane id ${this.paneId} actually exists`,\n command: `tmux display-message -t ${this.paneId} -p '#{pane_id} #{pane_title}'`,\n },\n {\n intent: \"List all live panes across all sessions\",\n command:\n \"tmux list-panes -a -F '#{session_name}:#{window_id}.#{pane_id}\\\\t#{pane_title}\\\\t#{pane_current_command}'\",\n },\n {\n intent: \"List mu-managed agents (registered)\",\n command: \"mu agent list -w *\",\n },\n {\n intent: \"List orphan panes (look like agents, not registered)\",\n command: \"mu agent list -w * --json | jq '.[] | .orphans'\",\n },\n ];\n }\n}\n\n// ─── Pane ID validation ────────────────────────────────────────────────\n\n/**\n * Stable tmux pane IDs are of the form `%N` (e.g. \"%15\"). They never change\n * for the lifetime of the pane. **Pane indexes** (0, 1, 2…) are volatile and\n * shift when other panes close — never store or pass them.\n */\nexport const PANE_ID_RE = /^%\\d+$/;\n\nexport function isValidPaneId(s: string): boolean {\n return PANE_ID_RE.test(s);\n}\n\nexport function assertValidPaneId(s: string): void {\n if (!isValidPaneId(s)) {\n throw new TypeError(`invalid tmux pane id: ${JSON.stringify(s)} (expected /^%\\\\d+$/)`);\n }\n}\n\n// ─── Configurable delay ────────────────────────────────────────────────\n\n/**\n * Delay between bracketed-paste and Enter, in milliseconds. Claude/Codex/pi\n * process pasted text asynchronously; without this delay, Enter can arrive\n * before the agent has ingested the text. Defaults to 500; lower for tests,\n * raise for slow remotes via `MU_SEND_DELAY_MS`.\n */\nexport function defaultSendDelayMs(): number {\n const raw = process.env.MU_SEND_DELAY_MS;\n if (raw === undefined) return 500;\n const parsed = Number.parseInt(raw, 10);\n if (Number.isNaN(parsed) || parsed < 0) return 500;\n return parsed;\n}\n\n// ─── Executor (swappable for tests) ────────────────────────────────────\n\nexport interface TmuxExecResult {\n stdout: string;\n stderr: string;\n exitCode: number | null;\n}\n\nexport type TmuxExecutor = (args: readonly string[]) => Promise<TmuxExecResult>;\n\nconst realExecutor: TmuxExecutor = async (args) => {\n const result = await execa(\"tmux\", [...args], { reject: false });\n return {\n stdout: result.stdout ?? \"\",\n stderr: result.stderr ?? \"\",\n exitCode: result.exitCode ?? null,\n };\n};\n\nlet currentExecutor: TmuxExecutor = realExecutor;\n\n/**\n * Install a custom executor (for tests). Returns the previous executor so\n * tests can restore it cleanly. Production code should never call this.\n */\nexport function setTmuxExecutor(executor: TmuxExecutor): TmuxExecutor {\n const previous = currentExecutor;\n currentExecutor = executor;\n return previous;\n}\n\n/** Restore the real (execa-backed) executor. */\nexport function resetTmuxExecutor(): void {\n currentExecutor = realExecutor;\n}\n\n/**\n * Run an arbitrary tmux command. The single point of contact with the\n * tmux binary; every higher-level operation in this module goes through it.\n *\n * Throws `TmuxError` on non-zero exit. Returns stdout on success.\n */\nexport async function tmux(args: readonly string[]): Promise<string> {\n const result = await currentExecutor(args);\n if (result.exitCode !== 0) {\n throw new TmuxError([...args], result.stderr, result.stdout, result.exitCode);\n }\n return result.stdout;\n}\n\n// ─── Sleep helper (testable) ──────────────────────────────────────────\n\nlet currentSleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nexport function setSleepForTests(\n impl: (ms: number) => Promise<void>,\n): (ms: number) => Promise<void> {\n const previous = currentSleep;\n currentSleep = impl;\n return previous;\n}\n\nexport function resetSleep(): void {\n currentSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** Test-aware sleep — honours `setSleepForTests`. Public so other modules\n * (notably `agents.ts` for spawn liveness polling) get free no-op-ing in\n * tests without re-implementing the swap. */\nexport function sleep(ms: number): Promise<void> {\n return currentSleep(ms);\n}\n\n// ─── Domain types ──────────────────────────────────────────────────────\n\nexport interface TmuxSession {\n name: string;\n}\n\nexport interface TmuxWindow {\n /** tmux window id, e.g. \"@1\". */\n id: string;\n name: string;\n /** Session this window belongs to (only set by cross-session listings). */\n sessionName?: string;\n}\n\nexport interface TmuxPane {\n /** Stable tmux pane id, e.g. \"%15\". */\n paneId: string;\n /** Pane title set via `select-pane -T`. The agent's name in mu's convention. */\n title: string;\n /** Current foreground command (e.g. \"claude\", \"node\", \"bash\"). */\n command: string;\n /** Window this pane lives in. Only set by cross-window listings. */\n windowId?: string;\n /** Session this pane lives in. Only set by cross-session listings. */\n sessionName?: string;\n}\n\n// ─── Sessions ──────────────────────────────────────────────────────────\n\nexport async function listSessions(): Promise<TmuxSession[]> {\n // `list-sessions` exits 1 when no sessions exist; treat as empty.\n try {\n const out = await tmux([\"list-sessions\", \"-F\", \"#{session_name}\"]);\n return out\n .split(\"\\n\")\n .filter((line) => line.length > 0)\n .map((name) => ({ name }));\n } catch (err) {\n if (err instanceof TmuxError && /no server running|no sessions/i.test(err.stderr)) {\n return [];\n }\n throw err;\n }\n}\n\nexport async function sessionExists(name: string): Promise<boolean> {\n const result = await currentExecutor([\"has-session\", \"-t\", name]);\n return result.exitCode === 0;\n}\n\nexport interface NewSessionOptions {\n detached?: boolean;\n windowName?: string;\n command?: string;\n /** Initial working directory for the first pane (`-c <path>`). */\n cwd?: string;\n /** Extra env vars to set in the new pane via tmux `-e KEY=VALUE`.\n * Available since tmux 3.0; sets the variable in the new pane's\n * environment without polluting the tmux server's global env. */\n env?: Record<string, string>;\n}\n\nexport async function newSession(name: string, opts: NewSessionOptions = {}): Promise<void> {\n const args = [\"new-session\"];\n if (opts.detached !== false) args.push(\"-d\");\n args.push(\"-s\", name);\n if (opts.windowName) args.push(\"-n\", opts.windowName);\n if (opts.cwd) args.push(\"-c\", opts.cwd);\n appendEnvFlags(args, opts.env);\n if (opts.command) args.push(opts.command);\n await tmux(args);\n}\n\nexport interface NewSessionWithPaneOptions {\n windowName: string;\n command: string;\n cwd?: string;\n detached?: boolean;\n /** Extra env vars to set in the new pane via tmux `-e KEY=VALUE`. */\n env?: Record<string, string>;\n}\n\n/**\n * Create a tmux session AND its first window+pane in one atomic call.\n * Returns the new pane's stable id. Used by mu when spawning the first\n * agent in a workstream so we never end up with an empty `mu-<workstream>`\n * session left behind by a failed spawn.\n */\nexport async function newSessionWithPane(\n name: string,\n opts: NewSessionWithPaneOptions,\n): Promise<string> {\n const args = [\"new-session\"];\n if (opts.detached !== false) args.push(\"-d\");\n args.push(\"-s\", name, \"-n\", opts.windowName);\n if (opts.cwd) args.push(\"-c\", opts.cwd);\n appendEnvFlags(args, opts.env);\n args.push(\"-P\", \"-F\", \"#{pane_id}\", opts.command);\n const out = (await tmux(args)).trim();\n assertValidPaneId(out);\n return out;\n}\n\n/** Idempotent: succeeds even if the session is already gone. */\nexport async function killSession(name: string): Promise<void> {\n const result = await currentExecutor([\"kill-session\", \"-t\", name]);\n if (result.exitCode !== 0 && !/can't find session|session not found/i.test(result.stderr)) {\n throw new TmuxError(\n [\"kill-session\", \"-t\", name],\n result.stderr,\n result.stdout,\n result.exitCode,\n );\n }\n}\n\n// ─── Windows ───────────────────────────────────────────────────────────\n\nexport async function listWindows(session?: string): Promise<TmuxWindow[]> {\n if (session) {\n const out = await tmux([\"list-windows\", \"-t\", session, \"-F\", \"#{window_id}\\t#{window_name}\"]);\n return parseWindows(out);\n }\n // Cross-session: include the session name.\n const out = await tmux([\n \"list-windows\",\n \"-a\",\n \"-F\",\n \"#{session_name}\\t#{window_id}\\t#{window_name}\",\n ]);\n const windows: TmuxWindow[] = [];\n for (const line of out.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [sessionName, id, name] = line.split(\"\\t\");\n if (!sessionName || !id || name === undefined) continue;\n windows.push({ id, name, sessionName });\n }\n return windows;\n}\n\nfunction parseWindows(output: string): TmuxWindow[] {\n const windows: TmuxWindow[] = [];\n for (const line of output.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [id, name] = line.split(\"\\t\");\n if (!id || name === undefined) continue;\n windows.push({ id, name });\n }\n return windows;\n}\n\nexport interface NewWindowOptions {\n /** Target session. Required if invoking outside an existing tmux client. */\n session?: string;\n /** Window name. Maps to the agent's `tab:` value (or its name if no tab). */\n name: string;\n /** Command to run in the first pane. */\n command: string;\n /** If true, do not switch focus. Defaults to true. */\n detached?: boolean;\n /** Initial working directory (`-c <path>`). */\n cwd?: string;\n /** Extra env vars to set in the new pane via tmux `-e KEY=VALUE`. */\n env?: Record<string, string>;\n}\n\n/**\n * Create a new tmux window with one pane. Returns the new pane's stable\n * pane id (e.g. `%15`).\n */\nexport async function newWindow(opts: NewWindowOptions): Promise<string> {\n const args = [\"new-window\"];\n if (opts.detached !== false) args.push(\"-d\");\n if (opts.session) args.push(\"-t\", opts.session);\n args.push(\"-n\", opts.name);\n if (opts.cwd) args.push(\"-c\", opts.cwd);\n appendEnvFlags(args, opts.env);\n args.push(\"-P\", \"-F\", \"#{pane_id}\", opts.command);\n const out = (await tmux(args)).trim();\n assertValidPaneId(out);\n return out;\n}\n\n// ─── Panes ─────────────────────────────────────────────────────────────\n\n/**\n * List ALL panes in a tmux session (across every window). Used by\n * reconciliation to find every pane in the workstream's session.\n *\n * Note `list-panes -t <session>` (no -s) lists panes in the current\n * *window* of that session, not the whole session — a common gotcha.\n * `-s` is the flag that says \"all panes in this session.\"\n *\n * Returns `[]` (not throws) when the session doesn't exist or has no\n * panes. tmux destroys a session as soon as its last pane closes, so the\n * \"session was just here a moment ago\" case is normal during reconcile.\n * tmux's error wording in this case varies (\"can't find session\" or\n * \"can't find window\"), so we match either.\n */\nexport async function listPanesInSession(session: string): Promise<TmuxPane[]> {\n const args = [\n \"list-panes\",\n \"-s\",\n \"-t\",\n session,\n \"-F\",\n \"#{window_id}\\t#{pane_id}\\t#{pane_title}\\t#{pane_current_command}\",\n ];\n const result = await currentExecutor(args);\n if (result.exitCode !== 0) {\n if (/can't find (session|window)|no server running|no sessions/i.test(result.stderr)) {\n return [];\n }\n throw new TmuxError(args, result.stderr, result.stdout, result.exitCode);\n }\n const panes: TmuxPane[] = [];\n for (const line of result.stdout.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [windowId, paneId, title, command] = line.split(\"\\t\");\n if (!windowId || !paneId || command === undefined) continue;\n panes.push({ paneId, title: title ?? \"\", command, windowId });\n }\n return panes;\n}\n\n/**\n * List panes in the current session, a specific window/session target, or\n * all panes across all sessions when `target` is the literal \"*\".\n */\nexport async function listPanes(target?: string): Promise<TmuxPane[]> {\n if (target === \"*\") {\n const out = await tmux([\n \"list-panes\",\n \"-a\",\n \"-F\",\n \"#{session_name}\\t#{window_id}\\t#{pane_id}\\t#{pane_title}\\t#{pane_current_command}\",\n ]);\n const panes: TmuxPane[] = [];\n for (const line of out.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [sessionName, windowId, paneId, title, command] = line.split(\"\\t\");\n if (!sessionName || !windowId || !paneId || command === undefined) continue;\n panes.push({ paneId, title: title ?? \"\", command, windowId, sessionName });\n }\n return panes;\n }\n\n const args = [\"list-panes\"];\n if (target !== undefined) args.push(\"-t\", target);\n args.push(\"-F\", \"#{pane_id}\\t#{pane_title}\\t#{pane_current_command}\");\n const out = await tmux(args);\n const panes: TmuxPane[] = [];\n for (const line of out.split(\"\\n\")) {\n if (line.length === 0) continue;\n const [paneId, title, command] = line.split(\"\\t\");\n if (!paneId || command === undefined) continue;\n panes.push({ paneId, title: title ?? \"\", command });\n }\n return panes;\n}\n\nexport interface SplitWindowOptions {\n /** Target window or pane (e.g. \":Backend\" or \"%15\"). */\n target: string;\n command: string;\n /** Horizontal split (side-by-side). Default true. */\n horizontal?: boolean;\n detached?: boolean;\n /** Initial working directory for the new pane (`-c <path>`). */\n cwd?: string;\n /** Extra env vars to set in the new pane via tmux `-e KEY=VALUE`. */\n env?: Record<string, string>;\n}\n\n/**\n * Split a window and run a command in the new pane. Returns the new pane's\n * stable pane id.\n */\nexport async function splitWindow(opts: SplitWindowOptions): Promise<string> {\n const args = [\"split-window\"];\n if (opts.horizontal !== false) args.push(\"-h\");\n if (opts.detached !== false) args.push(\"-d\");\n args.push(\"-t\", opts.target);\n if (opts.cwd) args.push(\"-c\", opts.cwd);\n appendEnvFlags(args, opts.env);\n args.push(\"-P\", \"-F\", \"#{pane_id}\", opts.command);\n const out = (await tmux(args)).trim();\n assertValidPaneId(out);\n return out;\n}\n\n/**\n * Push one `-e KEY=VALUE` flag per entry into `args`, validating that\n * keys are non-empty and contain no `=` (tmux would error obscurely\n * otherwise; throwing TypeError keeps the failure at the call site).\n * No-op when `env` is undefined or empty.\n *\n * Iteration order follows Object.entries (insertion order); tests\n * shouldn't depend on a specific ordering, only on the presence of\n * each `-e KEY=VALUE` pair in the captured args.\n */\nfunction appendEnvFlags(args: string[], env: Record<string, string> | undefined): void {\n if (!env) return;\n for (const [k, v] of Object.entries(env)) {\n if (k.length === 0) {\n throw new TypeError(\"tmux env key must be non-empty\");\n }\n if (k.includes(\"=\")) {\n throw new TypeError(`tmux env key must not contain '=': ${JSON.stringify(k)}`);\n }\n args.push(\"-e\", `${k}=${v}`);\n }\n}\n\n/** Idempotent: succeeds even if the pane is already gone. */\nexport async function killPane(paneId: string): Promise<void> {\n assertValidPaneId(paneId);\n const result = await currentExecutor([\"kill-pane\", \"-t\", paneId]);\n if (result.exitCode !== 0 && !/can't find pane/i.test(result.stderr)) {\n throw new TmuxError([\"kill-pane\", \"-t\", paneId], result.stderr, result.stdout, result.exitCode);\n }\n}\n\nexport async function paneExists(paneId: string): Promise<boolean> {\n if (!isValidPaneId(paneId)) return false;\n // tmux's `display-message -t <bogus>` exits 0 but emits empty output; we\n // must check that the echoed pane id matches what we asked for.\n const result = await currentExecutor([\"display-message\", \"-t\", paneId, \"-p\", \"#{pane_id}\"]);\n if (result.exitCode !== 0) return false;\n return result.stdout.trim() === paneId;\n}\n\nexport async function setPaneTitle(paneId: string, title: string): Promise<void> {\n assertValidPaneId(paneId);\n await tmux([\"select-pane\", \"-t\", paneId, \"-T\", title]);\n}\n\n/**\n * Look up the window id (e.g. `@42`) that contains a given pane id\n * (e.g. `%15`). Used by spawn so we can apply window-scoped options\n * (`pane-border-status`) to the freshly created window.\n *\n * Returns undefined if the pane no longer exists.\n */\nexport async function getWindowIdForPane(paneId: string): Promise<string | undefined> {\n if (!isValidPaneId(paneId)) return undefined;\n const result = await currentExecutor([\"display-message\", \"-t\", paneId, \"-p\", \"#{window_id}\"]);\n if (result.exitCode !== 0) return undefined;\n const id = result.stdout.trim();\n return id.length > 0 ? id : undefined;\n}\n\n/**\n * Single source of truth for the operator opt-out from the mu pane\n * banner / border decorations. Set `MU_BANNER_QUIET=1` to disable.\n * All `enableMuPaneBorders*` helpers self-check this so callers\n * don't have to wrap them in env guards (a footgun: forget the\n * guard and you set the border even when the operator wanted\n * quiet).\n */\nfunction muBannersDisabled(): boolean {\n return process.env.MU_BANNER_QUIET === \"1\";\n}\n\n/**\n * Apply the mu pane border (status=top, format='[mu] #{pane_title}')\n * to EVERY window currently in `session`. Idempotent. Best-effort:\n * windows that have vanished mid-iteration are silently skipped. Used\n * by `mu workstream init` (covers the placeholder `_mu` window plus\n * any windows that already exist, e.g. on re-init of an upgraded\n * mu-pre-border session) and by `mu agent spawn` (covers the\n * just-created window so the border shows immediately on attach).\n *\n * No-op (returns 0) when `MU_BANNER_QUIET=1`.\n *\n * Returns the number of windows that received the option.\n */\nexport async function enableMuPaneBordersForSession(session: string): Promise<number> {\n if (muBannersDisabled()) return 0;\n const windows = await listWindows(session).catch(() => []);\n let n = 0;\n for (const w of windows) {\n try {\n await enableMuPaneBorders(w.id);\n n += 1;\n } catch {\n // Window vanished; skip silently. Border is decorative.\n }\n }\n return n;\n}\n\n/**\n * Apply the mu pane border to the window containing `paneId`. This is\n * the spawn/adopt shape: callers have a pane id (from `new-window` or\n * from an adopt target), and need to resolve the enclosing window\n * before calling `enableMuPaneBorders` (a window-scoped option).\n *\n * Self-checks `MU_BANNER_QUIET` and swallows tmux errors — the border\n * is decorative; failing to set it is never load-bearing.\n */\nexport async function enableMuPaneBordersForPane(paneId: string): Promise<void> {\n if (muBannersDisabled()) return;\n const wid = await getWindowIdForPane(paneId).catch(() => undefined);\n if (wid) await enableMuPaneBorders(wid).catch(() => {});\n}\n\n/**\n * Enable a one-line top pane border on a specific window/session target,\n * showing `[mu] <pane-title>`. Idempotent (set-option is a write, not\n * a toggle).\n *\n * IMPORTANT: tmux's `pane-border-status` and `pane-border-format` are\n * **window** options, not session options. `set-option -t <session>`\n * only updates the active window at call time — windows created later\n * inherit from the GLOBAL value (which is `off` by default and which\n * we deliberately do NOT touch, since changing the global would\n * affect every other tmux session on the user's machine, including\n * dotfile-curated ones).\n *\n * Therefore mu must call this twice:\n * 1. At `mu workstream init` time on the placeholder `_mu` window\n * (so an attached operator sees a border immediately).\n * 2. On every `mu agent spawn` (which calls `tmux new-window`),\n * against the new window's id.\n *\n * The border is tmux chrome, not pane content: it doesn't scroll, it\n * survives copy-mode, and the inner CLI never sees it.\n *\n * Designed in roadmap-v0-2 hud_visual_cue_design (note #283); shipped\n * in hud_visual_cue_impl.\n */\nexport async function enableMuPaneBorders(target: string): Promise<void> {\n if (muBannersDisabled()) return;\n await tmux([\"set-option\", \"-w\", \"-t\", target, \"pane-border-status\", \"top\"]);\n await tmux([\"set-option\", \"-w\", \"-t\", target, \"pane-border-format\", \" [mu] #{pane_title} \"]);\n // Bottom + sides: heavy box-drawing lines so a mu-managed pane is\n // visually distinct even when not the active pane (top carries the\n // labeled status text; the rest of the frame carries the visual\n // \"this is mu\" cue). Cyan-bold for the active pane, dim brightblack\n // for inactive ones, so the operator's eye lands on the pane that\n // currently has focus.\n await tmux([\"set-option\", \"-w\", \"-t\", target, \"pane-border-lines\", \"heavy\"]);\n await tmux([\"set-option\", \"-w\", \"-t\", target, \"pane-active-border-style\", \"fg=cyan,bold\"]);\n await tmux([\"set-option\", \"-w\", \"-t\", target, \"pane-border-style\", \"fg=brightblack\"]);\n}\n\nexport async function getPaneTitle(paneId: string): Promise<string | undefined> {\n if (!isValidPaneId(paneId)) return undefined;\n const result = await currentExecutor([\"display-message\", \"-t\", paneId, \"-p\", \"#{pane_title}\"]);\n if (result.exitCode !== 0) return undefined;\n return result.stdout.trimEnd();\n}\n\n/**\n * Read the title of the *current* pane (the one whose shell is running this\n * process), via $TMUX_PANE. Returns undefined when not inside tmux. Used by\n * `mu claim` to derive the agent identity from the pane title — the claim\n * protocol's zero-config identity step.\n */\nexport async function currentPaneTitle(): Promise<string | undefined> {\n const paneId = process.env.TMUX_PANE;\n if (!paneId || !isValidPaneId(paneId)) return undefined;\n return getPaneTitle(paneId);\n}\n\n/**\n * Read the *current* pane's interior size (`pane_width` x `pane_height`)\n * via $TMUX_PANE. Returns undefined when not inside tmux or when the\n * tmux call fails. Used by `mu hud` to size its tables when stdout\n * isn't a TTY (e.g. when running under `watch -n 5 mu hud -w X` or\n * `tmux display-popup -E 'mu hud -w X'`, both of which strip TTY-ness\n * but still run inside a tmux pane whose dimensions matter).\n */\nexport async function currentPaneSize(): Promise<{ width: number; height: number } | undefined> {\n const paneId = process.env.TMUX_PANE;\n if (!paneId || !isValidPaneId(paneId)) return undefined;\n const result = await currentExecutor([\n \"display-message\",\n \"-t\",\n paneId,\n \"-p\",\n \"#{pane_width} #{pane_height}\",\n ]);\n if (result.exitCode !== 0) return undefined;\n const parts = result.stdout.trim().split(/\\s+/);\n if (parts.length !== 2) return undefined;\n const [wStr, hStr] = parts;\n if (wStr === undefined || hStr === undefined) return undefined;\n const width = Number.parseInt(wStr, 10);\n const height = Number.parseInt(hStr, 10);\n if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {\n return undefined;\n }\n return { width, height };\n}\n\n/**\n * Extract the agent-name token from a (possibly composed) pane title.\n * mu's composeAgentTitle renders titles as `name · <glyph> · task_id`,\n * where <glyph> is a Nerd Font codepoint from STATUS_EMOJI (see\n * src/agents.ts). The agent name is always the first ' · '-separated\n * token. Adopted panes that haven't been re-titled by mu have just the\n * name (one token) — still parses.\n *\n * Returns trimmed name, or the input unchanged if no separator.\n */\nexport function parseAgentNameFromTitle(title: string): string {\n const idx = title.indexOf(\" · \");\n return idx === -1 ? title.trim() : title.slice(0, idx).trim();\n}\n\n/**\n * Convenience: read the current pane's title and extract the agent name.\n */\nexport async function currentAgentName(): Promise<string | undefined> {\n const title = await currentPaneTitle();\n if (title === undefined) return undefined;\n return parseAgentNameFromTitle(title);\n}\n\nexport async function selectLayout(window: string, layout: string): Promise<void> {\n await tmux([\"select-layout\", \"-t\", window, layout]);\n}\n\n// ─── Send protocol (the canonical bracketed-paste sequence) ────────────\n\nexport interface SendOptions {\n /** Override the default delay between paste and Enter, in ms. */\n delayMs?: number;\n}\n\n/**\n * Send a single line of text to a pane and submit it.\n *\n * Sequence:\n * 1. exit copy mode (silent if not in copy mode)\n * 2. load text into a uniquely-named tmux buffer\n * 3. paste with bracketed-paste mode (-p) so apps treat as literal text;\n * delete buffer after paste (-d); preserve LF (-r)\n * 4. wait MU_SEND_DELAY_MS (default 500) so the agent ingests the text\n * 5. send Enter as a real key event\n *\n * Naive `send-keys \"<text>\"` would let characters like /, ?, f, : be\n * interpreted by the agent's TUI or by tmux's copy mode. Always use this.\n */\nexport async function sendToPane(\n paneId: string,\n text: string,\n opts: SendOptions = {},\n): Promise<void> {\n assertValidPaneId(paneId);\n\n // 1. Exit copy mode silently. -q suppresses errors when not in copy mode.\n const copyResult = await currentExecutor([\"copy-mode\", \"-q\", \"-t\", paneId]);\n // Even with -q, some tmux versions report errors. Swallow non-fatal.\n if (copyResult.exitCode !== 0 && /can't find pane|no current target/i.test(copyResult.stderr)) {\n throw new TmuxError(\n [\"copy-mode\", \"-q\", \"-t\", paneId],\n copyResult.stderr,\n copyResult.stdout,\n copyResult.exitCode,\n );\n }\n\n // 2. Load text into a uniquely-named buffer.\n const bufferName = `mu-send-${process.pid}-${Date.now()}-${Math.floor(Math.random() * 1e6)}`;\n await tmux([\"set-buffer\", \"-b\", bufferName, text]);\n\n // 3. Bracketed paste: -p wraps in \\e[200~...\\e[201~ so apps see literal\n // text; -d deletes buffer after paste; -r preserves LF (no CR conversion).\n try {\n await tmux([\"paste-buffer\", \"-p\", \"-d\", \"-r\", \"-b\", bufferName, \"-t\", paneId]);\n } catch (err) {\n // Best-effort buffer cleanup if paste failed before -d took effect.\n await currentExecutor([\"delete-buffer\", \"-b\", bufferName]).catch(() => {});\n throw err;\n }\n\n // 4. Wait for the agent CLI to ingest the pasted text.\n const delay = opts.delayMs ?? defaultSendDelayMs();\n if (delay > 0) await currentSleep(delay);\n\n // 5. Submit. Enter must be a real key event, not part of the paste.\n await tmux([\"send-keys\", \"-t\", paneId, \"Enter\"]);\n}\n\n// ─── Capture ───────────────────────────────────────────────────────────\n\nexport interface CaptureOptions {\n /**\n * Number of trailing lines to capture. Omitted = full scrollback.\n * 0 = visible pane only.\n */\n lines?: number;\n}\n\n/**\n * Read pane scrollback as plain text (no ANSI escapes).\n *\n * - No options: full scrollback (`-S - -E -`)\n * - `lines: 0`: visible pane only\n * - `lines: N`: last N lines (`-S -N`)\n */\nexport async function capturePane(paneId: string, opts: CaptureOptions = {}): Promise<string> {\n assertValidPaneId(paneId);\n const args = [\"capture-pane\", \"-t\", paneId, \"-p\"];\n if (opts.lines === undefined) {\n args.push(\"-S\", \"-\", \"-E\", \"-\");\n } else if (opts.lines > 0) {\n args.push(\"-S\", `-${opts.lines}`);\n }\n return tmux(args);\n}\n","// mu — the canonical \"reality wins\" reconciliation routine.\n//\n// Three steps, in order:\n//\n// 1. Prune ghost rows whose pane no longer exists in tmux.\n// 2. Detect status from pane scrollback for surviving agents.\n// 3. Surface orphan panes that look like agents but have no DB row.\n// Do NOT auto-adopt — `mu list` shows orphans under a separate\n// section and the user runs `mu adopt` (roadmap) to formally claim.\n//\n// `mu list` and `mu doctor` both call this. It's the only place where\n// the registry's view of the world is reconciled against tmux's view.\n\nimport {\n type AgentRow,\n deleteAgent,\n isPendingPaneId,\n listAgents,\n refreshAgentTitle,\n resolveCliCommand,\n shouldOverwriteAgentStatus,\n updateAgentStatus,\n} from \"./agents.js\";\nimport type { Db } from \"./db.js\";\nimport { detectPiStatus } from \"./detect.js\";\nimport { type TmuxPane, capturePane, listPanesInSession } from \"./tmux.js\";\n\n/**\n * What kind of reconciliation pass to run.\n *\n * \"full\" Default for `mu agent list`. Prunes ghosts (deleting\n * the registry row, which fires the deleteAgent reaper\n * that flips IN_PROGRESS tasks back to OPEN with\n * [reaper] notes), runs status detection against\n * surviving panes, surfaces orphans.\n *\n * \"status-only\" The \"freshen the operator's view\" mode. Runs status\n * detection (DB writes that update agent status +\n * pane title — desired side-effects of a refresh) and\n * orphan surface. Does NOT prune (so a dead pane's\n * row stays visible until a real `mu agent list`) and\n * does NOT reap. Used by `mu state`, `mu hud`, bare\n * `mu`, and `mu agent attach` — the verbs an operator\n * polls to answer \"is worker-X busy or idle right\n * now?\". Status detection skips placeholder agents\n * whose pane id starts with `%pending-` (mid-spawn,\n * no usable scrollback yet).\n *\n * \"report-only\" Pure observation. Counts would-be-pruned ghosts\n * without deleting; skips status detection entirely\n * (no DB writes, no tmux title writes); surfaces\n * orphans (pure read). Used by `mu undo` (the\n * post-restore pass MUST NOT delete rows the snapshot\n * just restored — see\n * snap_undo_reconcile_destroys_recovered_agents) and\n * `mu doctor` (read-only diagnostic).\n *\n * Surfaced live by bug_pane_title_glyph_stuck_at_needs_input: the\n * old `dryRun: boolean` flag conflated \"don't prune\" with \"don't\n * detect status\", so `mu state` / `mu hud` showed stale status\n * indefinitely. Splitting prune-suppression from status-suppression\n * is the fix.\n */\nexport type ReconcileMode = \"full\" | \"status-only\" | \"report-only\";\n\nexport interface ReconcileOptions {\n /** The workstream whose registry rows we're reconciling. */\n workstream: string;\n /**\n * Override the tmux session name. Defaults to `mu-<workstream>`. Useful\n * for tests and for the rare case where a workstream's tmux session was\n * created with a non-default name.\n */\n tmuxSession?: string;\n /**\n * Which kind of pass to run. Default is `\"full\"` (the documented\n * mutating behaviour `mu agent list` has always had). See\n * `ReconcileMode` for the full per-mode contract.\n *\n * BREAKING: this replaces the previous `dryRun?: boolean` flag.\n * Migration: `dryRun: true` → `mode: \"report-only\"`; default\n * (`dryRun: false` / unset) → `mode: \"full\"`.\n */\n mode?: ReconcileMode;\n}\n\nexport interface ReconcileReport {\n /** Number of registry rows whose pane was gone. In status-only and\n * report-only modes this is the count of rows that WOULD have\n * been pruned; in `full` mode it's the count actually deleted. */\n prunedGhosts: number;\n /** Number of agents whose status was changed by scrollback detection.\n * Always 0 in `report-only` mode (status detection is skipped). */\n statusChanges: number;\n /** Panes in the workstream's tmux session that look like agents but\n * aren't in the registry. NOT auto-adopted. */\n orphans: TmuxPane[];\n /** Which mode this report was generated in. Lets callers switch their\n * output text (\"agents pruned\" vs \"would-be-pruned (suppressed)\")\n * without re-deriving from options. */\n mode: ReconcileMode;\n}\n\n/**\n * Pane commands that suggest \"this is an agent, surface it as an orphan.\"\n * 0.1.0 scope: pi only is detected, but the orphan list will surface\n * claude/codex panes too so the user can adopt them later.\n *\n * Also includes any env-overridden binary names (e.g. `MU_PI_COMMAND=pi-alt`\n * makes \"pi-alt\" agent-worthy) so externally-spawned panes running the\n * user's actual pi binary are still surfaced as orphans.\n */\nconst BASE_AGENT_CLIS: readonly string[] = [\"pi\", \"claude\", \"codex\"];\n\nfunction knownAgentCommands(): ReadonlySet<string> {\n const names = new Set<string>(BASE_AGENT_CLIS);\n for (const cli of BASE_AGENT_CLIS) {\n names.add(resolveCliCommand(cli));\n }\n return names;\n}\n\nexport async function reconcile(db: Db, opts: ReconcileOptions): Promise<ReconcileReport> {\n const sessionName = opts.tmuxSession ?? `mu-${opts.workstream}`;\n const mode: ReconcileMode = opts.mode ?? \"full\";\n const dbAgents = listAgents(db, { workstream: opts.workstream });\n const tmuxPanes = await listPanesInSession(sessionName);\n const tmuxByPaneId = new Map(tmuxPanes.map((p) => [p.paneId, p]));\n\n let prunedGhosts = 0;\n let statusChanges = 0;\n const orphans: TmuxPane[] = [];\n\n // 1. Prune ghosts (DB row references a pane that no longer exists).\n // Only `full` mode actually deletes; `status-only` and\n // `report-only` count the would-be-prunes so callers can surface\n // drift, but leave the row in place. The orphan-surface step\n // always treats them as \"not-in-tmux\" so the orphan list\n // semantics don't change.\n const survivors: AgentRow[] = [];\n for (const agent of dbAgents) {\n if (tmuxByPaneId.has(agent.paneId)) {\n survivors.push(agent);\n } else {\n if (mode === \"full\") deleteAgent(db, agent.name, agent.workstreamName);\n prunedGhosts++;\n }\n }\n\n // 2. Detect status from scrollback for survivors. capturePane uses the\n // last 100 lines, which is the same window the detector operates on.\n //\n // `report-only` skips this entirely — status detection writes to\n // the DB (updateAgentStatus + refreshAgentTitle), and the\n // report-only contract is \"no mutation\".\n //\n // `status-only` runs detection but skips placeholder agents\n // whose pane id starts with `%pending-` — those have no usable\n // scrollback yet (mid-spawn) and the placeholder pane id won't\n // resolve to a real tmux pane anyway. The pending sentinel is\n // documented in src/agents.ts (PENDING_PANE_PREFIX). Without\n // this skip, status-only would still try to capturePane on a\n // fake id and tmux would error.\n if (mode !== \"report-only\") {\n for (const agent of survivors) {\n if (isPendingPaneId(agent.paneId)) continue;\n const scrollback = await capturePane(agent.paneId, { lines: 100 });\n const detected = detectPiStatus(scrollback);\n if (shouldOverwriteAgentStatus(agent.status, detected) && detected !== agent.status) {\n updateAgentStatus(db, agent.name, detected, agent.workstreamName);\n statusChanges++;\n }\n // ALWAYS refresh the pane title (even when status didn't change),\n // so that:\n // 1. Inner CLIs that self-set their pane title (pi, pi-meta, vim,\n // tmux's default 'host - dir') get overwritten with mu's\n // composed title.\n // 2. Task-ownership changes that happen between reconciles\n // (claim / release / close) re-propagate even if the status\n // detector didn't flip.\n // Best-effort: a tmux failure here never blocks the reconcile report.\n await refreshAgentTitle(db, agent.name, agent.workstreamName);\n }\n }\n\n // 3. Surface orphan panes. `looksLikeAgentPane` is conservative:\n // pane.command must be one we recognise as an agent CLI. A bash\n // pane the user spawned for their own use is never an orphan.\n // Pure read; runs in every mode.\n const dbPaneIds = new Set(survivors.map((a) => a.paneId));\n for (const pane of tmuxPanes) {\n if (dbPaneIds.has(pane.paneId)) continue;\n if (looksLikeAgentPane(pane)) orphans.push(pane);\n }\n\n return { prunedGhosts, statusChanges, orphans, mode };\n}\n\nfunction looksLikeAgentPane(pane: TmuxPane): boolean {\n return knownAgentCommands().has(pane.command);\n}\n","// mu — snapshots SDK.\n//\n// Pre-mutation backups of the whole DB so destructive verbs become\n// recoverable. Implements §1-§4 of snap_design (note #293 on\n// snap_design):\n//\n// - captureSnapshot: VACUUM INTO a flat <state-dir>/snapshots/<id>.db\n// file, append a row to the `snapshots` table, run opportunistic GC.\n// - listSnapshots: read the table.\n// - restoreSnapshot: version-check the file, then file-swap onto the\n// live DB path. The caller is expected to be a short-lived `mu undo`\n// process; restore closes the live handle internally.\n//\n// Why VACUUM INTO and not the async db.backup() the design proposed:\n// VACUUM INTO is synchronous, which lets the destructive task verbs\n// (closeTask / rejectTask / deferTask / releaseTask / deleteTask) hook\n// it without an async refactor. Both produce identical standalone .db\n// files; both run page-level on the live DB; both honour FK integrity\n// across the snapshot. VACUUM INTO additionally drops free-list pages,\n// so snapshots are smaller than db.backup() output. See snap_schema's\n// task note for the deviation.\n//\n// What this module does NOT do (deliberately, per snap_design §3):\n// - `mu undo` / `mu snapshot list` CLI verbs (snap_undo_verb's job).\n// - `mu redo` (rejected in design — verbs have side effects we\n// can't replay).\n// - cross-version snapshot migration (rejected in design — refuse\n// instead).\n// - tmux-state rollback (snap_design §EDGE CASES — DB-only, by\n// design; reconcile after restore is the caller's job).\n\nimport {\n closeSync,\n copyFileSync,\n existsSync,\n mkdirSync,\n openSync,\n renameSync,\n statSync,\n unlinkSync,\n writeSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport Database from \"better-sqlite3\";\nimport { CURRENT_SCHEMA_VERSION, type Db, defaultStateDir } from \"./db.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\n\n// ─── Types ────────────────────────────────────────────────────────────\n\nexport interface SnapshotRow {\n /** Operator-facing snapshot id. EXCEPTION to the no-surrogate-ids rule:\n * snapshots have no human-meaningful name; the id is what the\n * operator types in `mu undo --to <id>` / `mu snapshot show <id>`. */\n id: number;\n /** NULL for whole-DB snapshots (e.g. workstream destroy). */\n workstreamName: string | null;\n /** Human-readable operation label, e.g. \"task close design\". */\n label: string;\n /** Absolute path to the .db file on disk. */\n dbPath: string;\n /** schema_version at the moment of capture. */\n schemaVersion: number;\n /** ISO-8601 capture timestamp. */\n createdAt: string;\n}\n\ninterface RawSnapshotRow {\n id: number;\n workstream: string | null;\n label: string;\n db_path: string;\n schema_version: number;\n created_at: string;\n}\n\nfunction rowFromDb(r: RawSnapshotRow): SnapshotRow {\n return {\n id: r.id,\n workstreamName: r.workstream,\n label: r.label,\n dbPath: r.db_path,\n schemaVersion: r.schema_version,\n createdAt: r.created_at,\n };\n}\n\nexport interface ListSnapshotsOptions {\n /** Filter to one workstream. NULL-workstream rows are also returned\n * when this is set, since they (workstream-destroy snapshots) span\n * every workstream including this one. */\n workstream?: string;\n /** Cap the number of rows returned. Default: no cap. */\n limit?: number;\n}\n\nexport interface CaptureSnapshotResult {\n id: number;\n dbPath: string;\n}\n\nexport interface RestoreSnapshotResult {\n id: number;\n /** The path the snapshot was copied to (the live DB path). */\n restoredTo: string;\n /** schema_version of the restored snapshot (== CURRENT_SCHEMA_VERSION\n * by virtue of having passed the version check). */\n schemaVersion: number;\n}\n\n// ─── Errors (typed; mapped to exit codes via cli.ts handle()) ─────────\n\nexport class SnapshotNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"SnapshotNotFoundError\";\n constructor(public readonly snapshotId: number) {\n super(`no such snapshot: ${snapshotId}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List available snapshots\", command: \"mu snapshot list\" },\n {\n intent: \"Look one up directly\",\n command: `mu sql \"SELECT id, label, created_at FROM snapshots ORDER BY id DESC\"`,\n },\n ];\n }\n}\n\n/**\n * Thrown by restoreSnapshot when the snapshot's schema_version doesn't\n * match the live DB's CURRENT_SCHEMA_VERSION. Maps to exit code 4\n * (conflict). Auto-migration of snapshot files was deliberately rejected\n * in snap_design note #293 (mutates forensic data; migrations are\n * forward-only).\n */\nexport class SnapshotVersionMismatchError extends Error implements HasNextSteps {\n override readonly name = \"SnapshotVersionMismatchError\";\n constructor(\n public readonly snapshotId: number,\n public readonly snapshotVersion: number,\n public readonly currentVersion: number,\n ) {\n const direction =\n snapshotVersion < currentVersion\n ? \"older — your DB has migrated past it\"\n : \"newer — written by a newer mu binary\";\n super(\n `snapshot ${snapshotId} is at schema v${snapshotVersion}; current DB is at v${currentVersion} (${direction}). mu does not auto-migrate snapshots; refusing restore.`,\n );\n }\n errorNextSteps(): NextStep[] {\n const olderSnapshot = this.snapshotVersion < this.currentVersion;\n return olderSnapshot\n ? [\n {\n intent: \"Pick a newer snapshot at the current schema\",\n command: `mu sql \"SELECT id, label, created_at FROM snapshots WHERE schema_version = ${this.currentVersion} ORDER BY id DESC\"`,\n },\n {\n intent: \"Inspect the stale snapshot read-only (snapshot is forensic; bypass mu)\",\n command: `sqlite3 <snapshot-path> \"SELECT * FROM tasks\"`,\n },\n ]\n : [\n {\n intent: \"Run mu with a newer binary that knows this schema\",\n command: \"npm install -g @martintrojer/mu@latest\",\n },\n ];\n }\n}\n\n/**\n * Thrown when the snapshot's .db file has been removed from disk (manual\n * cleanup, fs corruption) but the row still exists. Maps to exit code 3\n * (not found).\n */\nexport class SnapshotFileMissingError extends Error implements HasNextSteps {\n override readonly name = \"SnapshotFileMissingError\";\n constructor(\n public readonly snapshotId: number,\n public readonly dbPath: string,\n ) {\n super(`snapshot ${snapshotId} row exists but file is missing: ${dbPath}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Drop the orphan row\",\n command: `mu sql \"DELETE FROM snapshots WHERE id = ${this.snapshotId}\"`,\n },\n { intent: \"List remaining snapshots\", command: \"mu snapshot list\" },\n ];\n }\n}\n\n// ─── GC caps (snap_design §CAPTURE STRATEGY > GC) ─────────────────────\n//\n// Both caps are env-tunable, mirroring the MU_SPAWN_LIVENESS_MS /\n// MU_IDLE_THRESHOLD_MS pattern in src/agents.ts: typed reader fn,\n// default on bad input rather than throwing — env-var typos shouldn't\n// crash a destructive verb's auto-GC pass.\n//\n// MU_SNAPSHOT_KEEP_LAST (default 100)\n// MU_SNAPSHOT_MAX_AGE_DAYS (default 14)\n//\n// Used by gcSnapshots(); also by `mu snapshot prune` (no-flag form).\n\n/** Default count cap. */\nconst DEFAULT_GC_MAX_COUNT = 100;\n/** Default age cap, in days. */\nconst DEFAULT_GC_MAX_AGE_DAYS = 14;\n\n/** Read the operator-tunable count cap (`MU_SNAPSHOT_KEEP_LAST`). */\nexport function gcMaxCount(): number {\n const env = process.env.MU_SNAPSHOT_KEEP_LAST;\n if (env === undefined || env === \"\") return DEFAULT_GC_MAX_COUNT;\n const n = Number.parseInt(env, 10);\n if (!Number.isFinite(n) || n < 0) return DEFAULT_GC_MAX_COUNT;\n return n;\n}\n\n/** Read the operator-tunable age cap (`MU_SNAPSHOT_MAX_AGE_DAYS`). */\nexport function gcMaxAgeDays(): number {\n const env = process.env.MU_SNAPSHOT_MAX_AGE_DAYS;\n if (env === undefined || env === \"\") return DEFAULT_GC_MAX_AGE_DAYS;\n const n = Number.parseInt(env, 10);\n if (!Number.isFinite(n) || n < 0) return DEFAULT_GC_MAX_AGE_DAYS;\n return n;\n}\n\n// ─── snapshotsDir ─────────────────────────────────────────────────────\n\n/**\n * Resolve the snapshots directory.\n *\n * If a live `Db` handle is supplied, snapshots land under\n * `<dirname(db-path)>/snapshots/` — colocated with the DB they back.\n * This keeps snapshots discoverable for non-default DB paths\n * (`MU_DB_PATH=/some/place/foo.db` users) AND keeps tests that use\n * temp-dir DBs from polluting the user's `~/.local/state/mu/`.\n *\n * Without a Db handle, falls back to `<state-dir>/snapshots/` (the\n * canonical default per snap_design §WHERE).\n *\n * Flat (not per-workstream) by design: workstream-destroy snapshots\n * span every workstream so subdirs would lie about scope.\n */\nexport function snapshotsDir(db?: Db): string {\n if (db) {\n const livePath = (db as Db & { name: string }).name;\n if (livePath && livePath !== \":memory:\") {\n return join(dirname(livePath), \"snapshots\");\n }\n }\n return join(defaultStateDir(), \"snapshots\");\n}\n\n// ─── captureSnapshot ──────────────────────────────────────────────────\n\n/**\n * Take a whole-DB snapshot before a destructive verb mutates state.\n *\n * Steps:\n * 1. INSERT a row to claim an id.\n * 2. VACUUM INTO <state-dir>/snapshots/<id>.db. Synchronous; runs\n * page-level on the live DB without extra locks beyond SQLite's\n * existing busy_timeout.\n * 3. UPDATE the row with the canonical db_path (we couldn't know it\n * before step 1 because id is AUTOINCREMENT).\n * 4. Run opportunistic GC.\n *\n * If VACUUM INTO fails (disk full, perms, race), the row is rolled back\n * so the DB never points at a non-existent file. The original verb's\n * exception path still surfaces the underlying error.\n *\n * Idempotent on a same-instant double-call (each call gets its own id).\n */\nexport function captureSnapshot(\n db: Db,\n label: string,\n workstream: string | null = null,\n): CaptureSnapshotResult {\n const dir = snapshotsDir(db);\n mkdirSync(dir, { recursive: true });\n\n // Step 1: claim an id with a placeholder path. We need the id to\n // build the path; without it we'd race on filename selection.\n const insert = db\n .prepare(\n \"INSERT INTO snapshots (workstream, label, db_path, schema_version, created_at) VALUES (?, ?, ?, ?, ?)\",\n )\n .run(workstream, label, \"\", CURRENT_SCHEMA_VERSION, new Date().toISOString());\n const id = Number(insert.lastInsertRowid);\n const dbPath = join(dir, `${id}.db`);\n\n try {\n // Step 2: patch the row with the now-known db_path BEFORE running\n // VACUUM INTO. Order matters: VACUUM INTO snapshots the COMMITTED\n // DB state, so the snapshot file must already contain the correct\n // db_path on its own row. If we did VACUUM first then UPDATE, the\n // restored snapshot would yield a DB whose own snapshot row has\n // db_path='' — caught on the first round-trip smoke test of\n // snap_undo_verb.\n db.prepare(\"UPDATE snapshots SET db_path = ? WHERE id = ?\").run(dbPath, id);\n // Pre-unlink any stale file at this path. VACUUM INTO refuses to\n // overwrite (\"output file already exists\"), so we clear the slot\n // first. The slot is normally vacant — AUTOINCREMENT never reuses\n // ids within a DB — but two cases can leave a file behind:\n // 1. After restoreSnapshot, the DB's max-id rolls back to the\n // snapshot's value; next captures use ids that may match\n // files created in the abandoned forward timeline.\n // 2. Test isolation: each test opens its own DB (id counter\n // restarts at 1), but the snapshots dir is shared via\n // MU_STATE_DIR / default. Pre-unlink keeps tests independent.\n if (existsSync(dbPath)) unlinkSync(dbPath);\n // Step 3: VACUUM INTO is the SQLite-blessed way to produce a clean\n // standalone .db file. Synchronous; copies pages, drops free-list\n // entries, doesn't block writers beyond ordinary page locking.\n // Path is interpolated as a SQL string literal — safe because dir\n // and id are mu-controlled (state-dir + AUTOINCREMENT integer).\n db.exec(`VACUUM INTO ${quoteSqlString(dbPath)}`);\n } catch (err) {\n // Roll back the row so we never have a snapshot pointing at a\n // file that doesn't exist. Best-effort unlink in case VACUUM\n // INTO created a partial file before throwing.\n db.prepare(\"DELETE FROM snapshots WHERE id = ?\").run(id);\n try {\n if (existsSync(dbPath)) unlinkSync(dbPath);\n } catch {\n // Ignore — we're already on the failure path.\n }\n throw err;\n }\n\n // Step 4: opportunistic GC. Best-effort — a GC failure must not\n // break the destructive verb that triggered this snapshot.\n try {\n gcSnapshots(db);\n } catch {\n // Same rationale: insurance, not version history.\n }\n\n return { id, dbPath };\n}\n\n/** Quote a string for safe inclusion in a SQL literal. SQLite's TEXT\n * literal escaping is doubled single-quotes. */\nfunction quoteSqlString(s: string): string {\n return `'${s.replace(/'/g, \"''\")}'`;\n}\n\n// ─── listSnapshots ────────────────────────────────────────────────────\n\n/**\n * List snapshots, newest first. When `workstream` is set, returns rows\n * for that workstream PLUS rows with workstream = NULL (workstream-\n * destroy snapshots span every workstream so excluding them would hide\n * the most-recent restorable point during recovery).\n */\nexport function listSnapshots(db: Db, opts: ListSnapshotsOptions = {}): SnapshotRow[] {\n const conditions: string[] = [];\n const params: unknown[] = [];\n if (opts.workstream !== undefined) {\n conditions.push(\"(workstream = ? OR workstream IS NULL)\");\n params.push(opts.workstream);\n }\n const where = conditions.length > 0 ? `WHERE ${conditions.join(\" AND \")}` : \"\";\n const limit = opts.limit !== undefined ? `LIMIT ${Math.max(0, Math.floor(opts.limit))}` : \"\";\n const rows = db\n .prepare(`SELECT * FROM snapshots ${where} ORDER BY id DESC ${limit}`)\n .all(...params) as RawSnapshotRow[];\n return rows.map(rowFromDb);\n}\n\n// ─── restoreSnapshot ──────────────────────────────────────────────────\n\n/**\n * Restore a snapshot by file-swapping its .db onto the live DB path.\n *\n * Caller contract: pass the live `Db` handle so we can read the live DB\n * path, the snapshot row, and emit a pre-restore self-snapshot for the\n * \"undo of undo\" case (snap_design §EDGE CASES > snapshot-of-snapshot).\n *\n * The caller is expected to be a short-lived `mu undo` process: this\n * function CLOSES `db` after taking the pre-restore snapshot, then\n * fs.copyFileSync's the snapshot file onto the live DB path and unlinks\n * any -wal / -shm sidecars. Any other live mu process holding the DB\n * will see SQLITE_BUSY / disk-image-malformed on next write and exit\n * cleanly (snap_design recommends gating the verb behind --yes for\n * exactly this reason; that's snap_undo_verb's surface, not ours).\n */\nexport function restoreSnapshot(db: Db, snapshotId: number): RestoreSnapshotResult {\n const row = db.prepare(\"SELECT * FROM snapshots WHERE id = ?\").get(snapshotId) as\n | RawSnapshotRow\n | undefined;\n if (!row) throw new SnapshotNotFoundError(snapshotId);\n if (!existsSync(row.db_path)) {\n throw new SnapshotFileMissingError(snapshotId, row.db_path);\n }\n if (row.schema_version !== CURRENT_SCHEMA_VERSION) {\n throw new SnapshotVersionMismatchError(snapshotId, row.schema_version, CURRENT_SCHEMA_VERSION);\n }\n\n // Pre-restore snapshot so `mu undo` after `mu undo` works\n // (snap_design §EDGE CASES > snapshot-of-snapshot). Capture BEFORE\n // we close the live handle. The .db file lands on disk under\n // <state-dir>/snapshots/<preId>.db; the row goes into the live DB\n // — which we are about to overwrite. We therefore stash the row\n // metadata and re-insert it into the post-restore DB so the user\n // can `mu undo` again to roll back this restore.\n const pre = captureSnapshot(db, `pre-restore of snapshot ${snapshotId}`, row.workstream);\n const preCreatedAt =\n (\n db.prepare(\"SELECT created_at FROM snapshots WHERE id = ?\").get(pre.id) as\n | { created_at: string }\n | undefined\n )?.created_at ?? new Date().toISOString();\n\n // Resolve the live DB path BEFORE close (better-sqlite3's `name` is\n // the path supplied at open time).\n const livePath = (db as Db & { name: string }).name;\n if (!livePath || livePath === \":memory:\") {\n throw new Error(\n `restoreSnapshot: refusing to restore over a non-file DB handle (path=${JSON.stringify(livePath)})`,\n );\n }\n\n db.close();\n\n // Atomic-ish swap: copy snapshot to a temp sibling, fsync, rename\n // over the live path. rename(2) is atomic within the same filesystem\n // (which the temp + live both are by construction).\n const tmpPath = `${livePath}.restore-${snapshotId}.tmp`;\n copyFileSync(row.db_path, tmpPath);\n // fsync the copy so the rename can't expose a half-written file on\n // crash. Best-effort: openSync may throw on exotic filesystems.\n try {\n const fd = openSync(tmpPath, \"r+\");\n try {\n writeSync(fd, Buffer.alloc(0), 0, 0, 0);\n } finally {\n closeSync(fd);\n }\n } catch {\n // ignore — fsync is best-effort here\n }\n renameSync(tmpPath, livePath);\n\n // Nuke -wal / -shm sidecars: the snapshot file is a clean checkpoint\n // (VACUUM INTO produces a fully-merged DB); leftover sidecars from\n // the OLD live DB would confuse SQLite's recovery.\n for (const sidecar of [`${livePath}-wal`, `${livePath}-shm`]) {\n if (existsSync(sidecar)) {\n try {\n unlinkSync(sidecar);\n } catch {\n // ignore — sqlite will recreate on next open\n }\n }\n }\n\n // Re-stamp the pre-restore snapshot row into the post-restore DB.\n // Use a fresh short-lived connection so the caller doesn't have to\n // know we did it; the connection is closed before we return.\n // Forced-id INSERT keeps any references stable.\n const tmp = new Database(livePath);\n try {\n tmp.pragma(\"foreign_keys = ON\");\n // The pre-restore row's id MAY collide with an id that was\n // already in the snapshot's row set. Use INSERT OR IGNORE: the\n // file on disk is what matters; if a row with that id is\n // already there (because the snapshot itself recorded the\n // history), leave it alone. The pre-restore .db file is still\n // on disk and discoverable via `ls <state-dir>/snapshots/`.\n tmp\n .prepare(\n \"INSERT OR IGNORE INTO snapshots (id, workstream, label, db_path, schema_version, created_at) VALUES (?, ?, ?, ?, ?, ?)\",\n )\n .run(\n pre.id,\n row.workstream,\n `pre-restore of snapshot ${snapshotId}`,\n pre.dbPath,\n CURRENT_SCHEMA_VERSION,\n preCreatedAt,\n );\n } finally {\n tmp.close();\n }\n\n return {\n id: snapshotId,\n restoredTo: livePath,\n schemaVersion: row.schema_version,\n };\n}\n\n// ─── Garbage collection (snap_design §CAPTURE STRATEGY > GC) ──────────\n\n/**\n * Drop snapshots that are EITHER past the count cap OR past the age\n * cap — \"whichever cap is more permissive wins\" (snap_design §GC).\n * Concretely: keep the N most recent AND keep everything <D days old;\n * delete the rest (and their on-disk .db files).\n *\n * The caps come from `gcMaxCount()` / `gcMaxAgeDays()` (env-tunable\n * via `MU_SNAPSHOT_KEEP_LAST` / `MU_SNAPSHOT_MAX_AGE_DAYS`).\n *\n * NOTE: prior to snapshot_gc_caps_too_lax_no_cleanup_verb the WHERE\n * was `created_at < cutoff AND id NOT IN protected`, i.e. \"delete\n * only if BOTH old AND past the count cap\". That made the count cap\n * effectively dead under bursty use (every row was younger than the\n * 14-day age cap, so the date filter spared everything regardless of\n * row count). The fix flips AND→OR.\n *\n * Best-effort on file unlink: if a file is already gone, the row goes\n * anyway (the user's intent — \"this snapshot is gone\" — is satisfied).\n */\nexport function gcSnapshots(db: Db): { deletedRows: number; deletedFiles: number } {\n const keepLast = gcMaxCount();\n const cutoffDate = new Date(Date.now() - gcMaxAgeDays() * 24 * 60 * 60 * 1000).toISOString();\n // Get the count-cap-protected ids: top `keepLast` rows by id DESC.\n const protectedIds = (\n db.prepare(`SELECT id FROM snapshots ORDER BY id DESC LIMIT ${keepLast}`).all() as Array<{\n id: number;\n }>\n ).map((r) => r.id);\n const placeholders = protectedIds.length > 0 ? protectedIds.map(() => \"?\").join(\",\") : \"NULL\";\n // Drop a row if it's outside the count-protected set OR older than\n // the age cap. (OR, not AND — see docstring.)\n const victims = db\n .prepare(\n `SELECT id, db_path FROM snapshots WHERE id NOT IN (${placeholders}) OR created_at < ?`,\n )\n .all(...protectedIds, cutoffDate) as Array<{ id: number; db_path: string }>;\n\n if (victims.length === 0) return { deletedRows: 0, deletedFiles: 0 };\n\n let deletedFiles = 0;\n for (const v of victims) {\n try {\n if (existsSync(v.db_path)) {\n unlinkSync(v.db_path);\n deletedFiles += 1;\n }\n } catch {\n // ignore — orphan file is preferable to a half-completed GC\n }\n }\n\n const ids = victims.map((v) => v.id);\n const inList = ids.map(() => \"?\").join(\",\");\n const result = db.prepare(`DELETE FROM snapshots WHERE id IN (${inList})`).run(...ids);\n return { deletedRows: result.changes, deletedFiles };\n}\n\n// ─── Manual cleanup verbs ─────────────────────────────────────────────\n//\n// snapshot_gc_caps_too_lax_no_cleanup_verb: the auto-GC above runs\n// opportunistically on every captureSnapshot, but operators occasionally\n// need surgical control (\"the .db files filled the disk\", \"a schema\n// bump made every pre-bump snapshot unrestorable, drop them\"). Two\n// SDK entry points cover that:\n//\n// pruneSnapshots(db, opts) — bulk policy-driven cleanup\n// (`mu snapshot prune` CLI)\n// deleteSnapshot(db, id) — surgical single-row removal\n// (`mu snapshot delete <id>` CLI)\n\nexport type PruneMode = \"gc\" | \"keep-last\" | \"older-than\" | \"stale-version\" | \"all\";\n\nexport interface PruneOptions {\n mode: PruneMode;\n /** For mode='keep-last'. Required by the CLI. */\n keepLast?: number;\n /** For mode='older-than'. Days; required by the CLI. */\n olderThanDays?: number;\n /** When true, return the would-delete shape but don't touch the DB\n * or the on-disk .db files. */\n dryRun?: boolean;\n}\n\nexport interface PruneResult {\n /** Rows that would be / were deleted. Always populated, even on\n * dry-run (the CLI's summary uses it). */\n victims: SnapshotRow[];\n /** Total bytes that would be / were freed (sum of victim file\n * sizes; missing files contribute 0). */\n freedBytes: number;\n /** Number of `snapshots` rows actually deleted. 0 on dry-run. */\n deletedRows: number;\n /** Number of on-disk .db files actually unlinked. 0 on dry-run. */\n deletedFiles: number;\n /** Set when mode='all' and dryRun=false: id of the safety-net\n * snapshot captured BEFORE the wipe. (Survives the wipe.) */\n safetyNetSnapshotId?: number;\n}\n\nexport class PruneOptionsInvalidError extends Error implements HasNextSteps {\n override readonly name = \"PruneOptionsInvalidError\";\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"Show prune options\", command: \"mu snapshot prune --help\" },\n { intent: \"List snapshots\", command: \"mu snapshot list\" },\n ];\n }\n}\n\n/** True if a snapshot row's schema_version doesn't match the live DB's\n * CURRENT_SCHEMA_VERSION. Stale snapshots are unrestorable (restore\n * raises SnapshotVersionMismatchError) — surfaced dimmed in\n * `mu snapshot list` and as the target set of `prune --stale-version`. */\nexport function isStaleVersion(row: { schemaVersion: number }): boolean {\n return row.schemaVersion !== CURRENT_SCHEMA_VERSION;\n}\n\n/**\n * Bulk policy-driven cleanup. The CLI's `mu snapshot prune` verb is\n * a thin wrapper. Modes:\n *\n * gc — apply the auto-GC policy explicitly (same as the\n * opportunistic call inside captureSnapshot).\n * keep-last — keep only the N newest rows.\n * older-than — drop rows whose created_at is older than D days.\n * stale-version — drop rows whose schema_version != current.\n * all — drop EVERY row. dryRun=false additionally captures\n * a safety-net snapshot of the live DB FIRST, so a\n * subsequent `mu undo` can recover; the safety-net\n * row survives the wipe.\n *\n * On dryRun=true: returns the victim set + freed-bytes total without\n * touching the DB or the filesystem.\n */\nexport function pruneSnapshots(db: Db, opts: PruneOptions): PruneResult {\n const mode = opts.mode;\n // ─ Mode-specific victim selection ─────────────────────────────────\n let victims: SnapshotRow[];\n let safetyNetSnapshotId: number | undefined;\n switch (mode) {\n case \"gc\": {\n victims = computeGcVictims(db);\n break;\n }\n case \"keep-last\": {\n if (opts.keepLast === undefined || !Number.isInteger(opts.keepLast) || opts.keepLast < 0) {\n throw new PruneOptionsInvalidError(\n `--keep-last requires a non-negative integer; got ${JSON.stringify(opts.keepLast)}`,\n );\n }\n victims = computeKeepLastVictims(db, opts.keepLast);\n break;\n }\n case \"older-than\": {\n if (\n opts.olderThanDays === undefined ||\n !Number.isFinite(opts.olderThanDays) ||\n opts.olderThanDays < 0\n ) {\n throw new PruneOptionsInvalidError(\n `--older-than requires a non-negative number of days; got ${JSON.stringify(opts.olderThanDays)}`,\n );\n }\n victims = computeOlderThanVictims(db, opts.olderThanDays);\n break;\n }\n case \"stale-version\": {\n victims = listSnapshots(db).filter(isStaleVersion);\n break;\n }\n case \"all\": {\n victims = listSnapshots(db);\n break;\n }\n default: {\n // Defensive: TS keeps this exhaustive, but bad input from JS\n // (e.g. SDK consumers) shouldn't crash.\n throw new PruneOptionsInvalidError(`unknown prune mode: ${JSON.stringify(mode)}`);\n }\n }\n\n // Pre-compute freed bytes from on-disk file sizes BEFORE we unlink.\n // Missing files contribute 0 (they were already gone — nothing to\n // free). statSync inside snapshotFileSize swallows ENOENT.\n let freedBytes = 0;\n for (const v of victims) {\n const sz = snapshotFileSize(v);\n if (sz !== null) freedBytes += sz;\n }\n\n if (opts.dryRun === true) {\n return { victims, freedBytes, deletedRows: 0, deletedFiles: 0 };\n }\n\n // ─ Mode='all': capture the safety-net snapshot FIRST. Without\n // this, --all is the only mu verb that nukes the user's only\n // recovery path. Capture happens BEFORE we delete anything so\n // the safety-net snapshot itself survives the wipe (it gets a\n // fresh id, gets inserted by captureSnapshot, then we delete\n // `victims` which were captured before the safety-net write).\n if (mode === \"all\") {\n // captureSnapshot also runs gcSnapshots opportunistically; that's\n // fine — the GC's victims overlap our `victims` set, and the\n // overlap is harmless (we're about to delete the same rows).\n const cap = captureSnapshot(db, \"snapshot prune --all (safety-net)\", null);\n safetyNetSnapshotId = cap.id;\n }\n\n if (victims.length === 0) {\n return {\n victims,\n freedBytes,\n deletedRows: 0,\n deletedFiles: 0,\n ...(safetyNetSnapshotId !== undefined ? { safetyNetSnapshotId } : {}),\n };\n }\n\n // Unlink files first (best-effort, same policy as gcSnapshots).\n let deletedFiles = 0;\n for (const v of victims) {\n try {\n if (existsSync(v.dbPath)) {\n unlinkSync(v.dbPath);\n deletedFiles += 1;\n }\n } catch {\n // ignore — orphan file is preferable to a half-completed prune\n }\n }\n\n const ids = victims.map((v) => v.id);\n const inList = ids.map(() => \"?\").join(\",\");\n const result = db.prepare(`DELETE FROM snapshots WHERE id IN (${inList})`).run(...ids);\n return {\n victims,\n freedBytes,\n deletedRows: result.changes,\n deletedFiles,\n ...(safetyNetSnapshotId !== undefined ? { safetyNetSnapshotId } : {}),\n };\n}\n\nfunction computeGcVictims(db: Db): SnapshotRow[] {\n const keepLast = gcMaxCount();\n const cutoffDate = new Date(Date.now() - gcMaxAgeDays() * 24 * 60 * 60 * 1000).toISOString();\n const protectedIds = (\n db.prepare(`SELECT id FROM snapshots ORDER BY id DESC LIMIT ${keepLast}`).all() as Array<{\n id: number;\n }>\n ).map((r) => r.id);\n const placeholders = protectedIds.length > 0 ? protectedIds.map(() => \"?\").join(\",\") : \"NULL\";\n const rows = db\n .prepare(\n `SELECT * FROM snapshots WHERE id NOT IN (${placeholders}) OR created_at < ? ORDER BY id DESC`,\n )\n .all(...protectedIds, cutoffDate) as RawSnapshotRow[];\n return rows.map(rowFromDb);\n}\n\nfunction computeKeepLastVictims(db: Db, n: number): SnapshotRow[] {\n // Victims = every row except the top-N by id DESC.\n const rows = db\n .prepare(\n `SELECT * FROM snapshots\n WHERE id NOT IN (SELECT id FROM snapshots ORDER BY id DESC LIMIT ?)\n ORDER BY id DESC`,\n )\n .all(n) as RawSnapshotRow[];\n return rows.map(rowFromDb);\n}\n\nfunction computeOlderThanVictims(db: Db, days: number): SnapshotRow[] {\n const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();\n const rows = db\n .prepare(\"SELECT * FROM snapshots WHERE created_at < ? ORDER BY id DESC\")\n .all(cutoff) as RawSnapshotRow[];\n return rows.map(rowFromDb);\n}\n\nexport interface DeleteSnapshotResult {\n /** Always true on success. (Misses raise SnapshotNotFoundError; the\n * shape mirrors `deleteTask`'s structured-result style.) */\n deleted: true;\n /** 1 if the .db file was on disk + unlinked; 0 if it was already\n * gone (orphaned row). */\n deletedFiles: 0 | 1;\n /** Bytes freed by unlinking the .db file. 0 when the file was\n * already gone. */\n freedBytes: number;\n}\n\n/**\n * Surgical removal of one snapshot: drop the `snapshots` row + unlink\n * the on-disk .db file. Mirrors `mu task delete`. Errors with\n * `SnapshotNotFoundError` on miss.\n *\n * No auto-snapshot before the delete: the point IS to delete one row,\n * and removing one stepping-stone can't break `mu undo` (it still has\n * every other snapshot). Auto-snapshotting here would be circular.\n */\nexport function deleteSnapshot(db: Db, snapshotId: number): DeleteSnapshotResult {\n const row = db.prepare(\"SELECT * FROM snapshots WHERE id = ?\").get(snapshotId) as\n | RawSnapshotRow\n | undefined;\n if (!row) throw new SnapshotNotFoundError(snapshotId);\n // Capture size BEFORE we unlink so freedBytes is accurate.\n let freedBytes = 0;\n let deletedFiles: 0 | 1 = 0;\n try {\n if (existsSync(row.db_path)) {\n freedBytes = statSync(row.db_path).size;\n unlinkSync(row.db_path);\n deletedFiles = 1;\n }\n } catch {\n // best-effort; the row goes either way\n }\n db.prepare(\"DELETE FROM snapshots WHERE id = ?\").run(snapshotId);\n return { deleted: true, deletedFiles, freedBytes };\n}\n\n// ─── stat helper (used by tests; cheap to expose) ─────────────────────\n\n/** Return the on-disk size of the snapshot file in bytes, or null if\n * the file is missing. Useful for `mu snapshot list --json` output. */\nexport function snapshotFileSize(snapshot: SnapshotRow): number | null {\n try {\n return statSync(snapshot.dbPath).size;\n } catch {\n return null;\n }\n}\n","// mu — task error classes.\n//\n// Every task verb that can fail in a typed way has its own error class\n// here. The CLI's `classifyError()` (src/cli.ts) maps them to exit codes:\n// not found → 3 (TaskNotFoundError)\n// conflict → 4 (TaskExistsError, TaskNotInWorkstreamError,\n// TaskAlreadyOwnedError, TaskHasOpenDependentsError,\n// ClaimerNotRegisteredError, CrossWorkstreamEdgeError,\n// TaskIdInvalidError)\n// cycle → 4 (CycleError — also a conflict)\n//\n// Each error implements HasNextSteps so the CLI can render a per-error\n// `Next:` block with the most useful follow-up commands.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nimport type { HasNextSteps, NextStep } from \"../output.js\";\nimport { sanitiseTaskId } from \"../tasks.js\";\n\nexport class TaskNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"TaskNotFoundError\";\n constructor(public readonly taskId: string) {\n super(`no such task: ${taskId}`);\n }\n errorNextSteps(): NextStep[] {\n // v5: tasks.workstream (TEXT) was replaced by tasks.workstream_id\n // (FK → workstreams.id). The pre-v5 recipe SELECTed a column that\n // no longer exists and crashed at runtime — and this is THE first\n // hint a user sees on a missed-task lookup, so the breakage was\n // high-traffic. Mirror the join pattern used by AgentExistsError.\n const idLit = this.taskId.replace(/'/g, \"''\").toLowerCase();\n const recipe = `mu sql \"SELECT ws.name AS workstream, t.local_id, t.status, t.title FROM tasks t JOIN workstreams ws ON ws.id = t.workstream_id WHERE LOWER(t.local_id) LIKE '%${idLit}%' OR LOWER(t.title) LIKE '%${idLit}%'\"`;\n return [\n { intent: \"List tasks in workstream\", command: \"mu task list -w <workstream>\" },\n { intent: \"Search by substring (id + title)\", command: recipe },\n { intent: \"Find which workstream owns it\", command: recipe },\n ];\n }\n}\n\n/**\n * Thrown by `addTask` when `localId` violates the schema regex\n * `/^[a-z][a-z0-9_-]{0,63}$/`. Replaces a bare `TypeError` so the\n * CLI's `handle()` wrapper can map it to exit code 4 (validation /\n * conflict) and surface a `--json` `nextSteps` block pointing at\n * the auto-derived-id workflow and a sanitised candidate.\n */\nexport class TaskIdInvalidError extends Error implements HasNextSteps {\n override readonly name = \"TaskIdInvalidError\";\n constructor(public readonly attempted: string) {\n super(`invalid task id: ${JSON.stringify(attempted)} (expected /^[a-z][a-z0-9_-]{0,63}$/)`);\n }\n errorNextSteps(): NextStep[] {\n const sanitised = sanitiseTaskId(this.attempted);\n return [\n {\n intent: \"Use the auto-derived id (drop --id and pass --title)\",\n command: 'mu task add --title \"...\" --impact <n> --effort-days <n>',\n },\n {\n intent: \"Sanitise to a valid id\",\n command: `mu task add ${sanitised} --title \"...\" --impact <n> --effort-days <n>`,\n },\n ];\n }\n}\n\nexport class TaskExistsError extends Error implements HasNextSteps {\n override readonly name = \"TaskExistsError\";\n constructor(public readonly taskId: string) {\n super(`task already exists: ${taskId}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"Show the existing task\", command: `mu task show ${this.taskId}` },\n {\n intent: \"Update fields on the existing task\",\n command: `mu task update ${this.taskId} --title \"...\" --impact <n> --effort-days <n>`,\n },\n {\n intent: \"Pick a different id\",\n command: 'mu task add <new-id> --title \"...\" --impact <n> --effort-days <n>',\n },\n ];\n }\n}\n\n/**\n * Thrown when a verb is invoked with `-w/--workstream <name>` but the\n * named task lives in a different workstream. Distinguishes \"the user\n * typo'd the workstream\" from \"the task doesn't exist anywhere\"\n * (which surfaces as `TaskNotFoundError`). Maps to exit code 4\n * (conflict / wrong scope).\n */\nexport class TaskNotInWorkstreamError extends Error implements HasNextSteps {\n override readonly name = \"TaskNotInWorkstreamError\";\n constructor(\n public readonly taskId: string,\n public readonly expectedWorkstream: string,\n public readonly actualWorkstream: string,\n ) {\n super(`task ${taskId} is in workstream ${actualWorkstream}, not ${expectedWorkstream}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Use the correct workstream\",\n command: `mu task show ${this.taskId} -w ${this.actualWorkstream}`,\n },\n {\n intent: \"List tasks in the requested workstream\",\n command: `mu task list -w ${this.expectedWorkstream}`,\n },\n ];\n }\n}\n\nexport class TaskAlreadyOwnedError extends Error implements HasNextSteps {\n override readonly name = \"TaskAlreadyOwnedError\";\n constructor(\n public readonly taskId: string,\n public readonly currentOwner: string,\n ) {\n super(`task ${taskId} is already owned by ${currentOwner}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"See the current owner's task list\",\n command: `mu task owned-by ${this.currentOwner}`,\n },\n {\n intent: \"Release the current claim (if you ARE the owner)\",\n command: `mu task release ${this.taskId}`,\n },\n { intent: \"Show full task state\", command: `mu task show ${this.taskId}` },\n ];\n }\n}\n\n/**\n * Thrown by `rejectTask` / `deferTask` when the target task has\n * dependents that are still OPEN or IN_PROGRESS. Rejecting or\n * deferring such a task would silently strand the dependents (they'd\n * remain blocked by a prereq that's never going to satisfy the edge),\n * so we refuse and force an explicit decision: pass `--cascade` to\n * apply the same status to every transitive dependent, drop the\n * blocking edge first with `mu task unblock`, or address the\n * dependents individually. Maps to exit code 4.\n */\nexport class TaskHasOpenDependentsError extends Error implements HasNextSteps {\n override readonly name = \"TaskHasOpenDependentsError\";\n constructor(\n public readonly taskId: string,\n public readonly verb: \"reject\" | \"defer\",\n public readonly dependents: readonly string[],\n ) {\n super(\n `cannot ${verb} ${taskId}: ${dependents.length} open dependent(s) would be stranded (${dependents.slice(0, 5).join(\", \")}${dependents.length > 5 ? \", …\" : \"\"}). Pick one resolution and re-run.`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: `Preview the cascade (lists dependents that would be ${this.verb}ed; --cascade alone is dry-run)`,\n command: `mu task ${this.verb} ${this.taskId} --cascade`,\n },\n {\n intent: `${this.verb.charAt(0).toUpperCase() + this.verb.slice(1)} the whole sub-tree (commit; rerun with --yes after previewing)`,\n command: `mu task ${this.verb} ${this.taskId} --cascade --yes`,\n },\n {\n intent: \"Drop the blocking edge from a dependent first\",\n command: `mu task unblock <dep> --by ${this.taskId}`,\n },\n {\n intent: \"Address dependents individually first\",\n command: `mu task ${this.verb} <dep>`,\n },\n ];\n }\n}\n\n/**\n * Thrown when `mu task claim` resolves a claimer agent name (from the\n * pane title or --for) that has no matching row in the agents table.\n *\n * The FK on `tasks.owner` references `agents.name`; without this guard\n * the claim attempt would fail with the unhelpful 'FOREIGN KEY constraint\n * failed' from SQLite. This typed error gives the user actionable next\n * steps (run `mu adopt <pane-id>` to register, or use --for to pick a\n * different agent).\n *\n * Maps to exit code 4 (conflict) via the cli.ts handler.\n */\nexport class ClaimerNotRegisteredError extends Error implements HasNextSteps {\n override readonly name = \"ClaimerNotRegisteredError\";\n constructor(\n public readonly agentName: string,\n public readonly paneId: string | null,\n ) {\n const paneHint = paneId !== null ? ` (pane ${paneId})` : \"\";\n super(\n `claimer '${agentName}'${paneHint} is not a registered mu agent (no row in agents table)`,\n );\n }\n\n /**\n * Three actionable resolutions in expected-frequency order:\n * 1. --self : orchestrator pattern (working directly)\n * 2. --for : dispatcher pattern (assigning to a worker)\n * 3. mu adopt: registration pattern (promote pane to worker)\n */\n errorNextSteps(): NextStep[] {\n const steps: NextStep[] = [\n { intent: \"Work directly (anonymous)\", command: \"mu task claim <id> --self\" },\n { intent: \"Dispatch to a worker\", command: \"mu task claim <id> --for <worker>\" },\n ];\n steps.push(\n this.paneId !== null\n ? { intent: \"Register this pane\", command: `mu adopt ${this.paneId}` }\n : {\n intent: \"Register a pane\",\n command: \"mu adopt <pane-id> (must be in mu-<workstream> tmux session)\",\n },\n );\n return steps;\n }\n}\n\nexport class CycleError extends Error implements HasNextSteps {\n override readonly name = \"CycleError\";\n constructor(\n public readonly from: string,\n public readonly to: string,\n ) {\n super(`adding edge ${from} -> ${to} would create a cycle`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Show the dependency tree\",\n command: `mu task tree ${this.to} --down`,\n },\n {\n intent: \"Show the prereq tree (what blocks the from-task)\",\n command: `mu task tree ${this.from}`,\n },\n {\n intent: \"Remove an edge in the path to break the cycle\",\n command: \"mu task unblock <blocked> --by <blocker>\",\n },\n ];\n }\n}\n\n/**\n * Thrown by the `mu task wait` CLI wrapper when the per-poll\n * reconciler detects that a watched task transitioned\n * `IN_PROGRESS → OPEN` between polls (the reaper saw the owner's\n * pane was gone and flipped the task back). With `--status CLOSED`\n * (the default) the wait can never satisfy by progress — the worker\n * is dead — so we abort fast instead of running out the operator's\n * `--timeout`.\n *\n * Maps to exit code 6 (REAPER_DETECTED) via the cli.ts handler. The\n * suppression rule (only fire when target=CLOSED) lives in the\n * caller; this error type is a pure data carrier.\n *\n * Surfaced live by task_wait_reconcile_dead_panes (twice in one\n * v0.3 dispatch wave: tmux restart killed worker panes; `mu task\n * wait --timeout 1800` blocked silently for 25 min instead of\n * failing in seconds).\n */\nexport class ReaperDetectedDuringWaitError extends Error implements HasNextSteps {\n override readonly name = \"ReaperDetectedDuringWaitError\";\n constructor(\n public readonly taskId: string,\n public readonly previousOwner: string | null,\n public readonly workstream: string,\n ) {\n const ownerBit = previousOwner !== null ? `owner=${previousOwner}` : \"owner=<unknown>\";\n super(\n `task ${taskId} was IN_PROGRESS ${ownerBit} until just now; reaper detected dead pane and flipped to OPEN. wait abandoned. Re-dispatch a worker and retry.`,\n );\n }\n errorNextSteps(): NextStep[] {\n const ws = this.workstream;\n return [\n {\n intent: \"Inspect the task's current state\",\n command: `mu task show ${this.taskId} -w ${ws}`,\n },\n {\n intent: \"List live agents in the workstream (post-reap)\",\n command: `mu agent list -w ${ws}`,\n },\n {\n intent: \"Re-dispatch a fresh worker, then re-run the wait\",\n command: `mu agent spawn <name> -w ${ws} && mu task claim ${this.taskId} --for <name> -w ${ws}`,\n },\n ];\n }\n}\n\n/**\n * Thrown by the `mu task wait` CLI wrapper when `--on-stall exit` is\n * in effect and the existing `--stuck-after` predicate fires on a\n * watched task — the task is IN_PROGRESS, owned by a registered\n * agent whose detected status is `needs_input` for `>= stuckAfterMs`.\n *\n * Pairs with `ReaperDetectedDuringWaitError` (exit 6, dead pane).\n * Stall is the AMBIGUOUS sibling: the worker is alive but not\n * progressing — the operator decides whether it's transient (poke +\n * retry) or terminal (release + reopen). Exit code 7 = STALL_DETECTED\n * via classifyError, distinct from 6 so consumer scripts can branch.\n *\n * Carve-out (lives at the call site, not here): only fires when the\n * wait target is CLOSED — same logic as exit-6's reaper-flip\n * suppression. With `--status OPEN`/etc the worker reaching\n * needs_input might BE the success path.\n *\n * Surfaced by task_wait_stall_action_flag (the warn-only behaviour\n * pre-dates this; the typed-throw path is the new escape hatch for\n * unattended orchestrators).\n */\nexport class StallDetectedDuringWaitError extends Error implements HasNextSteps {\n override readonly name = \"StallDetectedDuringWaitError\";\n constructor(\n public readonly taskName: string,\n public readonly owner: string | null,\n public readonly workstream: string,\n public readonly ageSecs: number,\n ) {\n const ownerBit = owner !== null ? owner : \"<unknown>\";\n super(\n `task ${taskName} owned by ${ownerBit} has been needs_input for ${ageSecs}s; exiting per --on-stall exit. Re-dispatch a worker or send a poke (mu agent send ${ownerBit} \"...\") and re-run wait.`,\n );\n }\n errorNextSteps(): NextStep[] {\n const ws = this.workstream;\n const ownerBit = this.owner !== null ? this.owner : \"<owner>\";\n return [\n {\n intent: \"Poke the worker (often unblocks a transient stall)\",\n command: `mu agent send ${ownerBit} '<retry-instruction>' -w ${ws}`,\n },\n {\n intent: \"Inspect the worker's recent scrollback\",\n command: `mu agent show ${ownerBit} -w ${ws} -n 60`,\n },\n {\n intent: \"Release the task back to OPEN (declare the stall terminal)\",\n command: `mu task release ${this.taskName} --reopen -w ${ws}`,\n },\n {\n intent: \"Inspect the task's current state\",\n command: `mu task show ${this.taskName} -w ${ws}`,\n },\n ];\n }\n}\n\nexport class CrossWorkstreamEdgeError extends Error implements HasNextSteps {\n override readonly name = \"CrossWorkstreamEdgeError\";\n constructor(\n public readonly blocker: string,\n public readonly blockerWorkstream: string,\n public readonly dependent: string,\n public readonly dependentWorkstream: string,\n ) {\n super(\n `cross-workstream edge: blocker '${blocker}' is in workstream '${blockerWorkstream}', dependent '${dependent}' is in workstream '${dependentWorkstream}'`,\n );\n }\n errorNextSteps(): NextStep[] {\n // schema v5+: tasks.workstream_id is an INTEGER FK to\n // workstreams.id (no tasks.workstream column), and (workstream_id,\n // local_id) is the per-workstream unique key — so the move-blocker\n // recipe must scope by BOTH the source workstream's id AND set the\n // destination workstream's id via subselects. The v4-shaped\n // `UPDATE tasks SET workstream='…' WHERE local_id='…'` recipe we\n // used to print here errored at runtime (\"no such column:\n // workstream\") and was also ambiguous across workstreams.\n //\n // We also dropped the \"rename one workstream to the other\" hint:\n // it silently moves *every* task in the source workstream and\n // fails outright when the destination name already exists\n // (UNIQUE violation). Operators almost always want to move just\n // the blocker — or duplicate it — not merge whole workstreams.\n return [\n {\n intent: \"Move the blocker into the dependent's workstream\",\n command: `mu sql \"UPDATE tasks SET workstream_id=(SELECT id FROM workstreams WHERE name='${this.dependentWorkstream}') WHERE local_id='${this.blocker}' AND workstream_id=(SELECT id FROM workstreams WHERE name='${this.blockerWorkstream}')\"`,\n },\n {\n intent: \"Or duplicate the blocker (typed verb deferred)\",\n command: `mu task add <new-id> -w ${this.dependentWorkstream} --title \"<copy of ${this.blocker}>\" --impact <n> --effort-days <n>`,\n },\n ];\n }\n}\n","// mu — workstream-level operations.\n//\n// One workstream = one tmux session + N agents + M tasks (and their\n// edges/notes) all sharing the workstream column. 0.1.0 ships `mu init`\n// (create the tmux session) and `mu destroy` (this module: nuke the\n// tmux session and every DB row tagged with the workstream name).\n//\n// `destroyWorkstream` is idempotent on every leg:\n// - tmux session already gone → killSession swallows the error\n// - no agents/tasks for this name → DELETE returns zero changes\n// - workstream never existed at all → returns all-zero counts\n//\n// Both summarize and destroy take an optional `tmuxSession` override so\n// tests (and the rare workstream whose tmux session was created with a\n// non-default name) work without env-var gymnastics.\n\nimport { existsSync, readdirSync, rmdirSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { type Db, defaultStateDir } from \"./db.js\";\nimport {\n type ExportManifest,\n type ExportSourceManifest,\n exportSourceForWorkstream,\n renderToBucket,\n} from \"./exporting.js\";\nimport { emitEvent } from \"./logs.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\nimport { captureSnapshot } from \"./snapshots.js\";\nimport { killSession, listSessions, sessionExists } from \"./tmux.js\";\nimport { type VcsBackend, type VcsBackendName, backendByName } from \"./vcs.js\";\nimport { listWorkspaces } from \"./workspace.js\";\n\n/**\n * Allowed workstream-name shape: lowercase alpha first, then alnum,\n * underscore, or hyphen, up to 32 chars total. Mirrors the agent-name\n * rule in VOCABULARY.md §\"Naming conventions\".\n *\n * Critically, this rule excludes `.` and `:` — tmux silently rewrites\n * `.` to `_` in session names (because `.` is the window/pane separator\n * in tmux's `session:window.pane` target syntax) and `:` is reserved\n * outright. A workstream name with `.` would create a session that mu\n * couldn't subsequently look up, breaking every downstream verb. We\n * fail loud at init time instead.\n */\nconst WORKSTREAM_NAME_RE = /^[a-z][a-z0-9_-]{0,31}$/;\n\n/** Reserved prefix — mu auto-prepends `mu-` to derive the tmux session\n * name (so workstream `auth` lives in tmux session `mu-auth`). A\n * workstream named `mu-auth` would produce session `mu-mu-auth`,\n * which the user almost certainly didn't intend. Fail loud rather\n * than silently double-prefix. */\nexport const RESERVED_WORKSTREAM_PREFIX = \"mu-\";\n\nexport function isValidWorkstreamName(name: string): boolean {\n if (!WORKSTREAM_NAME_RE.test(name)) return false;\n if (name.startsWith(RESERVED_WORKSTREAM_PREFIX)) return false;\n return true;\n}\n\n/** Thrown by `ensureWorkstream` and `mu workstream init` when the name\n * doesn't match the rules. */\nexport class WorkstreamNameInvalidError extends Error implements HasNextSteps {\n override readonly name = \"WorkstreamNameInvalidError\";\n constructor(public readonly attempted: string) {\n const reason = attempted.startsWith(RESERVED_WORKSTREAM_PREFIX)\n ? `the 'mu-' prefix is reserved (mu auto-prepends 'mu-' to derive the tmux session name; '${attempted}' would produce session 'mu-${attempted}', which is double-prefixed and almost never what you want). Drop the 'mu-' from the workstream name.`\n : `must match /^[a-z][a-z0-9_-]{0,31}$/. tmux silently rewrites '.' to '_' and reserves ':' as a target separator, so workstream names containing those characters would create tmux sessions mu couldn't look up afterwards. Use letters, digits, '_', and '-' only.`;\n super(`invalid workstream name ${JSON.stringify(attempted)}: ${reason}`);\n }\n errorNextSteps(): NextStep[] {\n // Suggest a sanitized form: strip the mu- prefix; replace dots and\n // colons with underscores; lowercase.\n const sanitized = this.attempted\n .toLowerCase()\n .replace(/^mu-/, \"\")\n .replace(/[.:]/g, \"_\")\n .slice(0, 32);\n // Branch the intent label on the failure class. For the mu-prefix\n // case the correction is unambiguous (drop the prefix), so phrase\n // the next-step as a direct action — \"Try a … (best guess)\" reads\n // as a hedge and dogfooding showed agents skip past the rationale\n // line entirely (workstream_init_name_rejected_mu in feedback ws).\n // For the regex/mangle branch the sanitiser really is guessing\n // (`.`/`:`/case all collapse), so the hedge stays honest there.\n const isPrefixCase = this.attempted.toLowerCase().startsWith(RESERVED_WORKSTREAM_PREFIX);\n const intent = isPrefixCase\n ? \"Retry without the 'mu-' prefix\"\n : \"Try a sanitized name (best guess)\";\n return [\n { intent, command: `mu workstream init ${sanitized || \"<name>\"}` },\n { intent: \"List existing workstreams\", command: \"mu workstream list\" },\n ];\n }\n}\n\nfunction assertValidWorkstreamName(name: string): void {\n if (!isValidWorkstreamName(name)) throw new WorkstreamNameInvalidError(name);\n}\n\n/**\n * Ensure a row exists in the `workstreams` table for `name`. Idempotent;\n * INSERT OR IGNORE so concurrent callers race safely. Called by\n * `insertAgent` and `addTask` so callers don't need to remember to call\n * `mu init` before adding a task / spawning an agent (preserves the\n * spawn-without-init ergonomics now that agents.workstream and\n * tasks.workstream are real FKs into this table).\n *\n * Validates the name before inserting; throws `WorkstreamNameInvalidError`\n * for names tmux would silently mangle (containing '.' or ':') or that\n * exceed 32 chars / start with a non-letter.\n *\n * Returns true iff a row was actually inserted (vs. already present).\n */\nexport function ensureWorkstream(db: Db, name: string): boolean {\n assertValidWorkstreamName(name);\n const result = db\n .prepare(\"INSERT OR IGNORE INTO workstreams (name, created_at) VALUES (?, ?)\")\n .run(name, new Date().toISOString());\n const created = result.changes > 0;\n if (created) emitEvent(db, name, `workstream init ${name}`);\n return created;\n}\n\nexport interface WorkstreamSummary {\n /** The workstream's own name. */\n name: string;\n /** Tmux session name, defaults to `mu-<name>`. */\n tmuxSession: string;\n /** True iff `tmux has-session -t <tmuxSession>` succeeds right now. */\n tmuxAlive: boolean;\n /** Rows in `agents` for this workstream. */\n agentCount: number;\n /** Rows in `tasks` for this workstream. */\n taskCount: number;\n /** Rows in `task_notes` whose task is in this workstream. */\n noteCount: number;\n /** Rows in `task_edges` whose `from_task` is in this workstream. */\n edgeCount: number;\n /** Rows in `vcs_workspaces` for this workstream. Surfaced so the\n * destroy dry-run can warn about per-agent worktrees that need\n * cleanup before the FK cascade silently nukes their rows. */\n workspaceCount: number;\n /** True iff a row exists in the `workstreams` table itself. False\n * for tmux-only `mu-*` sessions that mu never observed via\n * `mu workstream init`. Surfaced so destroy can clean up bare\n * registry rows (workstream row exists, no agents/tasks/etc.) —\n * otherwise such rows are orphaned forever (the previous\n * `nothingToDo` heuristic short-circuited on them). */\n registered: boolean;\n}\n\nexport interface DestroyResult {\n /** True iff `tmux kill-session` actually killed something. */\n killedTmux: boolean;\n /** Number of `agents` rows deleted. */\n deletedAgents: number;\n /** Number of `tasks` rows deleted (edges/notes cascade via FK). */\n deletedTasks: number;\n /** Number of `task_notes` deleted by the cascade — informational. */\n deletedNotes: number;\n /** Number of `task_edges` deleted by the cascade — informational. */\n deletedEdges: number;\n /** Number of vcs_workspaces whose on-disk path was actually\n * removed by the backend on this destroy. Excludes\n * `alreadyGoneWorkspaces` (those were no-ops on disk). */\n freedWorkspaces: number;\n /** Number of vcs_workspaces whose registry row existed but\n * whose on-disk path was already gone (manual rm -rf or a prior\n * interrupted destroy). The DB row was cascade-deleted; the\n * backend did no filesystem work. Tracked separately so the\n * destroy report doesn't lie about how much cleanup it actually\n * performed. */\n alreadyGoneWorkspaces: number;\n /** Workspaces whose backend cleanup failed (e.g. `git worktree\n * remove` refused because of uncommitted changes). The DB row\n * was still cascade-deleted; the on-disk path remains and needs\n * manual cleanup. */\n failedWorkspaces: WorkspaceFailure[];\n}\n\nexport interface WorkspaceFailure {\n agent: string;\n backend: string;\n path: string;\n error: string;\n}\n\nexport interface WorkstreamOptions {\n workstream: string;\n /** Override the tmux session name. Defaults to `mu-<workstream>`. */\n tmuxSession?: string;\n /** Override the per-name VcsBackend resolver. Defaults to\n * `backendByName`. Lets tests inject a fake backend (e.g. one whose\n * `freeWorkspace` throws) without mutating the exported singletons —\n * same pattern as `createWorkspace`'s `opts.backend` accepting a\n * pre-built `VcsBackend` object. Production callers leave this\n * unset. */\n resolveBackend?: (name: VcsBackendName) => VcsBackend;\n}\n\n/**\n * Discover every workstream visible on this machine. The union of:\n * - rows in the `workstreams` table (canonical DB source; populated by\n * `mu init` and auto-created by insertAgent / addTask)\n * - tmux sessions named `mu-*` (with the prefix stripped) — catches\n * externally-created `tmux new-session -s mu-foo` that mu hasn't\n * observed yet\n *\n * Returns one `WorkstreamSummary` per workstream, sorted by name.\n * Useful as a pre-flight before `mu init` (\"is this name taken?\") and\n * for `mu doctor`-style diagnostics.\n */\nexport async function listWorkstreams(db: Db): Promise<WorkstreamSummary[]> {\n const dbNames = new Set<string>(\n (db.prepare(\"SELECT name FROM workstreams\").all() as { name: string }[]).map((r) => r.name),\n );\n\n const tmuxNames = new Set<string>();\n for (const session of await listSessions()) {\n if (session.name.startsWith(RESERVED_WORKSTREAM_PREFIX))\n tmuxNames.add(session.name.slice(RESERVED_WORKSTREAM_PREFIX.length));\n }\n\n const allNames = Array.from(new Set([...dbNames, ...tmuxNames])).sort();\n return Promise.all(allNames.map((name) => summarizeWorkstream(db, { workstream: name })));\n}\n\n/**\n * Discover every workstream that has no user-meaningful state\n * attached. Two flavours unioned:\n *\n * 1. REGISTERED-empty: a row in `workstreams` with zero tasks,\n * zero agents, zero vcs_workspaces. Tmux\n * session presence and agent_logs entries do NOT disqualify\n * — the session itself was created at init time and contains\n * no agent panes; the events are audit, not state.\n *\n * 2. TMUX-only: a tmux session named `mu-*` with no row in the\n * `workstreams` table. Catches test litter and remnants of a\n * partial destroy where the DB row was wiped but the tmux\n * session survived (or sessions created out-of-band via\n * `tmux new-session -s mu-foo`). The synthetic summary has\n * `registered=false`, all counts 0, and `tmuxAlive=true` (it\n * wouldn't have been surfaced otherwise).\n *\n * The predicate is intentionally narrow on the prefix: only\n * `mu-*` sessions are eligible. Arbitrary tmux sessions the\n * operator created for unrelated work are NEVER matched — mu only\n * owns its own namespace.\n *\n * Used by `mu workstream destroy --empty` to sweep test-litter\n * workstreams in one command (instead of the per-name jq incantation\n * over `mu workstream list --json`).\n *\n * Returns one `WorkstreamSummary` per match, sorted by name (with\n * defensive dedup — a registered-empty and a tmux-only of the same\n * name can't both arise from the same call by construction, but\n * belt-and-braces).\n */\nexport async function listEmptyWorkstreams(db: Db): Promise<WorkstreamSummary[]> {\n const registeredRows = db\n .prepare(\n `SELECT ws.name AS name\n FROM workstreams ws\n LEFT JOIN tasks t ON t.workstream_id = ws.id\n LEFT JOIN agents a ON a.workstream_id = ws.id\n LEFT JOIN vcs_workspaces v ON v.workstream_id = ws.id\n GROUP BY ws.id, ws.name\n HAVING COUNT(DISTINCT t.id) = 0\n AND COUNT(DISTINCT a.id) = 0\n AND COUNT(DISTINCT v.id) = 0\n ORDER BY ws.name`,\n )\n .all() as { name: string }[];\n const registeredEmpty = await Promise.all(\n registeredRows.map((r) => summarizeWorkstream(db, { workstream: r.name })),\n );\n\n // Tmux-only mu-* sessions: enumerate every running tmux session,\n // keep the ones with the `mu-` prefix (strip it to get the\n // would-be workstream name), then subtract names already in the\n // `workstreams` table. The mirror of listWorkstreams above; see\n // its comment for the prefix rationale.\n const dbNames = new Set<string>(\n (db.prepare(\"SELECT name FROM workstreams\").all() as { name: string }[]).map((r) => r.name),\n );\n const tmuxOnlyNames: string[] = [];\n for (const session of await listSessions()) {\n if (!session.name.startsWith(\"mu-\")) continue;\n const name = session.name.slice(RESERVED_WORKSTREAM_PREFIX.length);\n if (dbNames.has(name)) continue;\n tmuxOnlyNames.push(name);\n }\n const tmuxOnly = await Promise.all(\n tmuxOnlyNames.map((name) => summarizeWorkstream(db, { workstream: name })),\n );\n\n // Compose + sort + dedup-by-name (defensive; no overlap is possible\n // by construction since tmuxOnlyNames excludes every dbName).\n const seen = new Set<string>();\n const all: WorkstreamSummary[] = [];\n for (const ws of [...registeredEmpty, ...tmuxOnly]) {\n if (seen.has(ws.name)) continue;\n seen.add(ws.name);\n all.push(ws);\n }\n all.sort((a, b) => a.name.localeCompare(b.name));\n return all;\n}\n\nexport async function summarizeWorkstream(\n db: Db,\n opts: WorkstreamOptions,\n): Promise<WorkstreamSummary> {\n const tmuxSession = opts.tmuxSession ?? `mu-${opts.workstream}`;\n return {\n name: opts.workstream,\n tmuxSession,\n tmuxAlive: await sessionExists(tmuxSession),\n agentCount: countAgents(db, opts.workstream),\n taskCount: countTasks(db, opts.workstream),\n noteCount: countNotes(db, opts.workstream),\n edgeCount: countEdges(db, opts.workstream),\n workspaceCount: listWorkspaces(db, opts.workstream).length,\n registered: isRegistered(db, opts.workstream),\n };\n}\n\nfunction isRegistered(db: Db, workstream: string): boolean {\n const row = db.prepare(\"SELECT 1 AS x FROM workstreams WHERE name = ?\").get(workstream) as\n | { x: number }\n | undefined;\n return row !== undefined;\n}\n\n/**\n * Tear down a workstream: kill its tmux session and delete every DB row\n * tagged with its name. Cascades on `tasks` clean up `task_edges` and\n * `task_notes` automatically (FK ON DELETE CASCADE in the schema).\n *\n * Idempotent: safe to call against a workstream that never existed; safe\n * to call repeatedly. Returns counts so the caller can print a useful\n * summary.\n */\nexport async function destroyWorkstream(db: Db, opts: WorkstreamOptions): Promise<DestroyResult> {\n const tmuxSession = opts.tmuxSession ?? `mu-${opts.workstream}`;\n\n // Pre-mutation snapshot (snap_design §EDGE CASES > WORKSTREAM\n // DESTROY). workstream=null because workstream-destroy snapshots\n // logically span every workstream in the DB (whole-DB backup;\n // anchoring to one name would lie about scope). If the snapshot\n // throws (disk full, perms), abort the destroy — better to refuse\n // than to delete irrecoverably.\n captureSnapshot(db, `workstream destroy ${opts.workstream}`, null);\n\n // Pre-count the cascade victims so we can report them — SQLite's\n // changes() only reports rows directly affected by the last statement,\n // not cascade victims.\n const agentsBefore = countAgents(db, opts.workstream);\n const tasksBefore = countTasks(db, opts.workstream);\n const notesBefore = countNotes(db, opts.workstream);\n const edgesBefore = countEdges(db, opts.workstream);\n const workspacesBefore = listWorkspaces(db, opts.workstream);\n\n // Tmux first: if killSession throws we don't want the DB rows already\n // gone with no way to recover. (killSession is itself idempotent on\n // missing sessions — a real throw here is an unexpected tmux error.)\n const tmuxAliveBefore = await sessionExists(tmuxSession);\n if (tmuxAliveBefore) {\n await killSession(tmuxSession);\n }\n\n // Workspaces SECOND, before the FK cascade. The cascade silently\n // deletes vcs_workspaces rows but leaves the on-disk worktrees\n // (and the git worktree registry entries) behind — the bug from\n // mufeedback note #195. Per backend, the right cleanup is\n // 'git worktree remove --force' / 'jj workspace forget' / etc.,\n // not 'rm -rf'. We surface failures so the user can recover; we\n // do NOT abort the destroy on workspace failure (the workstream\n // semantics are 'tear it all down', not 'partial cleanup').\n let freedWorkspaces = 0;\n let alreadyGoneWorkspaces = 0;\n const failedWorkspaces: WorkspaceFailure[] = [];\n const resolveBackend = opts.resolveBackend ?? backendByName;\n for (const ws of workspacesBefore) {\n try {\n const backend = resolveBackend(ws.backend);\n const result = await backend.freeWorkspace({\n workspacePath: ws.path,\n commit: false,\n });\n if (result.removed) {\n // Backend actually removed the on-disk path. This is the\n // only case that counts as 'work done by destroy'.\n freedWorkspaces += 1;\n } else {\n // Path was already gone (manual rm -rf or interrupted prior\n // destroy). The DB row is cascade-deleted below either way,\n // but we don't claim to have freed anything on disk — it was\n // already in the desired state. Tracked separately so the\n // user can spot stale registry rows from past mishaps.\n alreadyGoneWorkspaces += 1;\n }\n } catch (err) {\n failedWorkspaces.push({\n agent: ws.agentName,\n backend: ws.backend,\n path: ws.path,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n // After every per-agent worktree is freed, the parent\n // <state>/workspaces/<workstream>/ directory is empty — reap it\n // too. Best-effort: rmdir refuses if non-empty (e.g. backend\n // removal failed and left files behind), which is the right\n // outcome (don't silently rm -rf user data). Skipped if the\n // parent doesn't exist (workstream never had any workspaces).\n const parentDir = join(defaultStateDir(), \"workspaces\", opts.workstream);\n if (existsSync(parentDir)) {\n try {\n if (readdirSync(parentDir).length === 0) rmdirSync(parentDir);\n } catch {\n // Non-empty or otherwise unreapable. The failed-workspaces\n // list above already tells the user what to clean.\n }\n }\n\n // One DELETE: the FK CASCADE chain (workstreams → agents,\n // workstreams → tasks → task_edges + task_notes, workstreams →\n // agent_logs, workstreams → vcs_workspaces) cleans every row in\n // one shot, atomically. If the workstream was never registered\n // (e.g. an orphan tmux session that mu never observed),\n // changes() = 0 and we still report the killed tmux session\n // honestly.\n const result = db.prepare(\"DELETE FROM workstreams WHERE name = ?\").run(opts.workstream);\n // The destroy event itself goes to workstream=null (machine-wide)\n // because the FK CASCADE we just triggered would otherwise wipe\n // it on the same statement. Visible via `mu log --all`.\n if (result.changes > 0 || tmuxAliveBefore) {\n emitEvent(\n db,\n null,\n `workstream destroy ${opts.workstream} (agents=${agentsBefore}, tasks=${tasksBefore}, edges=${edgesBefore}, notes=${notesBefore}, workspaces=${freedWorkspaces}/${workspacesBefore.length}, already_gone=${alreadyGoneWorkspaces}, tmux=${tmuxAliveBefore})`,\n );\n }\n\n return {\n killedTmux: tmuxAliveBefore,\n deletedAgents: agentsBefore,\n deletedTasks: tasksBefore,\n deletedNotes: notesBefore,\n deletedEdges: edgesBefore,\n freedWorkspaces,\n alreadyGoneWorkspaces,\n failedWorkspaces,\n };\n}\n\n// ─── Counts ────────────────────────────────────────────────────────────\n\nfunction countAgents(db: Db, workstream: string): number {\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n FROM agents a\n JOIN workstreams ws ON ws.id = a.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number };\n return row.n;\n}\n\nfunction countTasks(db: Db, workstream: string): number {\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number };\n return row.n;\n}\n\nfunction countNotes(db: Db, workstream: string): number {\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n\n FROM task_notes n\n JOIN tasks t ON t.id = n.task_id\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number };\n return row.n;\n}\n\nfunction countEdges(db: Db, workstream: string): number {\n // Count edges whose blocker (from_task) is in the workstream. Since\n // cross-workstream edges are forbidden by addTask, this equals the\n // edge count for the workstream subgraph.\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n\n FROM task_edges e\n JOIN tasks t ON t.id = e.from_task_id\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number };\n return row.n;\n}\n\n// ─── exportWorkstream ──────────────────────────────────────────────────\n//\n// Thin sugar over `renderToBucket` (src/exporting.ts): one live\n// workstream → one ExportSource → bucket render. The renderer holds\n// every byte of layout knowledge; this wrapper just adapts the SDK\n// reads (listTasks / getTaskEdges / listNotes / latestSeq) and\n// emits the workstream-flavoured event.\n//\n// The on-disk shape is the v0.3 BUCKET layout (see src/exporting.ts):\n//\n// <outDir>/\n// README.md / INDEX.md / manifest.json # bucket-level\n// <workstream>/\n// README.md / INDEX.md / tasks/<id>.md # per-source-ws\n//\n// Re-export against the same outDir is additive: a different `-w`\n// adds a sibling subdir without touching the existing one. A re-run\n// with the same `-w` refreshes that subdir (sha256 short-circuit).\n// Pointing this at a directory built by mu < 0.3 throws\n// `LegacyExportLayoutError`.\n//\n// Anti-features (preserved from the originating design note):\n// - re-import: out of scope\n// - HTML/PDF: markdown-only\n// - embedded VCS: caller can `git init && git add . && git commit`\n// - cross-workstream merge: source-ws subdirs stay separate\n\nexport interface ExportWorkstreamOptions {\n workstream: string;\n /** Output directory (the bucket). Defaults to `./<workstream>/`\n * in the cwd — i.e. the bucket and its single source-ws subdir\n * share a name. */\n outDir?: string;\n}\n\nexport interface ExportResult {\n outDir: string;\n /** Per-task files rewritten this call. */\n written: number;\n /** Per-task files sha256-skipped this call. */\n unchanged: number;\n /** Tasks present in a prior manifest that are no longer in the DB.\n * Their .md stays on disk; a banner is added once. */\n preserved: number;\n manifestPath: string;\n manifest: ExportManifest;\n /** Per-source-ws manifest entry for this workstream — convenience\n * for callers who only want one source's view. */\n source: ExportSourceManifest;\n}\n\n/**\n * Export one live workstream to a bucket directory. Idempotent +\n * additive: re-exporting the same workstream is sha256-skipped,\n * exporting a different workstream into the same bucket appends a\n * sibling subdir.\n *\n * Throws:\n * - `LegacyExportLayoutError` if `outDir` already contains a\n * pre-0.3 (single-source) manifest.json.\n */\nexport function exportWorkstream(db: Db, opts: ExportWorkstreamOptions): ExportResult {\n const outDir = resolve(opts.outDir ?? join(process.cwd(), opts.workstream));\n const source = exportSourceForWorkstream(db, opts.workstream);\n const result = renderToBucket({\n sources: [source],\n bucketLabel: null,\n outDir,\n });\n const sourceManifest = result.manifest.sources[opts.workstream];\n if (!sourceManifest) {\n // Defensive: renderToBucket always inserts a manifest entry per\n // source it received. If this ever fires the renderer regressed.\n throw new Error(\n `exportWorkstream: renderer did not write a manifest entry for ${opts.workstream}`,\n );\n }\n emitEvent(\n db,\n opts.workstream,\n `workstream export ${opts.workstream} (out=${result.outDir}, tasks=${source.tasks.length}, written=${result.written}, unchanged=${result.unchanged}, preserved=${result.preserved})`,\n );\n return {\n outDir: result.outDir,\n written: result.written,\n unchanged: result.unchanged,\n preserved: result.preserved,\n manifestPath: result.manifestPath,\n manifest: result.manifest,\n source: sourceManifest,\n };\n}\n","// mu — unified bucket renderer for workstream / archive exports.\n//\n// One renderer, two entry points (`mu workstream export` and\n// `mu archive export`). Both produce the same on-disk shape: a\n// \"bucket\" directory whose top-level contains a bucket-wide README +\n// INDEX + manifest, and one subdirectory per source workstream that\n// holds the per-workstream README + INDEX + tasks/<id>.md files.\n//\n// The bucket layout is ADDITIVE: re-running `mu workstream export\n// -w X --out <bucket>` over an existing bucket either appends a new\n// source-ws subdirectory (if X wasn't there before) or refreshes the\n// existing subdirectory's contents in place (sha256 short-circuit).\n// Source-ws subdirectories from earlier exports are NEVER touched\n// by an unrelated source-ws's re-export.\n//\n// Disk shape (`bucketVersion: 2`):\n//\n// <bucket>/\n// README.md # bucket-level summary (every source-ws + dates + totals)\n// INDEX.md # union of all task tables; first column = source-ws\n// manifest.json # bucketVersion: 2 + per-source-ws sha256 + per-task sha256\n// <source-ws>/\n// README.md # per-source-ws (counts)\n// INDEX.md # per-source-ws (table of every task)\n// tasks/<id>.md # one .md per task; YAML frontmatter + notes\n//\n// Origin: this code was lifted out of `src/workstream.ts`'s\n// `exportWorkstream` (single-source rendering) and generalised to N\n// sources. The single-source case is preserved as a thin wrapper\n// (see exportWorkstream in src/workstream.ts) that builds a one-\n// element `sources` array and delegates here.\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { listArchivedTasks } from \"./archives.js\";\nimport type { Db } from \"./db.js\";\nimport { emitEvent, latestSeq } from \"./logs.js\";\nimport { getTaskEdges, listNotes, listTasks } from \"./tasks.js\";\nimport type { TaskNoteRow, TaskRow } from \"./tasks.js\";\n\n// ─── Types ───────────────────────────────────────────────────────────\n\n/** One per-task summary inside a per-source-ws section of the manifest. */\nexport interface ExportTaskEntry {\n /** Task local_id == filename stem (`<id>.md`). */\n id: string;\n /** Path relative to the bucket root (e.g. `auth/tasks/design.md`). */\n path: string;\n /** sha256 of the markdown body bytes; idempotency key. */\n sha256: string;\n /** ISO timestamp of the first observed export at which the task\n * was missing from the source. Absent for tasks still present. */\n deletedAt?: string;\n}\n\n/** Per-source-ws entry under `manifest.sources`. */\nexport interface ExportSourceManifest {\n /** ISO timestamp the source was first added to the bucket. */\n addedAt: string;\n /** ISO timestamp of the most recent re-export of this source. */\n lastReExportedAt: string;\n /** `latestSeq(db)` at the most recent re-export; for live workstreams\n * this is the live `agent_logs.seq` cursor. For archive sources\n * there is no equivalent live counter — we record the seq at\n * archive-add time when available, else 0. */\n eventsSeqAtExport: number;\n /** Per-task entries; sorted by id for stable diffs. */\n tasks: ExportTaskEntry[];\n}\n\n/** Top-level bucket manifest. `bucketVersion: 2` — the v0.3 shape.\n * v1 (bucketVersion absent + top-level `workstream` field) is the\n * legacy single-source shape and is rejected at write time. */\nexport interface ExportManifest {\n /** Schema discriminator. Always 2 in this codebase. */\n bucketVersion: 2;\n /** Operator-chosen bucket label (an archive label, or null for a\n * one-shot `mu workstream export`). Surfaced in README only. */\n bucketLabel: string | null;\n bucketCreatedAt: string;\n bucketLastUpdatedAt: string;\n muVersion: string;\n /** Per-source-ws map; key is the source workstream's TEXT name. */\n sources: Record<string, ExportSourceManifest>;\n}\n\n/** One source's worth of input: the per-task data the renderer needs.\n * Both entry points (workstream / archive) collapse to this shape. */\nexport interface ExportSource {\n /** Source workstream name. Becomes the subdirectory name. */\n name: string;\n tasks: TaskRow[];\n /** Per-task edges keyed on task name. Missing keys → no edges. */\n edges: Map<string, { blockers: string[]; dependents: string[] }>;\n /** Per-task notes keyed on task name. Missing keys → no notes. */\n notes: Map<string, TaskNoteRow[]>;\n /** `agent_logs.seq` cursor at this source's snapshot moment. 0 for\n * archive sources (no live cursor). */\n eventsSeqAtExport: number;\n}\n\nexport interface RenderBucketInput {\n sources: ExportSource[];\n /** Operator-chosen archive label, or null for a workstream export. */\n bucketLabel: string | null;\n outDir: string;\n}\n\nexport interface RenderBucketResult {\n outDir: string;\n /** Per-source-ws stat: how many task files were rewritten across\n * every source in this call. */\n written: number;\n /** Per-source-ws stat: how many task files were sha256-skipped. */\n unchanged: number;\n /** Per-source-ws stat: how many task files exist for a task that\n * has since vanished from the source. Banner is added once. */\n preserved: number;\n manifestPath: string;\n manifest: ExportManifest;\n}\n\n/** Thrown when the operator points an export at a directory whose\n * existing manifest predates bucket layout (v1, single-source). The\n * fix is destructive (remove and re-export) so we refuse to touch\n * it in-place — the legacy directory may be checked into git and\n * the operator should choose between rebuilding it and picking a\n * new --out. */\nexport class LegacyExportLayoutError extends Error {\n override readonly name = \"LegacyExportLayoutError\";\n constructor(public readonly outDir: string) {\n super(\n `${outDir} was created with a pre-bucket export (mu < 0.3); the on-disk shape changed in 0.3 (top-level README/INDEX/manifest + per-source-ws subdirs). Re-export is not in-place; either pick a different --out or 'rm -rf ${outDir}' and re-run.`,\n );\n }\n}\n\n// ─── Markdown render helpers (per-task) ──────────────────────────────\n\n/** Wrap arbitrary text in a fenced code block, choosing a fence\n * longer than any backtick run inside `body` so the body's literal\n * ``` (or ````, etc.) survives intact. Used for note content,\n * which routinely contains markdown / code / triple-fences. */\nexport function fenceForBody(body: string): string {\n const longestRun = (body.match(/`+/g) ?? []).reduce((m, s) => Math.max(m, s.length), 0);\n return \"`\".repeat(Math.max(3, longestRun + 1));\n}\n\n/** YAML-ish scalar quote: always double-quoted, with `\"` and `\\\\`\n * escaped. Multi-line values are coerced to single-line by\n * replacing newlines with ` ` so the frontmatter block stays\n * valid YAML. */\nexport function yamlScalar(value: string): string {\n return `\"${value.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"').replace(/\\n/g, \" \")}\"`;\n}\n\nexport function renderTaskMarkdown(\n task: TaskRow,\n edges: { blockers: string[]; dependents: string[] },\n notes: TaskNoteRow[],\n): string {\n const lines: string[] = [];\n lines.push(\"---\");\n lines.push(`id: ${yamlScalar(task.name)}`);\n lines.push(`workstream: ${yamlScalar(task.workstreamName)}`);\n lines.push(`status: ${task.status}`);\n lines.push(`impact: ${task.impact}`);\n lines.push(`effort_days: ${task.effortDays}`);\n // ROI is derived but a load-bearing field for operators ranking\n // closed tasks in retrospect; emit it precomputed so consumers\n // don't have to re-derive.\n lines.push(`roi: ${(task.impact / task.effortDays).toFixed(2)}`);\n lines.push(`owner: ${task.ownerName === null ? \"null\" : yamlScalar(task.ownerName)}`);\n lines.push(`created_at: ${yamlScalar(task.createdAt)}`);\n lines.push(`updated_at: ${yamlScalar(task.updatedAt)}`);\n lines.push(`blocked_by: [${edges.blockers.map(yamlScalar).join(\", \")}]`);\n lines.push(`blocks: [${edges.dependents.map(yamlScalar).join(\", \")}]`);\n lines.push(\"---\");\n lines.push(\"\");\n lines.push(`# ${task.title}`);\n lines.push(\"\");\n if (notes.length === 0) {\n lines.push(\"_No notes._\");\n lines.push(\"\");\n } else {\n lines.push(`## Notes (${notes.length})`);\n lines.push(\"\");\n for (const [i, note] of notes.entries()) {\n lines.push(`### #${i + 1} by ${note.author ?? \"system\"}, ${note.createdAt}`);\n lines.push(\"\");\n const fence = fenceForBody(note.content);\n lines.push(fence);\n lines.push(note.content);\n lines.push(fence);\n lines.push(\"\");\n }\n }\n // Trailing newline so POSIX tools (and git diff) don't complain.\n return `${lines.join(\"\\n\")}`.replace(/\\n*$/, \"\\n\");\n}\n\n/** Per-source-ws INDEX.md — one row per task in this source. */\nexport function renderSourceIndexMarkdown(workstream: string, tasks: TaskRow[]): string {\n const lines: string[] = [];\n lines.push(`# ${workstream} — task index`);\n lines.push(\"\");\n if (tasks.length === 0) {\n lines.push(\"_No tasks._\");\n lines.push(\"\");\n return lines.join(\"\\n\");\n }\n lines.push(\"| id | status | impact | effort | ROI | title |\");\n lines.push(\"| --- | --- | --- | --- | --- | --- |\");\n for (const t of tasks) {\n const roi = (t.impact / t.effortDays).toFixed(2);\n const title = t.title.replace(/\\|/g, \"\\\\|\");\n lines.push(\n `| [\\`${t.name}\\`](tasks/${t.name}.md) | ${t.status} | ${t.impact} | ${t.effortDays} | ${roi} | ${title} |`,\n );\n }\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\n/** Per-source-ws README.md — counts and pointer to INDEX.md. */\nexport function renderSourceReadmeMarkdown(\n workstream: string,\n tasks: TaskRow[],\n exportedAt: string,\n): string {\n const counts: Record<string, number> = {\n OPEN: 0,\n IN_PROGRESS: 0,\n CLOSED: 0,\n REJECTED: 0,\n DEFERRED: 0,\n };\n for (const t of tasks) counts[t.status] = (counts[t.status] ?? 0) + 1;\n const lines: string[] = [];\n lines.push(`# Source workstream: ${workstream}`);\n lines.push(\"\");\n lines.push(`Exported at: ${exportedAt}`);\n lines.push(\"\");\n lines.push(`- Tasks: ${tasks.length}`);\n for (const status of [\"OPEN\", \"IN_PROGRESS\", \"CLOSED\", \"REJECTED\", \"DEFERRED\"] as const) {\n lines.push(` - ${status}: ${counts[status] ?? 0}`);\n }\n lines.push(\"\");\n lines.push(\"See `INDEX.md` for the task table; one `.md` per task in `tasks/`.\");\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\n/** Bucket-level README.md — multi-source summary. */\nexport function renderBucketReadmeMarkdown(manifest: ExportManifest): string {\n const lines: string[] = [];\n const label = manifest.bucketLabel ?? \"(no label)\";\n lines.push(`# Export bucket: ${label}`);\n lines.push(\"\");\n lines.push(`- Bucket created at: ${manifest.bucketCreatedAt}`);\n lines.push(`- Bucket last updated at: ${manifest.bucketLastUpdatedAt}`);\n lines.push(`- mu version: ${manifest.muVersion}`);\n lines.push(`- Bucket layout version: ${manifest.bucketVersion}`);\n lines.push(\"\");\n const sources = Object.entries(manifest.sources).sort(([a], [b]) => a.localeCompare(b));\n lines.push(`## Sources (${sources.length})`);\n lines.push(\"\");\n if (sources.length === 0) {\n lines.push(\"_No sources yet._\");\n lines.push(\"\");\n } else {\n lines.push(\"| source workstream | tasks | added | last re-exported |\");\n lines.push(\"| --- | --- | --- | --- |\");\n for (const [name, src] of sources) {\n lines.push(\n `| [\\`${name}\\`](${name}/README.md) | ${src.tasks.length} | ${src.addedAt} | ${src.lastReExportedAt} |`,\n );\n }\n lines.push(\"\");\n }\n lines.push(\n \"_Bucket exports are additive: re-running `mu workstream export -w <ws> --out <this-dir>` appends or refreshes one source-ws subdirectory; `mu archive export <label> --out <this-dir>` (re)builds every source-ws from the named archive. See `INDEX.md` for the cross-source task table and `manifest.json` for per-task sha256s._\",\n );\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\n/** Bucket-level INDEX.md — union of every source-ws's task table,\n * with a leading source-ws column to disambiguate cross-source. */\nexport function renderBucketIndexMarkdown(input: RenderBucketInput): string {\n const lines: string[] = [];\n const label = input.bucketLabel ?? \"(no label)\";\n lines.push(`# ${label} — task index (all sources)`);\n lines.push(\"\");\n const sourcesWithTasks = input.sources.filter((s) => s.tasks.length > 0);\n if (sourcesWithTasks.length === 0) {\n lines.push(\"_No tasks._\");\n lines.push(\"\");\n return lines.join(\"\\n\");\n }\n lines.push(\"| source-ws | id | status | impact | effort | ROI | title |\");\n lines.push(\"| --- | --- | --- | --- | --- | --- | --- |\");\n // Stable sort across sources: source name then task name.\n const sortedSources = [...input.sources].sort((a, b) => a.name.localeCompare(b.name));\n for (const src of sortedSources) {\n for (const t of src.tasks) {\n const roi = (t.impact / t.effortDays).toFixed(2);\n const title = t.title.replace(/\\|/g, \"\\\\|\");\n lines.push(\n `| ${src.name} | [\\`${t.name}\\`](${src.name}/tasks/${t.name}.md) | ${t.status} | ${t.impact} | ${t.effortDays} | ${roi} | ${title} |`,\n );\n }\n }\n lines.push(\"\");\n return lines.join(\"\\n\");\n}\n\n// ─── Deletion banner ─────────────────────────────────────────────────\n\nexport const DELETED_BANNER_PREFIX = \"> **Deleted from DB on \";\n\nexport function bannerFor(timestamp: string): string {\n return `${DELETED_BANNER_PREFIX}${timestamp}** — this task no longer exists in mu's database. The export below is the last-known state. Re-export will not regenerate it.\\n\\n`;\n}\n\n// ─── manifest.json read/parse ────────────────────────────────────────\n\n/** Read an existing bucket manifest. Returns `{ kind: \"v2\", manifest }`\n * for a v0.3+ bucket; `{ kind: \"legacy\" }` for a pre-0.3\n * single-source export (whose manifest has a top-level `workstream`\n * field but no `bucketVersion`); `{ kind: \"absent\" }` if the file\n * doesn't exist. Throws `LegacyExportLayoutError` would be premature\n * here — the caller decides whether to refuse based on whether they\n * actually need to write. */\nexport type ManifestProbe =\n | { kind: \"v2\"; manifest: ExportManifest }\n | { kind: \"legacy\" }\n | { kind: \"absent\" }\n | { kind: \"corrupt\" };\n\nexport function readManifest(path: string): ManifestProbe {\n if (!existsSync(path)) return { kind: \"absent\" };\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch {\n return { kind: \"corrupt\" };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { kind: \"corrupt\" };\n }\n if (typeof parsed !== \"object\" || parsed === null) return { kind: \"corrupt\" };\n const obj = parsed as Record<string, unknown>;\n if (obj.bucketVersion === 2 && typeof obj.sources === \"object\" && obj.sources !== null) {\n // Best-effort cast; the caller treats unknown sources as a fresh\n // bucket if any field is malformed.\n return { kind: \"v2\", manifest: obj as unknown as ExportManifest };\n }\n // Legacy v1 shape: top-level `workstream` + `tasks` array, no\n // bucketVersion. We refuse to touch these in-place.\n if (typeof obj.workstream === \"string\" && Array.isArray(obj.tasks)) {\n return { kind: \"legacy\" };\n }\n return { kind: \"corrupt\" };\n}\n\n// ─── sha256 + mu version ─────────────────────────────────────────────\n\nexport function sha256Hex(content: string): string {\n return createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n}\n\n/** Read the package.json shipped next to the bundled CLI (or src/) so\n * the manifest records the mu version that produced it. Falls back\n * to \"unknown\" if the file isn't reachable. */\nexport function readMuVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n const raw = readFileSync(join(here, \"..\", \"package.json\"), \"utf8\");\n const parsed = JSON.parse(raw) as { version?: unknown };\n return typeof parsed.version === \"string\" ? parsed.version : \"unknown\";\n } catch {\n return \"unknown\";\n }\n}\n\n// ─── Renderer ────────────────────────────────────────────────────────\n\n/**\n * Render `input.sources` to disk under `input.outDir` in the v0.3\n * bucket layout. Idempotent + additive:\n * - If the bucket doesn't exist, scaffold it.\n * - If it does exist with bucketVersion 2, MERGE: each source in\n * `input.sources` either appends (new) or refreshes (existing)\n * its subdirectory; sources NOT in `input.sources` are left\n * untouched.\n * - If it exists but with a legacy (v1) manifest, throw\n * `LegacyExportLayoutError`.\n *\n * Per-task idempotency is sha256-keyed: a re-export of the same\n * source against an unchanged DB rewrites zero task files. Tasks\n * that disappear from a source between re-exports are preserved on\n * disk with a one-time `> **Deleted from DB on <ts>**` banner.\n */\nexport function renderToBucket(input: RenderBucketInput): RenderBucketResult {\n const outDir = input.outDir;\n if (existsSync(outDir)) {\n const stat = statSync(outDir);\n if (!stat.isDirectory()) {\n throw new Error(`renderToBucket: outDir exists and is not a directory: ${outDir}`);\n }\n } else {\n mkdirSync(outDir, { recursive: true });\n }\n\n const manifestPath = join(outDir, \"manifest.json\");\n const probe = readManifest(manifestPath);\n if (probe.kind === \"legacy\") {\n throw new LegacyExportLayoutError(outDir);\n }\n\n const now = new Date().toISOString();\n const muVersion = readMuVersion();\n const previous: ExportManifest | undefined = probe.kind === \"v2\" ? probe.manifest : undefined;\n // Start the new manifest from the previous one (so untouched\n // sources keep their entries) or a fresh scaffold.\n const manifest: ExportManifest = previous\n ? {\n bucketVersion: 2,\n bucketLabel: input.bucketLabel ?? previous.bucketLabel,\n bucketCreatedAt: previous.bucketCreatedAt,\n bucketLastUpdatedAt: now,\n muVersion,\n sources: { ...previous.sources },\n }\n : {\n bucketVersion: 2,\n bucketLabel: input.bucketLabel,\n bucketCreatedAt: now,\n bucketLastUpdatedAt: now,\n muVersion,\n sources: {},\n };\n\n let writtenTotal = 0;\n let unchangedTotal = 0;\n let preservedTotal = 0;\n\n for (const source of input.sources) {\n const sourceDir = join(outDir, source.name);\n const tasksDir = join(sourceDir, \"tasks\");\n mkdirSync(tasksDir, { recursive: true });\n\n const previousSource = previous?.sources[source.name];\n const previousById = new Map<string, ExportTaskEntry>();\n if (previousSource) {\n for (const t of previousSource.tasks) previousById.set(t.id, t);\n }\n\n const liveIds = new Set(source.tasks.map((t) => t.name));\n const manifestEntries: ExportTaskEntry[] = [];\n let written = 0;\n let unchanged = 0;\n let preserved = 0;\n\n for (const task of source.tasks) {\n const edges = source.edges.get(task.name) ?? { blockers: [], dependents: [] };\n const notes = source.notes.get(task.name) ?? [];\n const md = renderTaskMarkdown(task, edges, notes);\n const sha = sha256Hex(md);\n const relPath = `${source.name}/tasks/${task.name}.md`;\n const absPath = join(outDir, relPath);\n\n const prev = previousById.get(task.name);\n const onDisk = existsSync(absPath);\n if (onDisk && prev?.sha256 === sha && prev.deletedAt === undefined) {\n unchanged += 1;\n } else {\n writeFileSync(absPath, md, \"utf8\");\n written += 1;\n }\n manifestEntries.push({ id: task.name, path: relPath, sha256: sha });\n }\n\n // Preserve files for tasks that disappeared from the source.\n // Banner is one-time (idempotent across re-exports).\n for (const prev of previousById.values()) {\n if (liveIds.has(prev.id)) continue;\n const absPath = join(outDir, prev.path);\n const deletedAt = prev.deletedAt ?? now;\n if (existsSync(absPath)) {\n const existing = readFileSync(absPath, \"utf8\");\n if (!existing.startsWith(DELETED_BANNER_PREFIX)) {\n writeFileSync(absPath, bannerFor(deletedAt) + existing, \"utf8\");\n }\n }\n manifestEntries.push({ ...prev, deletedAt });\n preserved += 1;\n }\n\n // Stable order — diffs across re-exports stay clean.\n manifestEntries.sort((a, b) => a.id.localeCompare(b.id));\n\n // Per-source-ws scaffolding (cheap; always rewritten — but the\n // sha256 short-circuit on `tasks/<id>.md` is what matters for\n // mtime stability of the operator-visible files).\n writeFileSync(\n join(sourceDir, \"README.md\"),\n renderSourceReadmeMarkdown(source.name, source.tasks, now),\n \"utf8\",\n );\n writeFileSync(\n join(sourceDir, \"INDEX.md\"),\n renderSourceIndexMarkdown(source.name, source.tasks),\n \"utf8\",\n );\n\n manifest.sources[source.name] = {\n addedAt: previousSource?.addedAt ?? now,\n lastReExportedAt: now,\n eventsSeqAtExport: source.eventsSeqAtExport,\n tasks: manifestEntries,\n };\n\n writtenTotal += written;\n unchangedTotal += unchanged;\n preservedTotal += preserved;\n }\n\n // Bucket-level scaffolding: covers EVERY source-ws in the\n // (possibly merged) manifest, not just the ones in this call.\n // The bucket README/INDEX must reflect untouched siblings too.\n // To render the bucket INDEX we need TaskRow shapes for siblings\n // we did NOT pass in this call; we don't have them. Compromise:\n // the bucket INDEX renders ONLY the sources whose data we have\n // in `input.sources`. This is honest about what this call refreshed\n // and matches the additive semantics: mu archive export passes\n // every source; mu workstream export passes one and the bucket\n // INDEX shrinks to that one source. Operators who care about the\n // global table use mu archive export.\n const bucketReadme = renderBucketReadmeMarkdown(manifest);\n const bucketIndex = renderBucketIndexMarkdown(input);\n writeFileSync(join(outDir, \"README.md\"), bucketReadme, \"utf8\");\n writeFileSync(join(outDir, \"INDEX.md\"), bucketIndex, \"utf8\");\n\n writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`, \"utf8\");\n\n return {\n outDir,\n written: writtenTotal,\n unchanged: unchangedTotal,\n preserved: preservedTotal,\n manifestPath,\n manifest,\n };\n}\n\n// ─── Source builders ──────────────────────────────────────────────────\n\n/** Construct an ExportSource for one live workstream by reading the\n * current DB. Pure data assembly; renderer does the I/O. */\nexport function exportSourceForWorkstream(db: Db, workstream: string): ExportSource {\n const tasks = listTasks(db, workstream);\n const edges = new Map<string, { blockers: string[]; dependents: string[] }>();\n const notes = new Map<string, TaskNoteRow[]>();\n for (const t of tasks) {\n edges.set(t.name, getTaskEdges(db, t.name, t.workstreamName));\n notes.set(t.name, listNotes(db, t.name, t.workstreamName));\n }\n return {\n name: workstream,\n tasks,\n edges,\n notes,\n eventsSeqAtExport: latestSeq(db),\n };\n}\n\n/** Construct ExportSources for every source workstream that\n * contributed to an archive label. One ExportSource per\n * (archive_id, source_workstream) partition. The TaskRow shapes are\n * reconstructed from archived_* rows; `workstreamName` is set to\n * the source workstream so the rendered frontmatter reflects the\n * task's original home. */\nexport function exportSourcesForArchive(db: Db, label: string): ExportSource[] {\n // Pull every archived task in deterministic (source_workstream,\n // original_local_id) order.\n const allTasks = listArchivedTasks(db, label);\n if (allTasks.length === 0) return [];\n\n // archive_id (resolved internally) — we need it for the edge /\n // note / event queries below. Look it up via the first row's\n // archiveLabel (every row in `allTasks` shares the same archive).\n const archiveIdRow = db.prepare(\"SELECT id FROM archives WHERE label = ?\").get(label) as\n | { id: number }\n | undefined;\n if (!archiveIdRow) return []; // Should be unreachable: listArchivedTasks would have thrown.\n const archiveId = archiveIdRow.id;\n\n // Group tasks by source workstream.\n const bySource = new Map<string, typeof allTasks>();\n for (const t of allTasks) {\n const list = bySource.get(t.sourceWorkstream) ?? [];\n list.push(t);\n bySource.set(t.sourceWorkstream, list);\n }\n\n // Pre-load notes + edges per archived task. Two batched queries\n // is enough — the per-task loops below just dereference.\n const notesByArchivedId = new Map<number, TaskNoteRow[]>();\n const noteRows = db\n .prepare(\n `SELECT archived_task_id AS aid, author, content, created_at\n FROM archived_notes\n WHERE archive_id = ?\n ORDER BY id`,\n )\n .all(archiveId) as {\n aid: number;\n author: string | null;\n content: string;\n created_at: string;\n }[];\n for (const n of noteRows) {\n const list = notesByArchivedId.get(n.aid) ?? [];\n list.push({ author: n.author, content: n.content, createdAt: n.created_at });\n notesByArchivedId.set(n.aid, list);\n }\n\n // Edges: archived endpoint ids → original_local_id strings.\n // Build {from_archived_id → original_local_id} once, then map.\n const localIdByArchivedId = new Map<number, string>();\n for (const t of allTasks) localIdByArchivedId.set(t.id, t.originalLocalId);\n const blockersByArchivedId = new Map<number, string[]>();\n const dependentsByArchivedId = new Map<number, string[]>();\n const edgeRows = db\n .prepare(\n `SELECT from_archived_id AS f, to_archived_id AS t\n FROM archived_edges\n WHERE archive_id = ?`,\n )\n .all(archiveId) as { f: number; t: number }[];\n for (const e of edgeRows) {\n const fromId = localIdByArchivedId.get(e.f);\n const toId = localIdByArchivedId.get(e.t);\n if (fromId === undefined || toId === undefined) continue;\n // edge `from blocks to`: `from` is a blocker of `to`; `to` is\n // a dependent of `from`. (See getTaskEdges in src/tasks.ts.)\n const blockers = blockersByArchivedId.get(e.t) ?? [];\n blockers.push(fromId);\n blockersByArchivedId.set(e.t, blockers);\n const deps = dependentsByArchivedId.get(e.f) ?? [];\n deps.push(toId);\n dependentsByArchivedId.set(e.f, deps);\n }\n\n // archived_events: max(seq) per source workstream gives us the\n // best available \"events seq at archive time\" — the highest seq\n // of any event that contributed to this source's archive.\n const eventSeqRows = db\n .prepare(\n `SELECT source_workstream AS sw, MAX(seq) AS max_seq\n FROM archived_events\n WHERE archive_id = ?\n GROUP BY source_workstream`,\n )\n .all(archiveId) as { sw: string; max_seq: number }[];\n const eventsSeqBySource = new Map<string, number>();\n for (const r of eventSeqRows) eventsSeqBySource.set(r.sw, r.max_seq);\n\n const sources: ExportSource[] = [];\n for (const [sourceName, taskList] of bySource) {\n const tasks: TaskRow[] = taskList.map((t) => ({\n name: t.originalLocalId,\n localId: t.originalLocalId,\n workstreamName: t.sourceWorkstream,\n title: t.title,\n // Status as snapshotted; cast through the TaskStatus union by\n // way of any narrowed value (the renderer doesn't validate).\n status: t.status as TaskRow[\"status\"],\n impact: t.impact,\n effortDays: t.effortDays,\n ownerName: t.ownerName,\n createdAt: t.originalCreatedAt,\n updatedAt: t.originalUpdatedAt,\n }));\n const edges = new Map<string, { blockers: string[]; dependents: string[] }>();\n const notes = new Map<string, TaskNoteRow[]>();\n for (const t of taskList) {\n const blockers = (blockersByArchivedId.get(t.id) ?? []).sort((a, b) => a.localeCompare(b));\n const dependents = (dependentsByArchivedId.get(t.id) ?? []).sort((a, b) =>\n a.localeCompare(b),\n );\n edges.set(t.originalLocalId, { blockers, dependents });\n const ns = notesByArchivedId.get(t.id);\n if (ns) notes.set(t.originalLocalId, ns);\n }\n sources.push({\n name: sourceName,\n tasks,\n edges,\n notes,\n eventsSeqAtExport: eventsSeqBySource.get(sourceName) ?? 0,\n });\n }\n\n // Stable order across the bucket: source name ascending.\n sources.sort((a, b) => a.name.localeCompare(b.name));\n return sources;\n}\n\n// ─── Public verbs ────────────────────────────────────────────────────\n\nexport interface ExportArchiveOptions {\n label: string;\n /** Output directory (the bucket). Created if missing. */\n outDir: string;\n}\n\nexport interface ExportArchiveResult extends RenderBucketResult {\n archiveLabel: string;\n /** Number of source workstreams the renderer wrote / refreshed. */\n sourceCount: number;\n}\n\n/** Render every source-ws in an archive to a bucket directory.\n * Throws `ArchiveNotFoundError` (via listArchivedTasks) when the\n * label doesn't exist. */\nexport function exportArchive(db: Db, opts: ExportArchiveOptions): ExportArchiveResult {\n // Resolve up-front so a missing label fails before any disk I/O.\n // listArchivedTasks throws ArchiveNotFoundError on miss.\n listArchivedTasks(db, opts.label);\n const sources = exportSourcesForArchive(db, opts.label);\n const result = renderToBucket({\n sources,\n bucketLabel: opts.label,\n outDir: opts.outDir,\n });\n emitEvent(\n db,\n null,\n `archive export ${opts.label} (out=${result.outDir}, sources=${sources.length}, tasks=${sources.reduce((acc, s) => acc + s.tasks.length, 0)}, written=${result.written}, unchanged=${result.unchanged}, preserved=${result.preserved})`,\n );\n return { ...result, archiveLabel: opts.label, sourceCount: sources.length };\n}\n","// mu — archives: cross-workstream preservation of task graphs.\n//\n// An archive is an operator-named bucket that snapshots a workstream's\n// task graph (tasks + edges + notes + kind='event' agent_logs rows)\n// before `mu workstream destroy` blows it away. Unlike snapshots\n// (whole-DB binary backups for one-shot recovery via `mu undo`), an\n// archive is queryable structured state that survives indefinitely\n// and can accumulate snapshots from MULTIPLE workstreams under the\n// same label.\n//\n// Design pillars (see docs/VOCABULARY.md § archive):\n// - archives outlive workstreams: archives.label is GLOBALLY unique\n// (not per-workstream), and archived_tasks columns referencing\n// the source workstream are TEXT (not FKs).\n// - additive accumulation: addToArchive(label, ws) is idempotent\n// at (archive, source_workstream) granularity. Re-running on the\n// same workstream is a no-op; a new task added to the workstream\n// post-archive shows up on the next add. Two different workstreams\n// under the same label both contribute their snapshots without\n// overwriting each other.\n// - Phase 1 (this file): SDK + schema only. The CLI (`mu archive\n// add/list/show/free/free-all`) lands in Phase 2; the destroy\n// auto-archive hook lands in Phase 3; the export renderer lands\n// in Phase 4.\n\nimport { type Db, resolveWorkstreamId } from \"./db.js\";\nimport { emitEvent } from \"./logs.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\n\n// ─── Label validation ────────────────────────────────────────────────\n\n/**\n * Allowed archive-label shape: lowercase alpha first, then alnum,\n * underscore, or hyphen, up to 64 chars total. Wider than the\n * workstream-name window (32 chars) because archive labels often\n * encode the workstream name PLUS a date / purpose (\"auth-2026-q1\",\n * \"rewrite-postmortem\").\n */\nconst ARCHIVE_LABEL_RE = /^[a-z][a-z0-9_-]{0,63}$/;\n\n/** True iff `label` matches the archive-label rule. Pure predicate. */\nexport function isValidArchiveLabel(label: string): boolean {\n return ARCHIVE_LABEL_RE.test(label);\n}\n\nfunction assertValidArchiveLabel(label: string): void {\n if (!isValidArchiveLabel(label)) throw new ArchiveLabelInvalidError(label);\n}\n\n// ─── Typed errors ────────────────────────────────────────────────────\n\nexport class ArchiveNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"ArchiveNotFoundError\";\n constructor(public readonly label: string) {\n super(`no such archive: ${label}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List existing archives\", command: \"mu archive list\" },\n {\n intent: \"Create this archive\",\n command: `mu archive add ${this.label} -w <workstream>`,\n },\n ];\n }\n}\n\nexport class ArchiveAlreadyExistsError extends Error implements HasNextSteps {\n override readonly name = \"ArchiveAlreadyExistsError\";\n constructor(public readonly label: string) {\n super(`archive already exists: ${label}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Add a workstream to the existing archive (additive)\",\n command: `mu archive add ${this.label} -w <workstream>`,\n },\n {\n intent: \"Inspect the existing archive\",\n command: `mu archive show ${this.label}`,\n },\n ];\n }\n}\n\nexport class ArchiveLabelInvalidError extends Error implements HasNextSteps {\n override readonly name = \"ArchiveLabelInvalidError\";\n constructor(public readonly attempted: string) {\n super(\n `invalid archive label ${JSON.stringify(attempted)}: must match /^[a-z][a-z0-9_-]{0,63}$/. Use letters, digits, '_', and '-' only; start with a letter; up to 64 chars.`,\n );\n }\n errorNextSteps(): NextStep[] {\n const sanitized =\n this.attempted\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, \"_\")\n .replace(/^[^a-z]+/, \"\")\n .slice(0, 64) || \"archive\";\n return [\n {\n intent: \"Try a sanitized label (best guess)\",\n command: `mu archive add ${sanitized} -w <workstream>`,\n },\n { intent: \"List existing archives\", command: \"mu archive list\" },\n ];\n }\n}\n\n// ─── Domain types ─────────────────────────────────────────────────────\n\nexport interface Archive {\n /** Surrogate INTEGER id. Internal — operators identify by label. */\n id: number;\n /** Globally-unique operator-facing TEXT label. */\n label: string;\n /** Optional one-liner description set at create time. */\n description: string | null;\n /** ISO 8601, set when the archive was first created. */\n createdAt: string;\n /** ISO 8601, bumped on every successful add. */\n lastAddedAt: string;\n}\n\nexport interface ArchiveSourceSummary {\n /** TEXT name of the source workstream this snapshot came from. */\n name: string;\n /** Number of archived_tasks rows from this workstream in this archive. */\n taskCount: number;\n /** Earliest archived_at among this workstream's rows in this archive. */\n addedAt: string;\n}\n\nexport interface ArchiveSummary extends Archive {\n /** One row per source workstream that contributed to this archive,\n * sorted by source workstream name. */\n sourceWorkstreams: ArchiveSourceSummary[];\n /** Total archived_tasks rows across every source workstream. */\n totalTasks: number;\n}\n\nexport interface ArchivedTaskRow {\n /** Surrogate id of the archived_tasks row. */\n id: number;\n /** Operator-facing label of the parent archive. */\n archiveLabel: string;\n /** TEXT name of the source workstream (intentionally not an FK). */\n sourceWorkstream: string;\n /** The local_id the task had in its source workstream. */\n originalLocalId: string;\n title: string;\n /** Status as stored at archive time. */\n status: string;\n impact: number;\n effortDays: number;\n /** Owner agent name as snapshotted at archive time. */\n ownerName: string | null;\n /** Status at the moment of archive (pinned for re-add semantics). */\n archivedAtStatus: string;\n /** ISO 8601, when this row was added to the archive. */\n archivedAt: string;\n /** Original tasks.created_at preserved for retrospect ordering. */\n originalCreatedAt: string;\n /** Original tasks.updated_at preserved for retrospect ordering. */\n originalUpdatedAt: string;\n}\n\nexport interface AddToArchiveResult {\n /** Number of new archived_tasks rows actually inserted. Zero on a\n * re-run against the same workstream (idempotency). */\n addedTasks: number;\n /** Tasks present in the source workstream that were already in the\n * archive (skipped by the OR IGNORE). */\n skippedTasks: number;\n /** Number of new archived_edges rows actually inserted. */\n addedEdges: number;\n /** Number of new archived_notes rows inserted. (Notes have no\n * natural unique key, so this matches the count of notes attached\n * to NEW archived_tasks rows; existing rows' notes are not\n * duplicated because note copy is gated on at-least-one new task\n * for the (archive, source_workstream) pair.) */\n addedNotes: number;\n /** Number of new archived_events rows inserted (one per kind='event'\n * agent_logs row in the source workstream). */\n addedEvents: number;\n}\n\nexport interface RemoveFromArchiveResult {\n /** Number of archived_tasks rows deleted (cascade cleans the rest). */\n removedTasks: number;\n /** Number of archived_edges rows removed by the cascade. */\n removedEdges: number;\n /** Number of archived_notes rows removed by the cascade. */\n removedNotes: number;\n /** Number of archived_events rows directly deleted. */\n removedEvents: number;\n}\n\n// ─── Internal row shapes ──────────────────────────────────────────────\n\ninterface RawArchiveRow {\n id: number;\n label: string;\n description: string | null;\n created_at: string;\n last_added_at: string;\n}\n\nfunction rowFromArchive(r: RawArchiveRow): Archive {\n return {\n id: r.id,\n label: r.label,\n description: r.description,\n createdAt: r.created_at,\n lastAddedAt: r.last_added_at,\n };\n}\n\ninterface RawArchivedTaskRow {\n id: number;\n archive_label: string;\n source_workstream: string;\n original_local_id: string;\n title: string;\n status: string;\n impact: number;\n effort_days: number;\n owner_name: string | null;\n archived_at_status: string;\n archived_at: string;\n original_created_at: string;\n original_updated_at: string;\n}\n\nfunction rowFromArchivedTask(r: RawArchivedTaskRow): ArchivedTaskRow {\n return {\n id: r.id,\n archiveLabel: r.archive_label,\n sourceWorkstream: r.source_workstream,\n originalLocalId: r.original_local_id,\n title: r.title,\n status: r.status,\n impact: r.impact,\n effortDays: r.effort_days,\n ownerName: r.owner_name,\n archivedAtStatus: r.archived_at_status,\n archivedAt: r.archived_at,\n originalCreatedAt: r.original_created_at,\n originalUpdatedAt: r.original_updated_at,\n };\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\n/** Resolve an archive label to its surrogate id. Returns null on miss\n * so callers can pick between throwing (createArchive) and returning\n * empty (listArchivedTasks). archives.label is globally unique\n * (NOT per-workstream) by design — see schema doc in src/db.ts. */\nfunction tryResolveArchiveId(db: Db, label: string): number | null {\n // archives is the one entity table whose TEXT name is intentionally\n // global (archives outlive workstreams) — no per-workstream scope.\n const row = db.prepare(\"SELECT id FROM archives WHERE label = ?\").get(label) as\n | { id: number }\n | undefined;\n return row ? row.id : null;\n}\n\n/** Resolve an archive label to its surrogate id, throwing\n * ArchiveNotFoundError on miss. */\nfunction resolveArchiveId(db: Db, label: string): number {\n const id = tryResolveArchiveId(db, label);\n if (id === null) throw new ArchiveNotFoundError(label);\n return id;\n}\n\nfunction archiveByLabel(db: Db, label: string): Archive | null {\n // archive labels are globally unique (no workstream scope).\n const row = db\n .prepare(\n \"SELECT id, label, description, created_at, last_added_at FROM archives WHERE label = ?\",\n )\n .get(label) as RawArchiveRow | undefined;\n return row ? rowFromArchive(row) : null;\n}\n\nfunction summarizeArchive(db: Db, archive: Archive): ArchiveSummary {\n const sources = db\n .prepare(\n `SELECT source_workstream AS name,\n COUNT(*) AS task_count,\n MIN(archived_at) AS added_at\n FROM archived_tasks\n WHERE archive_id = ?\n GROUP BY source_workstream\n ORDER BY source_workstream`,\n )\n .all(archive.id) as { name: string; task_count: number; added_at: string }[];\n const sourceWorkstreams: ArchiveSourceSummary[] = sources.map((s) => ({\n name: s.name,\n taskCount: s.task_count,\n addedAt: s.added_at,\n }));\n const totalTasks = sourceWorkstreams.reduce((acc, s) => acc + s.taskCount, 0);\n return { ...archive, sourceWorkstreams, totalTasks };\n}\n\n// ─── Public SDK ───────────────────────────────────────────────────────\n\n/**\n * Create a new archive bucket. Throws `ArchiveAlreadyExistsError` if\n * the label is already in use; throws `ArchiveLabelInvalidError` for\n * malformed labels.\n *\n * The archive starts EMPTY: created_at and last_added_at both equal\n * now(). Use `addToArchive(label, workstream)` to populate it.\n */\nexport function createArchive(db: Db, label: string, description?: string): Archive {\n assertValidArchiveLabel(label);\n if (tryResolveArchiveId(db, label) !== null) {\n throw new ArchiveAlreadyExistsError(label);\n }\n const now = new Date().toISOString();\n const result = db\n .prepare(\n `INSERT INTO archives (label, description, created_at, last_added_at)\n VALUES (?, ?, ?, ?)`,\n )\n .run(label, description ?? null, now, now);\n const id = Number(result.lastInsertRowid);\n emitEvent(db, null, `archive create ${label}`);\n return {\n id,\n label,\n description: description ?? null,\n createdAt: now,\n lastAddedAt: now,\n };\n}\n\n/**\n * List every archive on this machine, summarised with per-source-\n * workstream counts. Sorted by label ascending. Pure read; safe to\n * call against an empty DB (returns []).\n */\nexport function listArchives(db: Db): ArchiveSummary[] {\n const rows = db\n .prepare(\n \"SELECT id, label, description, created_at, last_added_at FROM archives ORDER BY label\",\n )\n .all() as RawArchiveRow[];\n return rows.map((r) => summarizeArchive(db, rowFromArchive(r)));\n}\n\n/**\n * Look up a single archive by label. Throws `ArchiveNotFoundError`\n * on miss.\n */\nexport function getArchive(db: Db, label: string): ArchiveSummary {\n const archive = archiveByLabel(db, label);\n if (archive === null) throw new ArchiveNotFoundError(label);\n return summarizeArchive(db, archive);\n}\n\n/**\n * Delete an archive and every row that references it. The FK\n * CASCADE chain (archives → archived_tasks → archived_edges /\n * archived_notes; archives → archived_events) cleans every row in\n * one statement.\n *\n * Idempotent: throws `ArchiveNotFoundError` rather than silently\n * succeeding on a missing label (operator confusion safeguard).\n *\n * Mirror of `destroyWorkstream`'s safety story but cheaper: archives\n * have no on-disk artifacts (no tmux session, no workspaces). The\n * pre-delete snapshot is the operator's recovery path if they run\n * this verb by mistake (handled in the CLI wrapper, Phase 2).\n */\nexport function deleteArchive(db: Db, label: string): void {\n const id = resolveArchiveId(db, label);\n db.prepare(\"DELETE FROM archives WHERE id = ?\").run(id);\n emitEvent(db, null, `archive delete ${label}`);\n}\n\n/**\n * Add every task in `workstream` to the archive identified by `label`.\n *\n * Idempotency invariant: re-running with the same (label, workstream)\n * pair is a no-op for tasks already present. The\n * (archive_id, source_workstream, original_local_id) UNIQUE on\n * archived_tasks is the lever; we INSERT OR IGNORE and skip notes /\n * events for the (archive, source_workstream) pair entirely when the\n * task copy added zero new rows. This makes addToArchive\n * coarse-grained idempotent: the only way to get duplicate notes is\n * to add a NEW task to the source workstream and re-run, which\n * legitimately copies the new task's notes.\n *\n * Throws:\n * - `ArchiveNotFoundError` if the label doesn't exist (call\n * `createArchive` first).\n * - `WorkstreamNotFoundError` if the source workstream is gone\n * (you must archive BEFORE destroy).\n *\n * The whole operation runs in a transaction so a partial failure\n * leaves the archive untouched.\n */\nexport function addToArchive(db: Db, label: string, workstream: string): AddToArchiveResult {\n const archiveId = resolveArchiveId(db, label);\n // Throws WorkstreamNotFoundError if the source is already gone.\n const wsId = resolveWorkstreamId(db, workstream);\n\n return db.transaction(() => {\n const now = new Date().toISOString();\n\n // Pull every task in the source workstream. We snapshot owner_name\n // by joining agents (operator-facing string), not the surrogate\n // owner_id (which means nothing post-destroy).\n const sourceTasks = db\n .prepare(\n `SELECT t.id AS source_task_id,\n t.local_id AS original_local_id,\n t.title AS title,\n t.status AS status,\n t.impact AS impact,\n t.effort_days AS effort_days,\n ag.name AS owner_name,\n t.created_at AS original_created_at,\n t.updated_at AS original_updated_at\n FROM tasks t\n LEFT JOIN agents ag ON ag.id = t.owner_id\n WHERE t.workstream_id = ?\n ORDER BY t.id`,\n )\n .all(wsId) as {\n source_task_id: number;\n original_local_id: string;\n title: string;\n status: string;\n impact: number;\n effort_days: number;\n owner_name: string | null;\n original_created_at: string;\n original_updated_at: string;\n }[];\n\n const insertTask = db.prepare(\n `INSERT OR IGNORE INTO archived_tasks (\n archive_id, source_workstream, original_local_id, title, status,\n impact, effort_days, owner_name, archived_at_status, archived_at,\n original_created_at, original_updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n );\n\n // archived_id lookup keyed on (archive_id, source_workstream,\n // original_local_id) — the natural composite key for any prior or\n // new row.\n const lookupArchivedId = db.prepare(\n `SELECT id FROM archived_tasks\n WHERE archive_id = ? AND source_workstream = ? AND original_local_id = ?`,\n );\n\n let addedTasks = 0;\n let skippedTasks = 0;\n // Surrogate ids of the archived_tasks rows we just inserted (NEW\n // rows only — the gate for note/event copy below).\n const newArchivedIds: number[] = [];\n // Map source-side tasks.id -> archived_tasks.id for every source\n // task (NEW or already-present). Edge insertion needs both\n // endpoints regardless of newness.\n const archivedIdBySourceId = new Map<number, number>();\n\n for (const t of sourceTasks) {\n const r = insertTask.run(\n archiveId,\n workstream,\n t.original_local_id,\n t.title,\n t.status,\n t.impact,\n t.effort_days,\n t.owner_name,\n t.status,\n now,\n t.original_created_at,\n t.original_updated_at,\n );\n const isNew = r.changes > 0;\n const lookup = lookupArchivedId.get(archiveId, workstream, t.original_local_id) as\n | { id: number }\n | undefined;\n if (!lookup) {\n // Unreachable in practice: we either just inserted or the row\n // pre-existed. Defend the type narrowing.\n throw new Error(\n `addToArchive: archived_tasks lookup missed after upsert: ${workstream}/${t.original_local_id}`,\n );\n }\n archivedIdBySourceId.set(t.source_task_id, lookup.id);\n if (isNew) {\n addedTasks += 1;\n newArchivedIds.push(lookup.id);\n } else {\n skippedTasks += 1;\n }\n }\n\n // Edges: copy every task_edge whose endpoints are both in the\n // source workstream's task set. INSERT OR IGNORE so a re-run\n // is a no-op. Cross-workstream edges don't exist by design (the\n // addTask path rejects them via CrossWorkstreamEdgeError), but\n // the WHERE-clause filter keeps the implementation honest.\n const sourceEdges = db\n .prepare(\n `SELECT e.from_task_id AS from_id, e.to_task_id AS to_id\n FROM task_edges e\n JOIN tasks tf ON tf.id = e.from_task_id\n JOIN tasks tt ON tt.id = e.to_task_id\n WHERE tf.workstream_id = ? AND tt.workstream_id = ?`,\n )\n .all(wsId, wsId) as { from_id: number; to_id: number }[];\n const insertEdge = db.prepare(\n `INSERT OR IGNORE INTO archived_edges (archive_id, from_archived_id, to_archived_id)\n VALUES (?, ?, ?)`,\n );\n let addedEdges = 0;\n for (const e of sourceEdges) {\n const fromArchivedId = archivedIdBySourceId.get(e.from_id);\n const toArchivedId = archivedIdBySourceId.get(e.to_id);\n // Both endpoints are guaranteed in the map: tf and tt are both\n // in workstream_id = wsId, so they were enumerated above.\n if (fromArchivedId === undefined || toArchivedId === undefined) continue;\n const r = insertEdge.run(archiveId, fromArchivedId, toArchivedId);\n if (r.changes > 0) addedEdges += 1;\n }\n\n // Notes + events: gated on at-least-one new task for the\n // (archive, source_workstream) pair. Without the gate, re-running\n // addToArchive on the same workstream would duplicate every note\n // and event (notes have no natural unique key; events keyed on\n // (seq, archive) are also a duplicate of the same source row).\n // The gate makes the SDK truly idempotent at the operator-\n // visible \"add this workstream\" granularity: re-runs are no-ops;\n // adding a new task and re-running picks up only the new task's\n // notes and any new events.\n let addedNotes = 0;\n let addedEvents = 0;\n if (newArchivedIds.length > 0) {\n // Notes: copy every task_notes row whose task is in the NEW\n // archived_tasks set. An already-archived task's notes are\n // untouched (the operator can't selectively re-snapshot a\n // single task; they re-archive the whole workstream).\n const insertNote = db.prepare(\n `INSERT INTO archived_notes (archive_id, archived_task_id, author, content, created_at)\n VALUES (?, ?, ?, ?, ?)`,\n );\n const noteCopySql = db.prepare(\n `SELECT n.author AS author, n.content AS content, n.created_at AS created_at\n FROM task_notes n\n WHERE n.task_id = ?\n ORDER BY n.id`,\n );\n // Build reverse map archived_id -> source_id for the new set\n // so we can iterate source ids (the JOIN side that has notes).\n const sourceIdByArchivedId = new Map<number, number>();\n for (const [sId, aId] of archivedIdBySourceId) sourceIdByArchivedId.set(aId, sId);\n for (const archivedId of newArchivedIds) {\n const sourceId = sourceIdByArchivedId.get(archivedId);\n if (sourceId === undefined) continue;\n const notes = noteCopySql.all(sourceId) as {\n author: string | null;\n content: string;\n created_at: string;\n }[];\n for (const note of notes) {\n insertNote.run(archiveId, archivedId, note.author, note.content, note.created_at);\n addedNotes += 1;\n }\n }\n\n // Events: snapshot every kind='event' agent_logs row for the\n // source workstream. Only events (not the full message log;\n // recoverable via snapshot+undo). Same idempotency gate as\n // notes: only fires when at least one new task was added.\n const events = db\n .prepare(\n `SELECT seq, source, payload, created_at\n FROM agent_logs\n WHERE workstream_id = ? AND kind = 'event'\n ORDER BY seq`,\n )\n .all(wsId) as {\n seq: number;\n source: string;\n payload: string;\n created_at: string;\n }[];\n const insertEvent = db.prepare(\n `INSERT INTO archived_events (\n archive_id, source_workstream, seq, source, payload, created_at\n ) VALUES (?, ?, ?, ?, ?, ?)`,\n );\n for (const ev of events) {\n insertEvent.run(archiveId, workstream, ev.seq, ev.source, ev.payload, ev.created_at);\n addedEvents += 1;\n }\n }\n\n db.prepare(\"UPDATE archives SET last_added_at = ? WHERE id = ?\").run(now, archiveId);\n emitEvent(\n db,\n null,\n `archive add ${label} -w ${workstream} (tasks=${addedTasks}, edges=${addedEdges}, notes=${addedNotes}, events=${addedEvents}, skipped_existing=${skippedTasks})`,\n );\n\n return { addedTasks, skippedTasks, addedEdges, addedNotes, addedEvents };\n })();\n}\n\n/**\n * Remove every row contributed by `sourceWorkstream` from the named\n * archive. Other source workstreams' contributions are untouched\n * (additive accumulation invariant). Throws `ArchiveNotFoundError`\n * if the label doesn't exist; returns all-zero counts (no error)\n * when the source workstream never contributed to this archive.\n */\nexport function removeFromArchive(\n db: Db,\n label: string,\n sourceWorkstream: string,\n): RemoveFromArchiveResult {\n const archiveId = resolveArchiveId(db, label);\n return db.transaction(() => {\n // Pre-count cascade victims (changes() only reports rows directly\n // affected by the last statement, not cascade victims).\n const countBefore = (sql: string, params: unknown[]) =>\n (db.prepare(sql).get(...params) as { n: number }).n;\n const removedTasks = countBefore(\n \"SELECT COUNT(*) AS n FROM archived_tasks WHERE archive_id = ? AND source_workstream = ?\",\n [archiveId, sourceWorkstream],\n );\n const removedNotes = countBefore(\n `SELECT COUNT(*) AS n\n FROM archived_notes an\n JOIN archived_tasks t ON t.id = an.archived_task_id\n WHERE t.archive_id = ? AND t.source_workstream = ?`,\n [archiveId, sourceWorkstream],\n );\n const removedEdges = countBefore(\n `SELECT COUNT(*) AS n\n FROM archived_edges e\n JOIN archived_tasks t ON t.id = e.from_archived_id\n WHERE e.archive_id = ? AND t.source_workstream = ?`,\n [archiveId, sourceWorkstream],\n );\n const removedEvents = countBefore(\n \"SELECT COUNT(*) AS n FROM archived_events WHERE archive_id = ? AND source_workstream = ?\",\n [archiveId, sourceWorkstream],\n );\n\n // archived_tasks DELETE cascades to edges + notes via the FK\n // chain; archived_events is a sibling of archived_tasks (the\n // cascade is from archives, not from tasks) so we delete it\n // explicitly here.\n db.prepare(\"DELETE FROM archived_tasks WHERE archive_id = ? AND source_workstream = ?\").run(\n archiveId,\n sourceWorkstream,\n );\n db.prepare(\"DELETE FROM archived_events WHERE archive_id = ? AND source_workstream = ?\").run(\n archiveId,\n sourceWorkstream,\n );\n\n if (removedTasks > 0 || removedEvents > 0) {\n emitEvent(\n db,\n null,\n `archive remove ${label} -w ${sourceWorkstream} (tasks=${removedTasks}, edges=${removedEdges}, notes=${removedNotes}, events=${removedEvents})`,\n );\n }\n return { removedTasks, removedEdges, removedNotes, removedEvents };\n })();\n}\n\nexport interface ListArchivedTasksOptions {\n /** Filter by source workstream. Omit to return every source's\n * contribution, sorted by (source_workstream, original_local_id). */\n sourceWorkstream?: string;\n}\n\n/**\n * List archived task rows in a single archive. Throws\n * `ArchiveNotFoundError` on missing label.\n *\n * Default order: source_workstream ASC, then original_local_id ASC,\n * so the output is deterministic and groups each workstream's\n * contribution together.\n */\n// ─── searchArchives ──────────────────────────────────────────────────\n\nexport interface ArchiveSearchHit {\n /** Operator-facing label of the parent archive. */\n archiveLabel: string;\n /** TEXT name of the source workstream this row came from. */\n sourceWorkstream: string;\n /** local_id the task had in its source workstream. */\n originalLocalId: string;\n /** Snapshotted title (always present, even on a note match). */\n title: string;\n /** Where the match was found: the title column, or one of this\n * task's archived_notes.content rows. Title matches win when\n * both apply (the dedup pass below picks one row per task). */\n matchKind: \"title\" | \"note\";\n /** Up to ~120 chars of context centered on the FIRST occurrence\n * of the pattern in the matching field. Case-insensitive index;\n * the snippet itself preserves original casing. */\n matchSnippet: string;\n}\n\nexport interface SearchArchivesOptions {\n /** LIKE-style needle. Wrapped in `%…%` automatically; `_` and `%`\n * inside the pattern are still SQL LIKE wildcards (matches the\n * `searchTasks` convention in src/tasks.ts). Empty / whitespace-\n * only patterns throw — the CLI is the canonical caller and\n * enforces it via UsageError before we get here, but the SDK\n * guards it too so direct programmatic callers don't accidentally\n * match every row. */\n pattern: string;\n /** Restrict to one archive label; undefined = search every\n * archive. Throws ArchiveNotFoundError on miss. */\n label?: string;\n /** Cap on hits returned. Default 50; values below 1 fall back to\n * the default. There is no `--all` escape hatch — for unbounded\n * exports use `mu sql`. */\n limit?: number;\n}\n\n/** Default + cap for searchArchives. Mirrors the `--limit 50` shape\n * used elsewhere; deliberately small so an interactive search\n * doesn't dump thousands of rows by default. */\nconst SEARCH_DEFAULT_LIMIT = 50;\n/** Width of the snippet window (chars before+after the match). 120 is\n * the contract from the design note: enough context to disambiguate\n * matches in a table without wrapping a typical 80-col terminal. */\nconst SNIPPET_WIDTH = 120;\n\n/** Build a ~SNIPPET_WIDTH-char snippet centered on the first\n * case-insensitive occurrence of `needle` in `haystack`. Adds an\n * ellipsis prefix/suffix when we trimmed; preserves original casing\n * in the returned slice. SQL LIKE wildcards (`%`, `_`) in `needle`\n * are stripped before the match-locating step (we don't try to\n * reproduce LIKE-pattern semantics here — a glob like `foo%bar`\n * centers on \"foo\"). Pure helper; no DB access. */\nfunction snippetAround(haystack: string, needle: string): string {\n const literal = needle.replace(/[%_]/g, \"\");\n if (literal.length === 0) {\n // Pattern is just wildcards — return a head slice so the row\n // isn't blank in the table.\n return haystack.length <= SNIPPET_WIDTH ? haystack : `${haystack.slice(0, SNIPPET_WIDTH - 1)}…`;\n }\n const idx = haystack.toLowerCase().indexOf(literal.toLowerCase());\n if (idx < 0) {\n // Match was via SQL LIKE (e.g. `_` wildcard) but the literal\n // isn't present. Fall back to a head slice rather than throw.\n return haystack.length <= SNIPPET_WIDTH ? haystack : `${haystack.slice(0, SNIPPET_WIDTH - 1)}…`;\n }\n const half = Math.floor((SNIPPET_WIDTH - literal.length) / 2);\n const start = Math.max(0, idx - half);\n const end = Math.min(haystack.length, start + SNIPPET_WIDTH);\n const head = start > 0 ? \"…\" : \"\";\n const tail = end < haystack.length ? \"…\" : \"\";\n return `${head}${haystack.slice(start, end)}${tail}`;\n}\n\n/**\n * LIKE-search archived task titles AND archived note content. The\n * pattern is bound as a SQL parameter (never concatenated): an\n * archive label like `'); DROP TABLE archives; --` round-trips\n * through `?` without touching the DDL surface.\n *\n * Behaviour:\n * - One row per (archive, source_workstream, original_local_id)\n * pair. When a task matches via BOTH title and note, the title\n * row wins (matchKind='title'); only note matches stand on\n * their own as matchKind='note'.\n * - With `opts.label`, restricts to that archive. Resolves the\n * label up-front via the helper; throws ArchiveNotFoundError\n * on miss.\n * - Results sorted by (archive label, source workstream,\n * original_local_id) — the same order `mu archive show` uses,\n * so a search hit lines up with the show output.\n * - `limit` defaults to 50 and caps the result set. There is no\n * unbounded mode (use `mu sql` for raw extracts).\n */\nexport function searchArchives(db: Db, opts: SearchArchivesOptions): ArchiveSearchHit[] {\n const trimmed = opts.pattern.trim();\n if (trimmed.length === 0) {\n // Defensive guard for direct SDK callers; the CLI throws\n // UsageError(\"search pattern is required\") before we get here.\n throw new Error(\"searchArchives: pattern must be non-empty\");\n }\n const like = `%${trimmed}%`;\n const limit =\n opts.limit !== undefined && opts.limit > 0 ? Math.floor(opts.limit) : SEARCH_DEFAULT_LIMIT;\n\n // archiveId resolves up-front so a missing label fails loudly\n // before we run a (potentially expensive) full-table scan.\n let archiveFilterSql = \"\";\n const archiveFilterParams: unknown[] = [];\n if (opts.label !== undefined) {\n // getArchive throws ArchiveNotFoundError on miss — the contract.\n const archive = getArchive(db, opts.label);\n archiveFilterSql = \" AND a.id = ?\";\n archiveFilterParams.push(archive.id);\n }\n\n // Title hits: one row per archived_tasks row whose title matches.\n // The pattern is a parameter, never a string concat.\n const titleRows = db\n .prepare(\n `SELECT a.label AS archive_label,\n t.source_workstream AS source_workstream,\n t.original_local_id AS original_local_id,\n t.title AS title\n FROM archived_tasks t\n JOIN archives a ON a.id = t.archive_id\n WHERE LOWER(t.title) LIKE LOWER(?)${archiveFilterSql}`,\n )\n .all(like, ...archiveFilterParams) as {\n archive_label: string;\n source_workstream: string;\n original_local_id: string;\n title: string;\n }[];\n\n // Note hits: one row per archived_notes row that matches. We need\n // both the note content (for the snippet) AND the parent task's\n // title (for the table). DISTINCT-on-task is enforced in the JS\n // dedup step below — SQL DISTINCT here would only help if we\n // collapsed before reading n.content, losing the snippet source.\n const noteRows = db\n .prepare(\n `SELECT a.label AS archive_label,\n t.source_workstream AS source_workstream,\n t.original_local_id AS original_local_id,\n t.title AS title,\n n.content AS content\n FROM archived_notes n\n JOIN archived_tasks t ON t.id = n.archived_task_id\n JOIN archives a ON a.id = t.archive_id\n WHERE LOWER(n.content) LIKE LOWER(?)${archiveFilterSql}\n ORDER BY n.id`,\n )\n .all(like, ...archiveFilterParams) as {\n archive_label: string;\n source_workstream: string;\n original_local_id: string;\n title: string;\n content: string;\n }[];\n\n // Merge: title matches win over note matches for the same task.\n // Key on (archive label, source ws, original_local_id) — the\n // archive-side composite identity.\n const seen = new Set<string>();\n const hits: ArchiveSearchHit[] = [];\n for (const r of titleRows) {\n const key = `${r.archive_label}\\u0000${r.source_workstream}\\u0000${r.original_local_id}`;\n seen.add(key);\n hits.push({\n archiveLabel: r.archive_label,\n sourceWorkstream: r.source_workstream,\n originalLocalId: r.original_local_id,\n title: r.title,\n matchKind: \"title\",\n matchSnippet: snippetAround(r.title, trimmed),\n });\n }\n for (const r of noteRows) {\n const key = `${r.archive_label}\\u0000${r.source_workstream}\\u0000${r.original_local_id}`;\n if (seen.has(key)) continue;\n seen.add(key);\n hits.push({\n archiveLabel: r.archive_label,\n sourceWorkstream: r.source_workstream,\n originalLocalId: r.original_local_id,\n title: r.title,\n matchKind: \"note\",\n matchSnippet: snippetAround(r.content, trimmed),\n });\n }\n\n hits.sort((a, b) => {\n if (a.archiveLabel !== b.archiveLabel) return a.archiveLabel < b.archiveLabel ? -1 : 1;\n if (a.sourceWorkstream !== b.sourceWorkstream)\n return a.sourceWorkstream < b.sourceWorkstream ? -1 : 1;\n if (a.originalLocalId !== b.originalLocalId)\n return a.originalLocalId < b.originalLocalId ? -1 : 1;\n return 0;\n });\n return hits.slice(0, limit);\n}\n\nexport function listArchivedTasks(\n db: Db,\n label: string,\n opts: ListArchivedTasksOptions = {},\n): ArchivedTaskRow[] {\n const archiveId = resolveArchiveId(db, label);\n const conditions: string[] = [\"t.archive_id = ?\"];\n const params: unknown[] = [archiveId];\n if (opts.sourceWorkstream !== undefined) {\n conditions.push(\"t.source_workstream = ?\");\n params.push(opts.sourceWorkstream);\n }\n const where = conditions.join(\" AND \");\n const rows = db\n .prepare(\n `SELECT t.id AS id,\n a.label AS archive_label,\n t.source_workstream AS source_workstream,\n t.original_local_id AS original_local_id,\n t.title AS title,\n t.status AS status,\n t.impact AS impact,\n t.effort_days AS effort_days,\n t.owner_name AS owner_name,\n t.archived_at_status AS archived_at_status,\n t.archived_at AS archived_at,\n t.original_created_at AS original_created_at,\n t.original_updated_at AS original_updated_at\n FROM archived_tasks t\n JOIN archives a ON a.id = t.archive_id\n WHERE ${where}\n ORDER BY t.source_workstream ASC, t.original_local_id ASC`,\n )\n .all(...params) as RawArchivedTaskRow[];\n return rows.map(rowFromArchivedTask);\n}\n","// mu — VCS workspace abstraction.\n//\n// Tier-A goal from the \"make mu fully functional\" review: each agent\n// gets its own working copy so two agents in the same project don't\n// trample each other's edits.\n//\n// Four backends, deliberately concrete (no generic params, no\n// anticipatory abstraction) so the file stays small and a prior\n// internal LLM-runtime's `RunContext` cautionary tale doesn't apply:\n//\n// jj — `jj workspace add` (most isolated, native to mu's design)\n// sl — `sl share / sl up` (sapling-native shared store)\n// git — `git worktree add`\n// none — `cp -a` snapshot (heavy fallback for non-VCS dirs)\n//\n// Detection precedence: jj > sl > git > none. The dispatcher tries\n// each backend's `detect()` in that order; first hit owns the dir.\n//\n// On-disk layout: `<state-dir>/workspaces/<workstream>/<agent>/`.\n// State dir defaults to `~/.local/state/mu/` (overridable via\n// `$MU_STATE_DIR`, same as the DB).\n\nimport { execFile } from \"node:child_process\";\nimport { existsSync, rmSync } from \"node:fs\";\nimport { mkdir, stat } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { promisify } from \"node:util\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\n\nconst exec = promisify(execFile);\n\nexport type VcsBackendName = \"jj\" | \"sl\" | \"git\" | \"none\";\n\n// ─── Refresh / rebase result + typed errors ──────────────────────────\n//\n// rebaseTo is the backend-side of `mu workspace refresh`. The errors\n// live in this module (not src/workspace.ts) because they're thrown\n// from inside the backend impls; workspace.ts already imports vcs.ts,\n// so colocating avoids a circular import. handle.ts imports them by\n// name to map exit codes (4 for dirty / vcs-required, 5 for conflict).\n\nexport interface RebaseResult {\n /** The ref the workspace was actually rebased onto (resolved\n * symbolic-or-revset → concrete name). For git that is the\n * resolveGitMainRef() symbolic ref; for jj/sl it's the literal\n * `trunk()` revset (or whatever the operator passed via fromRef). */\n fromRef: string;\n /** Commit subjects (or descriptions) that got replayed, oldest-first.\n * Empty when the workspace was already at fromRef (no-op). */\n replayed: string[];\n /** Files / commits that conflicted during the rebase. Always\n * empty for a successful rebase — a non-empty conflicts list\n * means we threw WorkspaceConflictError before returning. The\n * field exists so the error's serialised payload can carry it. */\n conflicts: string[];\n}\n\nexport interface CommitSummary {\n /** Full commit / change id. */\n sha: string;\n /** First-line description / subject. */\n subject: string;\n /** Remainder of the commit message (may be empty). */\n body: string;\n /** ISO-8601 author / commit timestamp. */\n authorDate: string;\n}\n\n/**\n * Thrown by `rebaseTo` / `commitsSinceBase` on the `none` backend\n * (cp -a snapshots have no notion of a rebase target / fork point).\n * Maps to exit code 4.\n */\nexport class WorkspaceVcsRequiredError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceVcsRequiredError\";\n constructor(\n public readonly verb: string,\n public readonly workspacePath: string,\n ) {\n super(\n `vcs none: \\`mu workspace ${verb}\\` requires a real VCS (jj/sl/git); ${workspacePath} is a cp -a snapshot`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Free the snapshot and re-create with a real VCS backend\",\n command: \"mu workspace free <agent> && mu workspace create <agent> --backend <jj|sl|git>\",\n },\n ];\n }\n}\n\n/**\n * Thrown by `rebaseTo` when the workspace has uncommitted changes\n * the rebase would clobber. Carries the dirty file list so the operator\n * can decide between commit/stash/--force. Maps to exit code 4.\n */\nexport class WorkspaceDirtyError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceDirtyError\";\n constructor(\n public readonly workspacePath: string,\n public readonly files: readonly string[],\n ) {\n super(\n `workspace dirty (${files.length} uncommitted file(s)): ${workspacePath}; refusing to rebase`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Inspect the dirty files\",\n command: `(cd ${this.workspacePath} && git status -s) # or jj st / sl st`,\n },\n {\n intent: \"Commit them first, then retry refresh\",\n command: `(cd ${this.workspacePath} && git add -A && git commit -m WIP)`,\n },\n {\n intent: \"Or stash them first (git only)\",\n command: `(cd ${this.workspacePath} && git stash)`,\n },\n ];\n }\n}\n\n/**\n * Thrown by `rebaseTo` when the rebase produced conflicts the\n * operator must resolve manually. Carries the conflicting paths.\n * Maps to exit code 5.\n */\nexport class WorkspaceConflictError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceConflictError\";\n constructor(\n public readonly workspacePath: string,\n public readonly fromRef: string,\n public readonly conflicts: readonly string[],\n ) {\n super(`rebase onto ${fromRef} produced ${conflicts.length} conflict(s): ${workspacePath}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"cd into the workspace and resolve\",\n command: `cd ${this.workspacePath} # then resolve & commit; or: git rebase --abort / jj abandon / sl rebase --abort`,\n },\n ];\n }\n}\n\nexport interface CreateWorkspaceOptions {\n /** The repository being branched from. Absolute path. */\n projectRoot: string;\n /** Where to place the new workspace. Absolute path; must NOT exist. */\n workspacePath: string;\n /** Optional commit / branch / changeset id to base off. Backend-specific:\n * git uses it as a `git worktree add`'s ref, jj as a revset, sl as a\n * rev. Undefined = current head. */\n parentRef?: string;\n}\n\nexport interface CreateWorkspaceResult {\n /** The actual ref the workspace points at (resolved to a stable id\n * when possible). Stored on the row; useful for `mu workspace list`\n * and for `--commit` flows. May be null for backends that don't\n * expose a meaningful parent (e.g. `none`). */\n parentRef: string | null;\n}\n\nexport interface FreeWorkspaceOptions {\n workspacePath: string;\n /** If true, attempt to commit any pending changes BEFORE removal.\n * Backend-specific: jj auto-commits via `jj describe + jj new`, git\n * needs an explicit commit on the worktree, sl needs `sl commit`,\n * none has nothing to commit. If pending changes exist and `commit`\n * is false, the on-disk directory still gets removed and changes are\n * lost \\u2014 the verb prints a clear warning. */\n commit: boolean;\n}\n\nexport interface FreeWorkspaceResult {\n /** The commit id that captured the pending changes, when `commit` was\n * true and there was something to commit. Otherwise undefined. */\n committedRef?: string;\n /** True iff the on-disk path was actually removed (vs. already gone). */\n removed: boolean;\n}\n\nexport interface VcsBackend {\n readonly name: VcsBackendName;\n\n /** True iff this backend should handle `projectRoot`. Implementations\n * check for the relevant marker dir (`.jj`, `.sl`, `.git`); `none`\n * always returns true and is consulted last. */\n detect(projectRoot: string): Promise<boolean>;\n\n createWorkspace(opts: CreateWorkspaceOptions): Promise<CreateWorkspaceResult>;\n\n freeWorkspace(opts: FreeWorkspaceOptions): Promise<FreeWorkspaceResult>;\n\n /**\n * Count commits that the project's default branch (\"main\") has but\n * `ref` does not — i.e. how many commits `ref` is BEHIND main.\n *\n * Used by `mu workspace list` and `mu state` to surface staleness\n * (bug_workspace_stale_parent_silent_drift). Cheap, pure-observation:\n * NO automatic fetch. We compare against whatever main resolves to in\n * the workspace's LOCAL refs cache. The user can `git fetch` (or\n * equivalent) themselves if they want a fresher number.\n *\n * Returns null when:\n * - main / trunk cannot be resolved (no origin/HEAD, no origin/main,\n * no origin/master, no trunk() bookmark, etc.)\n * - the underlying VCS command fails for any reason (detached worktree,\n * missing refs, the `none` backend which has no notion of \"main\")\n *\n * Callers treat null as \"unknown — render — — and don't warn\".\n */\n commitsBehind(workspacePath: string, ref: string): Promise<number | null>;\n\n /**\n * Rebase the workspace onto `fromRef` (or the backend's tracked\n * base when undefined: `origin/HEAD` for git, `trunk()` for jj/sl).\n * Returns the resolved ref + replayed commits + conflicts list.\n *\n * Backend-specific behaviour:\n * - git: refuses on dirty WC (WorkspaceDirtyError); fetches first;\n * `git rebase <ref>`. On conflict, aborts the rebase and throws\n * WorkspaceConflictError so the operator resolves manually.\n * - jj: always-snapshotted, so dirty is never an issue. After\n * `jj rebase -d <ref>` the conflict-set is queried via\n * `jj log -r 'conflict()'`. Conflicts surface as\n * WorkspaceConflictError without an abort (jj's conflict markers\n * persist as commits; the operator resolves in-place).\n * - sl: similar to jj. `sl rebase -d <ref>`; conflicts via\n * `sl resolve -l`. On dirty WC sl errors itself; we wrap that\n * into WorkspaceDirtyError.\n * - none: throws WorkspaceVcsRequiredError unconditionally.\n *\n * Surfaced by fb_workspace_recycle_verb: dogfood between waves\n * needed `close → free → spawn` to refresh a worker against new\n * main; that killed the worker's LLM context. `refresh` updates\n * the on-disk dir without touching the agent or pane.\n */\n rebaseTo(workspacePath: string, fromRef?: string): Promise<RebaseResult>;\n\n /**\n * List commits the workspace has on top of `baseRef`, oldest-first.\n * Used by `mu workspace commits` (fb_workspace_commits_verb) to\n * promote the dogfood-painful\n * cd $(mu workspace path X) && git log <base>..HEAD\n * incantation into a typed verb that knows the workspace's\n * parent_ref. The CommitSummary fields survive subjects/bodies with\n * embedded newlines (NUL-delimited record format on the wire).\n *\n * `none` throws WorkspaceVcsRequiredError. Returns `[]` when the\n * workspace is exactly at baseRef (no commits since fork). Throws\n * on backend command failure (unknown ref, missing repo).\n */\n commitsSinceBase(workspacePath: string, baseRef: string): Promise<CommitSummary[]>;\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────\n\nasync function isDir(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function ensureParent(path: string): Promise<void> {\n await mkdir(dirname(path), { recursive: true });\n}\n\nfunction rmDirSync(path: string): boolean {\n if (!existsSync(path)) return false;\n rmSync(path, { recursive: true, force: true });\n return true;\n}\n\n/**\n * Run a binary with args. Throws a typed Error on non-zero exit. Stdout\n * is returned trimmed; stderr is appended to the Error message.\n */\nasync function run(bin: string, args: readonly string[], cwd?: string): Promise<string> {\n try {\n const { stdout } = await exec(bin, [...args], { cwd, maxBuffer: 16 * 1024 * 1024 });\n return stdout.trim();\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`vcs ${bin} ${args.join(\" \")} failed: ${msg}`);\n }\n}\n\n// ─── none backend ────────────────────────────────────────────────────\n//\n// The fallback for projects that aren't under any VCS we recognise.\n// `cp -a` is heavy but correct; the workspace is a full snapshot.\n// Free deletes the snapshot. No commit semantics (no VCS to commit\n// against), so `--commit` is silently ignored.\n\nexport const noneBackend: VcsBackend = {\n name: \"none\",\n\n async detect(_projectRoot) {\n return true;\n },\n\n async createWorkspace(opts) {\n if (existsSync(opts.workspacePath)) {\n throw new Error(`vcs none: workspacePath already exists: ${opts.workspacePath}`);\n }\n await ensureParent(opts.workspacePath);\n // `cp -a` is GNU/BSD-portable for \"preserve everything, recursive\".\n await run(\"cp\", [\"-a\", `${opts.projectRoot}/.`, opts.workspacePath]);\n return { parentRef: null };\n },\n\n async freeWorkspace(opts) {\n const removed = rmDirSync(opts.workspacePath);\n return { removed };\n },\n\n // The `none` backend has no VCS to compare against — there's no\n // notion of \"main\" for a `cp -a` snapshot. Always null.\n async commitsBehind(_workspacePath, _ref) {\n return null;\n },\n\n // none has no upstream to rebase onto. Throw a typed error so the\n // CLI's handle() maps it to exit 4 with a clean Next: hint.\n async rebaseTo(workspacePath, _fromRef) {\n throw new WorkspaceVcsRequiredError(\"refresh\", workspacePath);\n },\n\n // none has no notion of a fork point either — a cp -a snapshot\n // doesn't track history. Same typed error as rebaseTo.\n async commitsSinceBase(workspacePath, _baseRef) {\n throw new WorkspaceVcsRequiredError(\"commits\", workspacePath);\n },\n};\n\n// ─── git backend ─────────────────────────────────────────────────────\n//\n// Uses `git worktree` so the new workspace shares the .git store with\n// the project root. Cheap (no copy), fast, and integrates with the rest\n// of the agent's git workflow (push, log, etc) trivially.\n\nexport const gitBackend: VcsBackend = {\n name: \"git\",\n\n async detect(projectRoot) {\n return isDir(join(projectRoot, \".git\"));\n },\n\n async createWorkspace(opts) {\n if (existsSync(opts.workspacePath)) {\n throw new Error(`vcs git: workspacePath already exists: ${opts.workspacePath}`);\n }\n await ensureParent(opts.workspacePath);\n // Defensive prune: if a previous workspace at the same path was\n // freed by `rm -rf` (or otherwise lost its dir without proper\n // teardown), git's worktree registry still points at it. Then\n // `git worktree add` fails with 'missing but already registered\n // worktree'. `git worktree prune` is idempotent and cheap; running\n // it BEFORE every add costs ~10ms and immunises us against the\n // mufeedback workspace_free_cleanup_leaves_git case.\n await run(\"git\", [\"worktree\", \"prune\"], opts.projectRoot).catch(() => {\n /* prune is best-effort; if it fails we'll get a clear error from `add` next */\n });\n // `git worktree add <path> [<ref>]`. Without a ref, the new worktree\n // checks out a detached HEAD at the project's current HEAD, which\n // matches the \"fresh per-agent workspace\" semantics we want. Use a\n // detached HEAD by default so two agents from the same parent ref\n // don't collide on a branch name.\n const args = [\"worktree\", \"add\", \"--detach\", opts.workspacePath];\n if (opts.parentRef) args.push(opts.parentRef);\n await run(\"git\", args, opts.projectRoot);\n // Resolve the actual SHA we ended up on for the parentRef record.\n const sha = await run(\"git\", [\"rev-parse\", \"HEAD\"], opts.workspacePath);\n return { parentRef: sha };\n },\n\n // Compute commits-behind as: count of commits reachable from main\n // but not from `ref`. Resolves \"main\" via origin/HEAD (the symbolic\n // ref the remote advertises), falling back to origin/main and then\n // origin/master. Returns null when none of those resolve, or when\n // the rev-list call fails (e.g. ref unknown locally).\n //\n // Pure observation: NO `git fetch`. The number is as fresh as the\n // last time the user (or some other process) updated the local\n // remote-tracking refs.\n async commitsBehind(workspacePath, ref) {\n if (!existsSync(workspacePath)) return null;\n const main = await resolveGitMainRef(workspacePath);\n if (main === undefined) return null;\n try {\n const out = await run(\"git\", [\"rev-list\", \"--count\", `${ref}..${main}`], workspacePath);\n const n = Number.parseInt(out.trim(), 10);\n return Number.isFinite(n) ? n : null;\n } catch {\n return null;\n }\n },\n\n // Rebase the worktree onto `fromRef` (default = origin/HEAD via\n // resolveGitMainRef). Refuses on a dirty WC; returns the replayed\n // commit subjects oldest-first; on conflict aborts the rebase and\n // throws WorkspaceConflictError so the operator never inherits a\n // half-rebased worktree from us.\n //\n // We DO `git fetch` first — otherwise the rebase target would only\n // be as fresh as the local refs cache, and the operator running\n // `mu workspace refresh` is explicitly asking for the latest. This\n // is the one-and-only place mu fetches; commitsBehind() stays pure.\n async rebaseTo(workspacePath, fromRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs git: workspace path missing: ${workspacePath}`);\n }\n // Dirty-check first: refuse before any side effect.\n const dirtyFiles = await listGitDirtyFiles(workspacePath);\n if (dirtyFiles.length > 0) {\n throw new WorkspaceDirtyError(workspacePath, dirtyFiles);\n }\n // Best-effort fetch so the resolved main ref is fresh. Failure is\n // ignored (offline / no remote) — we still attempt the rebase\n // against whatever the local refs cache holds.\n await run(\"git\", [\"fetch\", \"--quiet\"], workspacePath).catch(() => {});\n let resolvedRef: string;\n if (fromRef !== undefined) {\n resolvedRef = fromRef;\n } else {\n const main = await resolveGitMainRef(workspacePath);\n if (main === undefined) {\n throw new Error(\n `vcs git: cannot resolve default branch (no origin/HEAD, origin/main, or origin/master) in ${workspacePath}; pass --from <ref>`,\n );\n }\n resolvedRef = main;\n }\n // Capture the pre-rebase HEAD so we can compute `replayed` as the\n // commits that ended up on top of resolvedRef after the rebase.\n const preHead = await run(\"git\", [\"rev-parse\", \"HEAD\"], workspacePath);\n try {\n await run(\n \"git\",\n [\"-c\", \"user.email=mu@local\", \"-c\", \"user.name=mu\", \"rebase\", resolvedRef],\n workspacePath,\n );\n } catch (err) {\n // Capture the conflicting paths BEFORE aborting (the abort\n // resets the index and the unmerged paths disappear).\n const conflicts = await listGitUnmergedPaths(workspacePath);\n await run(\"git\", [\"rebase\", \"--abort\"], workspacePath).catch(() => {});\n if (conflicts.length > 0) {\n throw new WorkspaceConflictError(workspacePath, resolvedRef, conflicts);\n }\n // Non-conflict rebase failure (e.g. unknown ref). Surface it raw.\n throw err;\n }\n // Replayed commits = the new HEAD..resolvedRef gap, but oldest-first\n // and limited to what was actually replayed from preHead. We use\n // `git log --reverse <merge-base>..HEAD` where the merge base is\n // computed against resolvedRef, since after a successful rebase\n // HEAD's history above the base IS the replayed set.\n const mergeBase = await run(\"git\", [\"merge-base\", \"HEAD\", resolvedRef], workspacePath).catch(\n () => preHead,\n );\n const logOut = await run(\n \"git\",\n [\"log\", \"--reverse\", \"--format=%s\", `${mergeBase}..HEAD`],\n workspacePath,\n );\n const replayed = logOut.length === 0 ? [] : logOut.split(\"\\n\");\n return { fromRef: resolvedRef, replayed, conflicts: [] };\n },\n\n // List commits in (baseRef..HEAD), oldest-first. The format string\n // packs four NUL-delimited fields per record, then a record\n // separator '\\x1e' (RECORD-SEPARATOR control char) so subjects /\n // bodies with embedded newlines or NULs survive parsing. We don't\n // use a JSON template because git's `--format` is field-oriented;\n // %x00 (NUL) and %x1e (RS) are git's portable escape sequences.\n async commitsSinceBase(workspacePath, baseRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs git: workspace path missing: ${workspacePath}`);\n }\n const out = await run(\n \"git\",\n [\"log\", \"--reverse\", \"-z\", \"--format=%H%x00%s%x00%b%x00%aI\", `${baseRef}..HEAD`],\n workspacePath,\n );\n if (out.length === 0) return [];\n // -z makes git use NUL as the record separator; combined with our\n // %x00 field separators each commit looks like:\n // <sha>\\0<subject>\\0<body>\\0<authorDate>\\0\n // i.e. four fields followed by the record-terminating NUL git\n // injects with -z. Splitting on NUL leaves us with 4N fields plus\n // a trailing empty string we drop.\n const fields = out.split(\"\\x00\");\n const records: CommitSummary[] = [];\n for (let i = 0; i + 3 < fields.length; i += 4) {\n const sha = fields[i] ?? \"\";\n const subject = fields[i + 1] ?? \"\";\n const body = fields[i + 2] ?? \"\";\n const authorDate = fields[i + 3] ?? \"\";\n if (sha.length === 0) continue;\n records.push({ sha, subject, body, authorDate });\n }\n return records;\n },\n\n async freeWorkspace(opts) {\n // Disk-missing case: a previous caller (or the user) ran `rm -rf`\n // out from under us, but the git worktree registry STILL has an\n // entry pointing here. Without a prune, the next `git worktree\n // add` at this path errors out (the mufeedback case). We can't\n // reach the project root via the workspace itself (the .git\n // pointer file is gone with the dir), but `worktree prune` runs\n // from inside any git repo and reaps every dead worktree. We\n // can't reliably guess WHICH project root, so log it as a hint\n // in the result rather than running prune ourselves; the spawn\n // path's defensive prune (above) will clean it on next use.\n if (!existsSync(opts.workspacePath)) {\n return { removed: false };\n }\n let committedRef: string | undefined;\n if (opts.commit) {\n // Commit only if there's anything to commit. `git diff --quiet`\n // returns 0 when clean, 1 when dirty.\n const dirty = await isGitDirty(opts.workspacePath);\n if (dirty) {\n await run(\"git\", [\"add\", \"-A\"], opts.workspacePath);\n await run(\n \"git\",\n [\n \"-c\",\n \"user.email=mu@local\",\n \"-c\",\n \"user.name=mu\",\n \"commit\",\n \"-m\",\n \"mu workspace free auto-commit\",\n ],\n opts.workspacePath,\n );\n committedRef = await run(\"git\", [\"rev-parse\", \"HEAD\"], opts.workspacePath);\n }\n }\n // Tear down: git worktree remove --force <path> cleans both the\n // on-disk directory AND the git/worktrees/<name>/ admin entry. We\n // can't easily run it from the project root (we don't store it on\n // the workspace row), but git accepts the worktree's own path from\n // anywhere with --force, so we resolve a usable cwd via git's own\n // pointer back to the project's .git dir.\n const projectRoot = await resolveGitProjectRoot(opts.workspacePath);\n if (projectRoot) {\n await run(\"git\", [\"worktree\", \"remove\", \"--force\", opts.workspacePath], projectRoot);\n } else {\n // Lost the link — just rm the directory. git's admin entry will\n // be cleaned by the next `git worktree prune` invocation.\n rmDirSync(opts.workspacePath);\n }\n const result: FreeWorkspaceResult = { removed: true };\n if (committedRef !== undefined) result.committedRef = committedRef;\n return result;\n },\n};\n\n/**\n * Resolve the workspace's notion of \"main\". Tries, in order:\n * 1. `origin/HEAD` — the symbolic ref the remote published\n * (e.g. \"refs/remotes/origin/main\"). The most accurate signal.\n * 2. `refs/remotes/origin/main` — the convention.\n * 3. `refs/remotes/origin/master` — pre-rename convention.\n * Returns the resolved ref string (suitable for `git rev-list`) or\n * undefined if none of the three resolve.\n */\nasync function resolveGitMainRef(workspacePath: string): Promise<string | undefined> {\n for (const candidate of [\n \"refs/remotes/origin/HEAD\",\n \"refs/remotes/origin/main\",\n \"refs/remotes/origin/master\",\n ]) {\n try {\n await exec(\"git\", [\"rev-parse\", \"--verify\", \"--quiet\", candidate], { cwd: workspacePath });\n return candidate;\n } catch {\n /* try next */\n }\n }\n return undefined;\n}\n\n/**\n * Return the list of dirty paths (working-tree modifications + staged\n * + untracked-not-ignored), one entry per file. Empty when clean.\n * Used by `rebaseTo` to refuse with WorkspaceDirtyError carrying the\n * file list — one error message tells the operator both that the\n * workspace is dirty and which files to deal with.\n */\nasync function listGitDirtyFiles(workspacePath: string): Promise<string[]> {\n // `git status --porcelain` prints one line per changed file with\n // a 2-char status prefix; trim the prefix to get the path. Untracked\n // files appear as `?? path` and are included by default — same\n // strictness as isGitDirty (which used three independent commands).\n const { stdout } = await exec(\"git\", [\"status\", \"--porcelain\"], { cwd: workspacePath });\n const lines = stdout.split(\"\\n\").filter((l) => l.length > 0);\n return lines.map((l) => l.slice(3));\n}\n\n/**\n * Return the unmerged paths after a failed `git rebase`. Used to\n * enrich WorkspaceConflictError before we abort. Empty list means the\n * rebase failed for a non-conflict reason (unknown ref, etc).\n */\nasync function listGitUnmergedPaths(workspacePath: string): Promise<string[]> {\n try {\n const out = await run(\"git\", [\"diff\", \"--name-only\", \"--diff-filter=U\"], workspacePath);\n return out.length === 0 ? [] : out.split(\"\\n\").filter((l) => l.length > 0);\n } catch {\n return [];\n }\n}\n\nasync function isGitDirty(workspacePath: string): Promise<boolean> {\n // Three independent checks: working-tree changes, staged changes,\n // untracked-but-not-ignored files. Any of the three → dirty.\n try {\n await exec(\"git\", [\"diff\", \"--quiet\"], { cwd: workspacePath });\n } catch {\n return true;\n }\n try {\n await exec(\"git\", [\"diff\", \"--cached\", \"--quiet\"], { cwd: workspacePath });\n } catch {\n return true;\n }\n const { stdout } = await exec(\"git\", [\"ls-files\", \"--others\", \"--exclude-standard\"], {\n cwd: workspacePath,\n });\n return stdout.trim().length > 0;\n}\n\n/**\n * Resolve the project root that owns this worktree. Returns undefined\n * if the link is broken (e.g. project root deleted). git rev-parse\n * --git-common-dir gives us the parent project's .git dir; the worktree's\n * project root is its parent.\n */\nasync function resolveGitProjectRoot(workspacePath: string): Promise<string | undefined> {\n try {\n const { stdout } = await exec(\n \"git\",\n [\"rev-parse\", \"--path-format=absolute\", \"--git-common-dir\"],\n { cwd: workspacePath },\n );\n return resolve(stdout.trim(), \"..\");\n } catch {\n return undefined;\n }\n}\n\n// ─── jj backend ─────────────────────────────────────────────────────────────\n//\n// `jj workspace add --name <name> <path>` shares the .jj/repo store\n// while giving each agent its own working copy. Workspaces are named;\n// we use basename(workspacePath) which (per our on-disk layout) is the\n// agent name.\n//\n// Free is two-step: `jj workspace forget <name>` from the workspace\n// itself unregisters; then we rm the dir since jj leaves the files\n// behind.\n//\n// --commit semantics for jj: jj's working copy is always automatically\n// snapshotted, so \"commit\" is really \"capture the current change_id\n// as the result.\" Nothing is ever lost; jj keeps all operations in\n// its op log indefinitely. We additionally call `jj describe` to set\n// a description IF the current commit's description is empty so the\n// captured ref is human-discoverable.\n\nexport const jjBackend: VcsBackend = {\n name: \"jj\",\n\n async detect(projectRoot) {\n return isDir(join(projectRoot, \".jj\"));\n },\n\n async createWorkspace(opts) {\n if (existsSync(opts.workspacePath)) {\n throw new Error(`vcs jj: workspacePath already exists: ${opts.workspacePath}`);\n }\n await ensureParent(opts.workspacePath);\n const name = jjWorkspaceName(opts.workspacePath);\n const args = [\"workspace\", \"add\", \"--name\", name];\n if (opts.parentRef) args.push(\"--revision\", opts.parentRef);\n args.push(opts.workspacePath);\n await run(\"jj\", args, opts.projectRoot);\n const commitId = await jjCommitId(opts.workspacePath);\n return { parentRef: commitId };\n },\n\n async freeWorkspace(opts) {\n if (!existsSync(opts.workspacePath)) {\n return { removed: false };\n }\n const name = jjWorkspaceName(opts.workspacePath);\n let committedRef: string | undefined;\n if (opts.commit) {\n const desc = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n \"@\",\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--template\",\n \"description\",\n ],\n opts.workspacePath,\n );\n if (desc.trim().length === 0) {\n await run(\"jj\", [\"describe\", \"-m\", \"mu workspace free auto-commit\"], opts.workspacePath);\n }\n committedRef = await jjCommitId(opts.workspacePath);\n }\n // `jj workspace forget` works from inside the workspace itself.\n // jj prints a hint about the working copy becoming orphaned;\n // we resolve that immediately by rm-ing the dir.\n await run(\"jj\", [\"workspace\", \"forget\", name], opts.workspacePath);\n rmDirSync(opts.workspacePath);\n const result: FreeWorkspaceResult = { removed: true };\n if (committedRef !== undefined) result.committedRef = committedRef;\n return result;\n },\n\n // Compute commits-behind via jj's `trunk()` revset, which resolves\n // to the project's configured trunk (default-branch heuristic).\n // Returns null when trunk() is unresolvable (e.g. fresh repo with\n // no configured trunk) or when the log call fails.\n //\n // Pure observation: NO `jj git fetch`.\n async commitsBehind(workspacePath, ref) {\n if (!existsSync(workspacePath)) return null;\n try {\n // `<ref>..trunk()` is the set of commits reachable from trunk\n // but not from ref — exactly the staleness number. Template `\"x\\n\"`\n // gives one line per commit, which we count.\n const out = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n `${ref}..trunk()`,\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--template\",\n '\"x\\\\n\"',\n ],\n workspacePath,\n );\n if (out.length === 0) return 0;\n return out.split(\"\\n\").filter((l) => l.length > 0).length;\n } catch {\n return null;\n }\n },\n\n // Rebase the workspace's @ onto `fromRef` (default = `trunk()`).\n // jj is always-snapshotted so dirty WC is never an issue — the auto-\n // snapshot becomes part of the rebase. After the rebase we query\n // `conflict()` to surface any commits that ended up conflicted; jj\n // doesn't auto-abort on conflicts (they materialise as commits with\n // conflict markers), so the workspace is left in a state the\n // operator can resolve in-place.\n async rebaseTo(workspacePath, fromRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs jj: workspace path missing: ${workspacePath}`);\n }\n const target = fromRef ?? \"trunk()\";\n // Snapshot the pre-rebase change_id so we can compute replayed\n // descriptions afterwards. `@` is the working-copy commit.\n const preRev = await run(\n \"jj\",\n [\"log\", \"-r\", \"@\", \"--no-graph\", \"--no-pager\", \"--color\", \"never\", \"--template\", \"change_id\"],\n workspacePath,\n );\n await run(\"jj\", [\"rebase\", \"-d\", target], workspacePath);\n // Replayed = descriptions of commits in (target..@), oldest-first.\n // Template prints `description ++ \"\\n\\x00\"` so multi-line descs\n // survive splitting; we keep the first non-empty line as subject.\n const replayedRaw = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n `${target}..@`,\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--reversed\",\n \"--template\",\n 'description.first_line() ++ \"\\\\n\"',\n ],\n workspacePath,\n ).catch(() => \"\");\n const replayed = replayedRaw\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n // Conflict surface: list change_ids of commits in the rebased\n // range that are conflicted. Empty = clean rebase.\n const conflictRaw = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n `(${target}..@) & conflict()`,\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--template\",\n 'change_id.short() ++ \"\\\\n\"',\n ],\n workspacePath,\n ).catch(() => \"\");\n const conflicts = conflictRaw\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n if (conflicts.length > 0) {\n throw new WorkspaceConflictError(workspacePath, target, conflicts);\n }\n // Use preRev so future-resolution of @ at call time is irrelevant.\n void preRev;\n return { fromRef: target, replayed, conflicts: [] };\n },\n\n // List jj commits in (baseRef..@), oldest-first. jj's templating\n // gives us per-field strings; we glue them with NUL field-separators\n // and \\x1e record-separators so multi-line descriptions/bodies\n // round-trip cleanly. The author timestamp template is\n // `author.timestamp().format(\"%Y-%m-%dT%H:%M:%S%:z\")` which is\n // ISO-8601 (matches git's --aiso strict / --aI).\n async commitsSinceBase(workspacePath, baseRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs jj: workspace path missing: ${workspacePath}`);\n }\n const out = await run(\n \"jj\",\n [\n \"log\",\n \"-r\",\n `${baseRef}..@`,\n \"--no-graph\",\n \"--no-pager\",\n \"--color\",\n \"never\",\n \"--reversed\",\n \"--template\",\n // commit_id\\0subject\\0body\\0iso-date\\x1e per record. The\n // outer string is jj-template syntax; \\\\x00 / \\\\x1e are\n // jj-template literal escape sequences. body = full\n // description (jj has no portable \"rest of message\" template,\n // and a small duplication of the first line beats a brittle\n // string-slicing template that breaks across jj versions).\n 'commit_id ++ \"\\\\x00\" ++ description.first_line() ++ \"\\\\x00\" ++ description ++ \"\\\\x00\" ++ author.timestamp().format(\"%Y-%m-%dT%H:%M:%S%:z\") ++ \"\\\\x1e\"',\n ],\n workspacePath,\n );\n return parseNulRecords(out);\n },\n};\n\n/**\n * Parse the NUL-field / \\x1e-record format used by the jj/sl\n * commitsSinceBase impls. Each record is `sha\\0subject\\0body\\0date`\n * terminated by \\x1e. Empty input → [].\n */\nfunction parseNulRecords(raw: string): CommitSummary[] {\n if (raw.length === 0) return [];\n const records: CommitSummary[] = [];\n for (const rec of raw.split(\"\\x1e\")) {\n if (rec.length === 0) continue;\n const fields = rec.split(\"\\x00\");\n const sha = fields[0] ?? \"\";\n if (sha.length === 0) continue;\n records.push({\n sha,\n subject: fields[1] ?? \"\",\n body: fields[2] ?? \"\",\n authorDate: fields[3] ?? \"\",\n });\n }\n return records;\n}\n\nfunction jjWorkspaceName(workspacePath: string): string {\n // basename of /foo/bar/worker-1 → worker-1\n return workspacePath.replace(/\\/+$/, \"\").split(\"/\").pop() ?? workspacePath;\n}\n\nasync function jjCommitId(workspacePath: string): Promise<string> {\n return run(\n \"jj\",\n [\"log\", \"-r\", \"@\", \"--no-graph\", \"--no-pager\", \"--color\", \"never\", \"--template\", \"commit_id\"],\n workspacePath,\n );\n}\n\n// ─── sl backend (Sapling) ────────────────────────────────────────────────────\n//\n// `sl worktree` exists in Sapling but only for EdenFS-backed repos.\n// For portability we use `sl clone` instead, which works on any\n// sapling install. The trade-off is heavier (history copy) vs lighter\n// (shared store), but the workspace is fully isolated either way —\n// which is what we care about. EdenFS-specific worktree optimization\n// can layer on later if anyone hits the friction.\n//\n// Free is just rm -rf: sapling has no formal \"unclone\" because each\n// clone is a self-contained repo.\n\nexport const slBackend: VcsBackend = {\n name: \"sl\",\n\n async detect(projectRoot) {\n // Sapling uses `.sl` in newer mode and `.hg` in mercurial-compat\n // mode. Both are valid sapling repos to `sl`. Accept either.\n return (await isDir(join(projectRoot, \".sl\"))) || (await isDir(join(projectRoot, \".hg\")));\n },\n\n async createWorkspace(opts) {\n if (existsSync(opts.workspacePath)) {\n throw new Error(`vcs sl: workspacePath already exists: ${opts.workspacePath}`);\n }\n await ensureParent(opts.workspacePath);\n const args = [\"clone\"];\n if (opts.parentRef) args.push(\"-r\", opts.parentRef);\n args.push(opts.projectRoot, opts.workspacePath);\n await run(\"sl\", args);\n const commitId = await slCommitId(opts.workspacePath);\n return { parentRef: commitId };\n },\n\n async freeWorkspace(opts) {\n if (!existsSync(opts.workspacePath)) {\n return { removed: false };\n }\n let committedRef: string | undefined;\n if (opts.commit && (await slIsDirty(opts.workspacePath))) {\n // sl commit -A: stage all (including untracked), commit. Exits\n // non-zero if nothing changed, but we just guarded above.\n await run(\n \"sl\",\n [\n \"--config\",\n \"ui.username=mu <mu@local>\",\n \"commit\",\n \"-A\",\n \"-m\",\n \"mu workspace free auto-commit\",\n ],\n opts.workspacePath,\n );\n committedRef = await slCommitId(opts.workspacePath);\n }\n rmDirSync(opts.workspacePath);\n const result: FreeWorkspaceResult = { removed: true };\n if (committedRef !== undefined) result.committedRef = committedRef;\n return result;\n },\n\n // Same shape as the jj impl: count commits in trunk() not reachable\n // from ref. Sapling's revset language is close enough to jj's that\n // the same idiom works. Returns null when trunk() is unresolvable\n // (fresh repo, missing remote bookmark, etc.) or the log fails.\n //\n // Pure observation: NO `sl pull`.\n async commitsBehind(workspacePath, ref) {\n if (!existsSync(workspacePath)) return null;\n try {\n const out = await run(\n \"sl\",\n [\"log\", \"-r\", `${ref}::trunk() - ${ref}`, \"--template\", \"x\\\\n\"],\n workspacePath,\n );\n if (out.length === 0) return 0;\n return out.split(\"\\n\").filter((l) => l.length > 0).length;\n } catch {\n return null;\n }\n },\n\n // Rebase the active draft chain onto `fromRef` (default = `trunk()`).\n // Sapling refuses on dirty WC by default — we pre-check and convert\n // its error into the typed WorkspaceDirtyError. Conflict surface\n // post-rebase via `sl resolve --list --tool=internal:dumpjson` is\n // brittle across versions, so we use the textual `sl resolve --list`\n // output and look for the U-prefixed lines (unresolved).\n async rebaseTo(workspacePath, fromRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs sl: workspace path missing: ${workspacePath}`);\n }\n const target = fromRef ?? \"trunk()\";\n const dirtyFiles = await listSlDirtyFiles(workspacePath);\n if (dirtyFiles.length > 0) {\n throw new WorkspaceDirtyError(workspacePath, dirtyFiles);\n }\n await run(\n \"sl\",\n [\"--config\", \"ui.username=mu <mu@local>\", \"rebase\", \"-d\", target],\n workspacePath,\n ).catch(() => {\n // Rebase failure is acceptable here — the conflict-listing call\n // below will tell us what happened. Bare exception loss is OK\n // since `sl resolve` is the source of truth on conflicts.\n });\n const conflicts = await listSlUnresolved(workspacePath);\n if (conflicts.length > 0) {\n // Best-effort abort so the workspace returns to a clean state\n // — mirrors the git impl's never-leave-half-rebased policy.\n await run(\"sl\", [\"rebase\", \"--abort\"], workspacePath).catch(() => {});\n throw new WorkspaceConflictError(workspacePath, target, conflicts);\n }\n // Replayed = log of `target..` post-rebase, oldest-first. Single-\n // line subjects via `{desc|firstline}`. Empty when nothing replayed.\n const replayedRaw = await run(\n \"sl\",\n [\"log\", \"-r\", `${target}::. - ${target}`, \"--template\", \"{desc|firstline}\\\\n\"],\n workspacePath,\n ).catch(() => \"\");\n const replayed = replayedRaw\n .split(\"\\n\")\n .map((l) => l.trim())\n .filter((l) => l.length > 0);\n return { fromRef: target, replayed, conflicts: [] };\n },\n\n // List sl commits in (baseRef..., minus baseRef itself), oldest-\n // first. Same NUL-field / \\x1e-record format as the jj impl;\n // sl's templating uses {field} braces (not jj's `++` operator) but\n // the field set is equivalent ({node}, {desc|firstline}, {desc},\n // {date|isodate}).\n async commitsSinceBase(workspacePath, baseRef) {\n if (!existsSync(workspacePath)) {\n throw new Error(`vcs sl: workspace path missing: ${workspacePath}`);\n }\n const out = await run(\n \"sl\",\n [\n \"log\",\n \"-r\",\n `${baseRef}::. - ${baseRef}`,\n \"--template\",\n \"{node}\\\\0{desc|firstline}\\\\0{desc}\\\\0{date|isodatesec}\\\\x1e\",\n ],\n workspacePath,\n );\n // sl emits oldest-last by default; reverse to oldest-first to match\n // the git/jj contract.\n return parseNulRecords(out).reverse();\n },\n};\n\n/**\n * List dirty paths for a sapling workspace. `sl status` prints one\n * line per changed file: `<status> <path>`. Empty = clean.\n */\nasync function listSlDirtyFiles(workspacePath: string): Promise<string[]> {\n const out = await run(\"sl\", [\"status\"], workspacePath);\n if (out.length === 0) return [];\n return out\n .split(\"\\n\")\n .filter((l) => l.length > 0)\n .map((l) => l.slice(2));\n}\n\n/**\n * List unresolved (conflicting) paths after an `sl rebase`. `sl resolve\n * --list` prints `<U|R> <path>` per file; U = unresolved.\n */\nasync function listSlUnresolved(workspacePath: string): Promise<string[]> {\n try {\n const out = await run(\"sl\", [\"resolve\", \"--list\"], workspacePath);\n if (out.length === 0) return [];\n return out\n .split(\"\\n\")\n .filter((l) => l.startsWith(\"U \"))\n .map((l) => l.slice(2));\n } catch {\n return [];\n }\n}\n\nasync function slCommitId(workspacePath: string): Promise<string> {\n return run(\"sl\", [\"log\", \"-r\", \".\", \"--template\", \"{node}\"], workspacePath);\n}\n\nasync function slIsDirty(workspacePath: string): Promise<boolean> {\n // sl status prints one line per changed file; empty stdout = clean.\n const out = await run(\"sl\", [\"status\"], workspacePath);\n return out.length > 0;\n}\n\n// ─── Dispatcher ──────────────────────────────────────────────────────\n\n/**\n * Detection precedence: jj > sl > git > none. The first backend whose\n * detect() returns true wins. `none` is always last.\n *\n * jj and sl are added in follow-up commits but this list is the\n * canonical order they'll prepend to.\n */\nconst BACKENDS: readonly VcsBackend[] = [jjBackend, slBackend, gitBackend, noneBackend];\n\n/** Return the backend that should handle projectRoot. Walks BACKENDS\n * in precedence order; never returns undefined because noneBackend\n * always claims. */\nexport async function detectBackend(projectRoot: string): Promise<VcsBackend> {\n for (const backend of BACKENDS) {\n if (await backend.detect(projectRoot)) return backend;\n }\n return noneBackend;\n}\n\n/** Look up a backend by name. Throws on unknown name. Used by\n * `mu workspace create --backend ...` to honour an explicit override. */\nexport function backendByName(name: VcsBackendName): VcsBackend {\n for (const backend of BACKENDS) {\n if (backend.name === name) return backend;\n }\n throw new Error(`unknown vcs backend: ${name}`);\n}\n","// mu — workspace verbs: per-agent isolated working copies on top of\n// the VcsBackend abstraction (src/vcs.ts).\n//\n// The schema (vcs_workspaces) plus this module give us:\n//\n// - createWorkspace(db, opts) → makes a fresh on-disk workspace via\n// the appropriate backend, records the row\n// - listWorkspaces(db, ws?) → registry rows (optionally filtered)\n// - freeWorkspace(db, agent, opts) → tears down the on-disk dir AND\n// removes the row (atomic-ish: dir teardown first, then DELETE)\n// - getWorkspaceForAgent(db, agent) → spawn integration uses this to\n// resolve a managed agent's cwd\n//\n// On-disk layout: <state-dir>/workspaces/<workstream>/<agent>/. Each\n// workspace lives under the same state dir as the DB so a single\n// `rm -rf ~/.local/state/mu` cleans everything.\n\nimport { existsSync, readdirSync, rmSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport { AgentNotFoundError } from \"./agents/errors.js\";\nimport { type Db, defaultStateDir, tryResolveWorkstreamId } from \"./db.js\";\nimport { emitEvent } from \"./logs.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\nimport { captureSnapshot } from \"./snapshots.js\";\nimport {\n type CommitSummary,\n type RebaseResult,\n type VcsBackend,\n type VcsBackendName,\n backendByName,\n detectBackend,\n} from \"./vcs.js\";\n\nexport interface WorkspaceRow {\n agentName: string;\n workstreamName: string;\n backend: VcsBackendName;\n path: string;\n parentRef: string | null;\n createdAt: string;\n /** How many commits the workspace's parent_ref is behind the project's\n * default branch HEAD, as of the last time the workspace's local refs\n * cache was updated. Undefined when not yet computed (the listWorkspaces\n * fast path leaves it unset; call decorateWithStaleness to populate).\n * Null when staleness was queried but cannot be computed (no main found,\n * none-backend, missing parent_ref, command failure). */\n commitsBehindMain?: number | null;\n}\n\ninterface RawWorkspaceRow {\n /** Joined from agents.name. */\n agent: string;\n /** Joined from workstreams.name. */\n workstream: string;\n backend: string;\n path: string;\n parent_ref: string | null;\n created_at: string;\n}\n\n// SELECT clause that joins vcs_workspaces back to operator-facing\n// agent + workstream names (v5 stores surrogate ids; the JS row shape\n// is operator-facing TEXT names).\nconst SELECT_WS_COLS = `\n ag.name AS agent,\n ws.name AS workstream,\n v.backend AS backend,\n v.path AS path,\n v.parent_ref AS parent_ref,\n v.created_at AS created_at\n`;\n\nconst WS_FROM_JOIN = `FROM vcs_workspaces v\n JOIN agents ag ON ag.id = v.agent_id\n JOIN workstreams ws ON ws.id = v.workstream_id`;\n\nfunction rowFromDb(row: RawWorkspaceRow): WorkspaceRow {\n return {\n agentName: row.agent,\n workstreamName: row.workstream,\n backend: row.backend as VcsBackendName,\n path: row.path,\n parentRef: row.parent_ref,\n createdAt: row.created_at,\n };\n}\n\nexport class WorkspaceExistsError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceExistsError\";\n constructor(public readonly agent: string) {\n super(`workspace already exists for agent: ${agent}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"Show its on-disk path\", command: `mu workspace path ${this.agent}` },\n {\n intent: \"Free it (optionally --commit pending changes first)\",\n command: `mu workspace free ${this.agent} (--commit to commit pending changes first)`,\n },\n {\n intent: \"Then re-create with a different backend or base ref\",\n command: `mu workspace create ${this.agent} --backend <jj|sl|git|none> --from <ref>`,\n },\n ];\n }\n}\n\nexport class WorkspaceNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"WorkspaceNotFoundError\";\n constructor(public readonly agent: string) {\n super(`no workspace for agent: ${agent}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List workspaces in current workstream\", command: \"mu workspace list\" },\n { intent: \"List workspaces across all workstreams\", command: \"mu workspace list --all\" },\n {\n intent: \"Create one for this agent\",\n command: `mu workspace create ${this.agent}`,\n },\n ];\n }\n}\n\n/**\n * Thrown by createWorkspace when the on-disk path it would create is\n * already occupied. Distinct from WorkspaceExistsError (which is about\n * the DB row) so the recovery is clear: the dir is orphaned (no DB\n * row points at it) and needs cleanup.\n *\n * Surfaced as a real bug from the multi-agent dogfood (mufeedback note\n * #143): users hit a bare 'vcs git: workspacePath already exists' from\n * the backend, with no nextSteps. After the cccba88 fix (close-refuses-\n * with-workspace), this case only fires when an orphan from a previous\n * mu version persists OR when the dir was manually rm-rf'd while a\n * stale registration remains (the git-worktree case).\n *\n * Maps to exit code 4 (conflict).\n */\nexport class WorkspacePathNotEmptyError extends Error implements HasNextSteps {\n override readonly name = \"WorkspacePathNotEmptyError\";\n constructor(\n public readonly agent: string,\n public readonly workstream: string,\n public readonly workspacePath: string,\n ) {\n super(\n `workspace dir already on disk for agent ${agent} (${workspacePath}); refusing to overwrite`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"List every orphan workspace dir in this workstream\",\n command: `mu workspace orphans -w ${this.workstream}`,\n },\n {\n intent: \"If the dir is intentional (orphan from older mu), free it via mu first\",\n command: `mu workspace free ${this.agent} -w ${this.workstream} # also runs backend cleanup if a row remains`,\n },\n {\n intent: \"Or delete it manually if the registry has no row\",\n command: `rm -rf ${this.workspacePath}`,\n },\n {\n intent: \"For git workspaces specifically: also prune the worktree registration\",\n command: \"cd <project-root> && git worktree prune\",\n },\n ];\n }\n}\n\n/**\n * Thrown by createWorkspace when the resolved projectRoot is the\n * user's $HOME. Surfaced by snap_dogfood Finding 4: a `mu workspace\n * create` invoked from cwd=$HOME with no --project-root began a\n * recursive `cp -a` of $HOME (~/Music, ~/.config, ...) into the\n * workspace dir, stalled on DRM-protected files, and on ctrl-C left\n * a partial dir behind with no DB row.\n *\n * The guard's whole point is to make the user pick a real project\n * deliberately — there's no --force escape hatch on purpose. The\n * resolution is `--project-root <real-path>` (or `cd` into a real\n * project first).\n *\n * Maps to exit code 4 (conflict).\n */\nexport class HomeDirAsProjectRootError extends Error implements HasNextSteps {\n override readonly name = \"HomeDirAsProjectRootError\";\n constructor(\n public readonly agent: string,\n public readonly workstream: string,\n public readonly homeDir: string,\n ) {\n super(\n `refusing to create workspace with projectRoot=$HOME (${homeDir}); a recursive copy/clone of your home directory is almost never what you want`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Re-run from inside a real project directory\",\n command: `cd <your-project> && mu workspace create ${this.agent} -w ${this.workstream}`,\n },\n {\n intent: \"Or pass --project-root explicitly\",\n command: `mu workspace create ${this.agent} -w ${this.workstream} --project-root <your-project>`,\n },\n ];\n }\n}\n\n/**\n * Compose the canonical on-disk path for an agent's workspace. Used by\n * createWorkspace and reachable from `mu workspace path` so the user\n * can `cd $(mu workspace path foo)` even before the directory exists.\n */\nexport function workspacePath(workstream: string, agent: string): string {\n return join(defaultStateDir(), \"workspaces\", workstream, agent);\n}\n\n/** Root dir for a workstream's workspaces — the parent of all\n * per-agent workspace dirs. Used by listWorkspaceOrphans to scan\n * the filesystem. */\nexport function workspacesRoot(workstream: string): string {\n return join(defaultStateDir(), \"workspaces\", workstream);\n}\n\nexport interface WorkspaceOrphan {\n /** The on-disk dir name (the agent name it WOULD be for, if mu had\n * registered it). */\n agentName: string;\n /** Workstream the dir is filed under. */\n workstreamName: string;\n /** Absolute path to the orphan dir. */\n path: string;\n}\n\n/**\n * Like WorkspaceOrphan but additionally flags whether the parent\n * workstream itself is gone (no row in `workstreams`). Returned by\n * listAllOrphanWorkspaces; the per-workstream listWorkspaceOrphans\n * doesn't carry this since by construction it only runs against an\n * existing workstream.\n */\nexport interface StrandedWorkspaceOrphan extends WorkspaceOrphan {\n /** True iff the parent workstream has no DB row (the dir was left\n * behind by a `mu workstream destroy` or a manual DELETE). */\n stranded: boolean;\n}\n\n/**\n * Scan `<state-dir>/workspaces/<workstream>/` for directories that\n * have no row in `vcs_workspaces`. These are the result of:\n * - pre-cccba88 agents closed without --discard-workspace\n * - failed spawn rollbacks (pre-bug_agent_spawn_workspace_fk_failure fix)\n * - manual cleanup that left the dir but not the row\n * - any case where the operator manually rm-rf'd vcs_workspaces rows\n *\n * Returns `[]` when the workstream's workspaces dir doesn't exist,\n * or when every dir on disk has a corresponding DB row. Filesystem\n * read is best-effort: a missing/inaccessible dir returns `[]`\n * (caller doesn't have to check existsSync first).\n *\n * Surfaced by bug_workspace_orphan_not_in_state: orphan dirs were\n * invisible to `mu state` and `mu workspace list`, but blocked\n * subsequent `--workspace` spawns with WorkspacePathNotEmptyError.\n */\nexport function listWorkspaceOrphans(db: Db, workstream: string): WorkspaceOrphan[] {\n const root = workspacesRoot(workstream);\n let dirs: string[];\n try {\n dirs = readdirSync(root, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n return [];\n }\n const registered = new Set(listWorkspaces(db, workstream).map((w) => w.path));\n const orphans: WorkspaceOrphan[] = [];\n for (const agentDir of dirs) {\n const fullPath = join(root, agentDir);\n if (!registered.has(fullPath)) {\n orphans.push({ agentName: agentDir, workstreamName: workstream, path: fullPath });\n }\n }\n return orphans;\n}\n\n/**\n * Cross-workstream variant of listWorkspaceOrphans. Reads\n * `<state-dir>/workspaces/`, recurses one level (per-ws subdir →\n * per-agent subdir), and surfaces every dir with no row in\n * `vcs_workspaces`.\n *\n * Each entry is additionally tagged with `stranded: boolean`: true\n * when the parent workstream has no row in `workstreams`. Stranded\n * orphans are the failure mode this verb was added for — workstreams\n * destroyed before the close-refuses-with-workspace fix landed (or\n * via `mu sql DELETE FROM workstreams ...`) would leave their entire\n * workspace subtree invisible to `mu workspace orphans -w <ws>`,\n * because the user couldn't know to ask for the right name.\n *\n * Surfaced by workspace_orphans_misses_destroyed_workstreams. Returns\n * `[]` when the workspaces root itself doesn't exist; otherwise scans\n * best-effort and skips any subdir that fails to read.\n */\nexport function listAllOrphanWorkspaces(db: Db): StrandedWorkspaceOrphan[] {\n const root = join(defaultStateDir(), \"workspaces\");\n let wsDirs: string[];\n try {\n wsDirs = readdirSync(root, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n return [];\n }\n // One pass over every registered workspace path, regardless of\n // workstream. listWorkspaces(db) joins to workstreams, so any rows\n // for destroyed workstreams (FK CASCADE-deleted) are already gone —\n // their on-disk dirs all fall through as stranded.\n const registered = new Set(listWorkspaces(db).map((w) => w.path));\n const orphans: StrandedWorkspaceOrphan[] = [];\n for (const wsName of wsDirs) {\n const wsRoot = join(root, wsName);\n let agentDirs: string[];\n try {\n agentDirs = readdirSync(wsRoot, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch {\n continue;\n }\n const stranded = tryResolveWorkstreamId(db, wsName) === null;\n for (const agentDir of agentDirs) {\n const fullPath = join(wsRoot, agentDir);\n if (!registered.has(fullPath)) {\n orphans.push({\n agentName: agentDir,\n workstreamName: wsName,\n path: fullPath,\n stranded,\n });\n }\n }\n }\n return orphans;\n}\n\nexport interface CreateWorkspaceOptions {\n agent: string;\n workstream: string;\n /** Project root to branch from. Defaults to the current working\n * directory (the `mu` invocation site, which is normally what the\n * user wants). */\n projectRoot?: string;\n /** Override backend detection. Default: walk `detectBackend`.\n * Accepts either a name (\"jj\" / \"sl\" / \"git\" / \"none\") OR a\n * pre-built `VcsBackend` object — the object form lets tests inject\n * a fresh fake backend without mutating the exported singletons. */\n backend?: VcsBackendName | VcsBackend;\n /** Optional ref to base the workspace on. Backend-specific. */\n parentRef?: string;\n}\n\n/**\n * Create a fresh workspace for an agent. Allocates the on-disk\n * directory, records the row, emits a system event. Idempotent ONLY\n * to the extent that the row check is up-front; if the row exists\n * we throw `WorkspaceExistsError` rather than silently re-using a\n * possibly-stale on-disk state. Callers should `freeWorkspace` first.\n */\nexport async function createWorkspace(db: Db, opts: CreateWorkspaceOptions): Promise<WorkspaceRow> {\n if (getWorkspaceForAgent(db, opts.agent, opts.workstream) !== undefined) {\n throw new WorkspaceExistsError(opts.agent);\n }\n\n const projectRoot = opts.projectRoot ?? process.cwd();\n\n // Footgun guard: refuse projectRoot=$HOME. resolve() normalises a\n // trailing slash, `.`, symlinks-in-name, etc., so `cd && mu workspace\n // create ...` and `--project-root ~/` are all blocked the same way.\n // Direct children of $HOME (e.g. ~/Documents) are NOT blocked —\n // that would be overreach. See snap_dogfood Finding 4.\n if (resolve(projectRoot) === resolve(homedir())) {\n throw new HomeDirAsProjectRootError(opts.agent, opts.workstream, homedir());\n }\n\n const backend =\n opts.backend === undefined\n ? await detectBackend(projectRoot)\n : typeof opts.backend === \"string\"\n ? backendByName(opts.backend)\n : opts.backend;\n const path = workspacePath(opts.workstream, opts.agent);\n\n // Surface the dir-already-exists case as a typed error WITH actionable\n // nextSteps before we delegate to the backend (which throws a bare\n // Error). This is the orphan-from-older-mu case: a workspace dir from\n // before the cccba88 close-refuses fix landed; it has no DB row.\n if (existsSync(path)) {\n throw new WorkspacePathNotEmptyError(opts.agent, opts.workstream, path);\n }\n\n const createOpts: { projectRoot: string; workspacePath: string; parentRef?: string } = {\n projectRoot,\n workspacePath: path,\n };\n if (opts.parentRef !== undefined) createOpts.parentRef = opts.parentRef;\n\n // Wrap the backend's on-disk side effect in a cleanup guard. If the\n // backend throws mid-way (cp -a hits a DRM-protected file, git\n // worktree add fails after creating the dir, an interrupt during a\n // long copy), the partial dir would otherwise be left behind with\n // no DB row — exactly the failure mode from snap_dogfood Finding 4,\n // which then blocked subsequent `mu workspace create` calls with\n // WorkspacePathNotEmptyError. Best-effort: if the rm itself fails,\n // surface the original error and let the user clean up via\n // `mu workspace orphans`.\n let created: Awaited<ReturnType<typeof backend.createWorkspace>>;\n try {\n created = await backend.createWorkspace(createOpts);\n } catch (err) {\n try {\n rmSync(path, { recursive: true, force: true });\n } catch {\n // best-effort; original error wins\n }\n throw err;\n }\n\n // Roll back the on-disk + VCS-registry side effect if the DB row\n // insert fails (FK violation, schema constraint, sqlite_busy timeout).\n // Without this, a failed INSERT leaves a real git worktree (or jj\n // workspace, or 'cp -a' tree) on disk + registered, with no DB row\n // to track it. Surfaced by bug_agent_spawn_workspace_fk_failure: the\n // operator hit 'FOREIGN KEY constraint failed' and was left with a\n // 226M git worktree at workspaces/<ws>/<agent>/ that mu state\n // couldn't see and that blocked subsequent spawns.\n const now = new Date().toISOString();\n try {\n // Resolve agent + workstream to surrogate ids before insert. The\n // agent must already exist (the spawn flow always inserts the\n // agent row first, even on the --workspace pre-stage path that\n // uses the placeholder pane id). We don't auto-create here — a\n // missing row is a programmer bug, not a recoverable condition.\n const wsId = tryResolveWorkstreamId(db, opts.workstream);\n if (wsId === null) {\n throw new Error(\n `createWorkspace: workstream not found: ${opts.workstream} (insertAgent should have ensured this)`,\n );\n }\n const agentRow = db\n .prepare(\"SELECT id FROM agents WHERE name = ? AND workstream_id = ? LIMIT 1\")\n .get(opts.agent, wsId) as { id: number } | undefined;\n // Throw a typed AgentNotFoundError BEFORE the INSERT so the\n // operator sees `no such agent: worker-1 (in workstream X)`\n // instead of a leaked `NOT NULL constraint failed:\n // vcs_workspaces.agent_id` (or, in WAL mode, a FK violation).\n // The CLI's classifyError maps this to exit 3. Surfaced by the\n // parallel-fan-out spawn — see workspace_create_typed_no_agent_error.\n // The catch below reuses the same backend rollback path the\n // SQLite errors used.\n if (!agentRow) {\n throw new AgentNotFoundError(opts.agent, opts.workstream);\n }\n db.prepare(\n `INSERT INTO vcs_workspaces (agent_id, workstream_id, backend, path, parent_ref, created_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n ).run(agentRow.id, wsId, backend.name, path, created.parentRef, now);\n } catch (err) {\n // Best-effort backend-level cleanup. If the backend's free fails\n // (e.g. git worktree remove --force errors), the on-disk state\n // is still there — but at this point we're already failing the\n // verb; surface the original error and let the user clean up\n // via mu workspace orphans (or git worktree remove --force).\n await backend.freeWorkspace({ workspacePath: path, commit: false }).catch(() => {});\n throw err;\n }\n\n emitEvent(\n db,\n opts.workstream,\n `workspace create ${opts.agent} (backend=${backend.name}, path=${path}${created.parentRef ? `, parent=${created.parentRef.slice(0, 12)}` : \"\"})`,\n );\n\n return {\n agentName: opts.agent,\n workstreamName: opts.workstream,\n backend: backend.name,\n path,\n parentRef: created.parentRef,\n createdAt: now,\n };\n}\n\nexport function getWorkspaceForAgent(\n db: Db,\n agent: string,\n workstream: string,\n): WorkspaceRow | undefined {\n // v5: agents.name is per-workstream unique — the lookup must scope\n // by (workstream, agent) so a same-named worker elsewhere can't be\n // resolved instead.\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return undefined;\n const row = db\n .prepare(`SELECT ${SELECT_WS_COLS} ${WS_FROM_JOIN} WHERE ag.name = ? AND v.workstream_id = ?`)\n .get(agent, wsId) as RawWorkspaceRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\nexport function listWorkspaces(db: Db, workstream?: string): WorkspaceRow[] {\n if (workstream === undefined) {\n const rows = db\n .prepare(`SELECT ${SELECT_WS_COLS} ${WS_FROM_JOIN} ORDER BY ws.name, ag.name`)\n .all() as RawWorkspaceRow[];\n return rows.map(rowFromDb);\n }\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(`SELECT ${SELECT_WS_COLS} ${WS_FROM_JOIN} WHERE v.workstream_id = ? ORDER BY ag.name`)\n .all(wsId) as RawWorkspaceRow[];\n return rows.map(rowFromDb);\n}\n\n/**\n * Decorate each row with `commitsBehindMain` by asking the row's backend\n * how far the parent_ref is behind the project's default branch HEAD.\n * Cheap, pure observation: NO automatic `git fetch` / `jj git fetch` /\n * `sl pull`. The number is as fresh as the workspace's local refs cache.\n *\n * Surfaced by bug_workspace_stale_parent_silent_drift: long-lived\n * workspaces silently drift from main, and there was no signal to the\n * operator. `mu workspace list` and `mu state` show the result.\n *\n * Returns a NEW array; does not mutate the input. Rows whose parent_ref\n * is missing, or whose backend's commitsBehind throws / returns null,\n * get `commitsBehindMain: null`.\n *\n * Performance hardening (review_code_decorate_with_staleness_n_plus_one):\n * 1. Concurrency-cap (DECORATE_CONCURRENCY = 4) so a workstream with\n * many workspaces can't fork-burst N parallel git/jj/sl children.\n * Real-user pain surface is `watch -n 5 mu state -w X` loops.\n * 2. Per-invocation memoization keyed by (backend, parentRef).\n * Sibling workspaces in a workstream share a project root, and\n * git worktrees / jj workspaces share their local refs cache by\n * construction — so commits-behind for the same parent_ref\n * resolves to the same answer regardless of which workspace dir\n * we shell out from. Sl clones are independent, but in practice\n * all sibling clones in a workstream were produced from the same\n * origin and stay in lockstep on local origin/* refs. The cache\n * is local to this function call — no cross-invocation TTL, no\n * invalidation policy.\n */\nconst DECORATE_CONCURRENCY = 4;\n\nexport async function decorateWithStaleness(\n rows: readonly WorkspaceRow[],\n): Promise<WorkspaceRow[]> {\n // Per-invocation cache: identical (backend, path, parentRef) tuples\n // resolve to one shellout regardless of how many rows hit them. We key\n // by path AND parentRef because the backend's commitsBehind contract\n // depends on both (different workspaces can share a parentRef but\n // each shells out from its own cwd; sharing only across identical\n // (path, parentRef) tuples is the strictly-correct memo).\n const cache = new Map<string, Promise<number | null>>();\n const fetchBehind = (r: WorkspaceRow): Promise<number | null> => {\n const parentRef = r.parentRef;\n if (parentRef === null) return Promise.resolve(null);\n const key = `${r.backend}\\x00${parentRef}`;\n const cached = cache.get(key);\n if (cached !== undefined) return cached;\n const p = (async (): Promise<number | null> => {\n try {\n const backend = backendByName(r.backend);\n return await backend.commitsBehind(r.path, parentRef);\n } catch {\n return null;\n }\n })();\n cache.set(key, p);\n return p;\n };\n return mapWithConcurrency(rows, DECORATE_CONCURRENCY, async (r) => ({\n ...r,\n commitsBehindMain: await fetchBehind(r),\n }));\n}\n\n/**\n * Tiny p-limit-style helper. Keeps at most `limit` callbacks in flight\n * at once and preserves input order in the result. Stays in this file\n * because it has exactly one caller (decorateWithStaleness); promote\n * out only when a second caller appears (anti-feature pledge: no\n * abstractions for hypothetical future flexibility).\n */\nasync function mapWithConcurrency<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<R[]> {\n const results = new Array<R>(items.length);\n let next = 0;\n const worker = async (): Promise<void> => {\n while (true) {\n const i = next++;\n if (i >= items.length) return;\n const item = items[i] as T;\n results[i] = await fn(item, i);\n }\n };\n const workerCount = Math.min(Math.max(1, limit), items.length);\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n return results;\n}\n\nexport interface FreeWorkspaceOptions {\n /** If true, attempt to commit pending changes before tearing down.\n * Backend-specific; see VcsBackend.freeWorkspace. */\n commit?: boolean;\n}\n\nexport interface FreeWorkspaceResult {\n /** The committed ref, when `commit` was true and there was something\n * to commit. */\n committedRef?: string;\n /** True iff the on-disk path was actually removed. */\n removed: boolean;\n /** True iff the DB row was actually deleted. */\n rowDeleted: boolean;\n}\n\nexport interface RefreshWorkspaceOptions {\n agent: string;\n workstream: string;\n /** Optional override of the rebase target. When undefined, the\n * backend resolves its own default (origin/HEAD for git,\n * `trunk()` for jj/sl). */\n fromRef?: string;\n}\n\nexport interface RefreshWorkspaceResult extends RebaseResult {\n /** Backend name (mirrors the row) so a JSON consumer doesn't have\n * to look up the workspace separately to know what kind of rebase\n * it just got. */\n vcs: VcsBackendName;\n /** The workspace's on-disk path (so the JSON shape is self-contained\n * for piping to a downstream `jq` script). */\n workspacePath: string;\n}\n\n/**\n * Refresh an agent's workspace by rebasing it onto `fromRef` (or the\n * backend's default base). The agent / pane are NOT touched — only\n * the on-disk working copy moves. Bumps the row's `created_at` proxy\n * via the emit event; the row itself is otherwise unchanged.\n *\n * Surfaced by fb_workspace_recycle_verb: dogfood between waves\n * required `close → free → spawn`, which killed the worker's LLM\n * context. `refresh` lets the worker keep its context while the\n * dir gets fresh main.\n *\n * Throws (with typed errors propagated from the backend):\n * - WorkspaceNotFoundError — no row for this (agent, workstream)\n * - WorkspaceVcsRequiredError — backend == none\n * - WorkspaceDirtyError — git/sl: uncommitted changes present\n * - WorkspaceConflictError — rebase produced conflicts\n */\nexport async function refreshWorkspace(\n db: Db,\n opts: RefreshWorkspaceOptions,\n): Promise<RefreshWorkspaceResult> {\n const row = getWorkspaceForAgent(db, opts.agent, opts.workstream);\n if (!row) throw new WorkspaceNotFoundError(opts.agent);\n const backend = backendByName(row.backend);\n const result = await backend.rebaseTo(row.path, opts.fromRef);\n emitEvent(\n db,\n row.workstreamName,\n `workspace refresh ${opts.agent} (backend=${row.backend}, fromRef=${result.fromRef}, replayed=${result.replayed.length})`,\n );\n return { ...result, vcs: row.backend, workspacePath: row.path };\n}\n\nexport interface ListCommitsOptions {\n workstream: string;\n /** Optional override of the base ref (default: the workspace row's\n * parent_ref). Useful when the operator wants to ask \"what's on\n * top of an arbitrary ref\" without re-creating the workspace. */\n since?: string;\n}\n\nexport interface ListCommitsResult {\n /** Backend name (mirrors the row). */\n vcs: VcsBackendName;\n /** The base ref actually used. */\n baseRef: string;\n /** The commits, oldest-first. Empty when the workspace is exactly\n * at baseRef. */\n commits: CommitSummary[];\n /** The workspace's on-disk path (so JSON consumers don't have to\n * call `mu workspace path` separately). */\n workspacePath: string;\n}\n\n/**\n * List commits the workspace has on top of its `parent_ref` (or the\n * `--since` override), oldest-first. Promotes the dogfood-painful\n * cd $(mu workspace path X) && git log <base>..HEAD\n * incantation into a typed verb that knows the workspace's\n * recorded fork point.\n *\n * Throws (with typed errors propagated from the backend):\n * - WorkspaceNotFoundError — no row for this (agent, workstream)\n * - WorkspaceVcsRequiredError — backend == none\n * - any backend-level Error — unknown ref, missing repo, etc.\n *\n * Surfaced by fb_workspace_commits_verb.\n */\nexport async function listCommitsForWorkspace(\n db: Db,\n agent: string,\n opts: ListCommitsOptions,\n): Promise<ListCommitsResult> {\n const row = getWorkspaceForAgent(db, agent, opts.workstream);\n if (!row) throw new WorkspaceNotFoundError(agent);\n const backend = backendByName(row.backend);\n // Dispatch FIRST when the backend can't possibly answer (none),\n // so the operator sees the typed WorkspaceVcsRequiredError with\n // its Next: hints rather than a generic \"no recorded parent_ref\"\n // string. The backend.commitsSinceBase impl is the source of\n // truth on \"is this verb meaningful for this backend?\".\n const baseRef = opts.since ?? row.parentRef;\n if (baseRef === null || baseRef.length === 0) {\n if (row.backend === \"none\") {\n // Force the typed error path — baseRef value doesn't matter\n // since none.commitsSinceBase ignores it.\n await backend.commitsSinceBase(row.path, \"\");\n }\n // Without a recorded parent_ref AND no --since override there's\n // no defensible answer to \"commits since fork\". Surface as a\n // standard Error (no typed UX upgrade until a real user hits this\n // — the row's parent_ref is set by every backend that supports\n // commits at all).\n throw new Error(`workspace ${agent} has no recorded parent_ref; pass --since <ref> explicitly`);\n }\n const commits = await backend.commitsSinceBase(row.path, baseRef);\n return { vcs: row.backend, baseRef, commits, workspacePath: row.path };\n}\n\n/**\n * Tear down an agent's workspace. Calls the backend to remove the\n * on-disk directory (with optional auto-commit), then DELETEs the row.\n * Idempotent on a missing workspace (returns all-false).\n */\nexport async function freeWorkspace(\n db: Db,\n agent: string,\n opts: FreeWorkspaceOptions & { workstream: string },\n): Promise<FreeWorkspaceResult> {\n const row = getWorkspaceForAgent(db, agent, opts.workstream);\n if (!row) return { removed: false, rowDeleted: false };\n\n // Pre-mutation snapshot — the row deletion + on-disk teardown is\n // not recoverable from history. Snapshot is DB-only (the worktree\n // is not rolled back; that's the design's tmux/disk honesty point).\n captureSnapshot(db, `workspace free ${agent}`, row.workstreamName);\n\n const backend = backendByName(row.backend);\n const result = await backend.freeWorkspace({\n workspacePath: row.path,\n commit: opts.commit ?? false,\n });\n\n // Resolve to surrogate ids scoped by the row's workstream.\n const wsIdForDel = tryResolveWorkstreamId(db, row.workstreamName);\n const del =\n wsIdForDel === null\n ? { changes: 0 }\n : db\n .prepare(\n `DELETE FROM vcs_workspaces\n WHERE agent_id = (SELECT id FROM agents WHERE name = ? AND workstream_id = ?)\n AND workstream_id = ?`,\n )\n .run(agent, wsIdForDel, wsIdForDel);\n emitEvent(\n db,\n row.workstreamName,\n `workspace free ${agent} (backend=${row.backend}, path=${row.path}${result.committedRef ? `, committed=${result.committedRef.slice(0, 12)}` : \"\"})`,\n );\n\n return {\n removed: result.removed,\n rowDeleted: del.changes > 0,\n ...(result.committedRef !== undefined ? { committedRef: result.committedRef } : {}),\n };\n}\n","// mu — self-documenting output helpers.\n//\n// Every successful verb output should answer \"what changed AND what's\n// the natural next step?\". Every error should answer \"why AND what are\n// the actionable resolutions?\". The same data shape feeds both human\n// (dim text) and JSON (`nextSteps` array) consumers.\n//\n// Why a separate module: this is shared by cli.ts (success-path\n// rendering), the typed errors in src/agents.ts / src/tasks.ts /\n// src/workstream.ts (error nextSteps), and tests. Keeping the type +\n// helpers in one place avoids circular imports.\n\nimport Table from \"cli-table3\";\nimport type { Command } from \"commander\";\nimport picocolors from \"picocolors\";\n\n/**\n * Should we emit ANSI color escapes from this process?\n *\n * picocolors ships an `isColorSupported` flag, but it bakes its env\n * inspection (NO_COLOR / FORCE_COLOR / isTTY) ONCE at module-load\n * time. That makes the function untestable without dynamic re-imports\n * — and worse, it loses colors whenever stdout is a pipe, most\n * painfully under `watch --color mu hud` and `tmux display-popup -E\n * mu hud | cat`, where the surrounding pane is a real terminal but\n * our own stdout is a pipe.\n *\n * We therefore re-implement the decision from scratch at call time,\n * reading every signal directly from `process.env` / `process.stdout`\n * so tests can flip env vars and observe the result without the\n * vi.resetModules + vi.doMock dance (per task\n * review_test_color_enabled_no_color_module_load_caveat).\n *\n * Order of precedence (first match wins):\n * - `NO_COLOR` set (cross-tool opt-out, https://no-color.org/) →\n * OFF, even when TMUX/MU_FORCE_COLOR/FORCE_COLOR are set. We\n * treat any defined value (including \"\") as set, matching the\n * no-color.org convention and picocolors' own behavior.\n * - `MU_FORCE_COLOR` set → ON (mu-specific override).\n * - `FORCE_COLOR` set → ON (the standard env var picocolors / chalk\n * consult).\n * - `TMUX` set → ON (the load-bearing fix for `watch` inside tmux:\n * the surrounding pane is a real terminal even though our stdout\n * is a pipe).\n * - Fall back to the standard TTY heuristic: stdout is a TTY AND\n * TERM !== \"dumb\". This mirrors what picocolors itself does in\n * `isColorSupported` for the happy-path case.\n *\n * See task hud_colors_stripped_under_watch_and for the full repro.\n */\nexport function colorEnabled(): boolean {\n if (process.env.NO_COLOR !== undefined) return false;\n if (process.env.MU_FORCE_COLOR !== undefined) return true;\n if (process.env.FORCE_COLOR !== undefined) return true;\n if (process.env.TMUX !== undefined) return true;\n return Boolean(process.stdout.isTTY) && process.env.TERM !== \"dumb\";\n}\n\n/**\n * The single picocolors instance the rest of the codebase imports.\n * Built once at module load with `colorEnabled()` baked in, so every\n * caller (cli.ts, src/cli/*.ts) renders consistently regardless of\n * isTTY heuristics. Any other module that needs `pc` should import\n * this one rather than reaching for `picocolors` directly.\n */\n// picocolors is still used as the renderer (createColors honors the\n// flag we pass), but the *decision* of whether to render is ours.\nexport const pc = picocolors.createColors(colorEnabled());\n\n/**\n * One actionable next step. The `intent` is human-prose (\"Drop notes\n * as you work\"); the `command` is a literal shell command the user (or\n * an LLM) can copy-paste or `eval` directly.\n *\n * Used both for success-path hints (post-verb) and for typed-error\n * resolutions (in the error message + JSON output).\n */\nexport interface NextStep {\n /** Short human-prose label, e.g. \"Drop notes as you work\". */\n intent: string;\n /** Literal shell command, e.g. `mu task note foo \"...\"`. */\n command: string;\n}\n\n/**\n * Print a block of next-step hints to stdout, dimmed so humans can\n * skim past them but agents reading the captured output still get\n * them. No-op when the array is empty.\n *\n * Format:\n *\n * Next:\n * <intent padded>: <command>\n * <intent padded>: <command>\n *\n * The padding aligns the colons so visual scanning is easy.\n */\nexport function printNextSteps(steps: readonly NextStep[]): void {\n printNextStepsTo(steps, \"stdout\");\n}\n\n/** Same as `printNextSteps` but routes to either stdout or stderr.\n * Errors emit nextSteps to stderr (so success vs failure paths\n * capture cleanly when scripts redirect them separately); success\n * paths emit to stdout. Single source of truth for the formatting\n * (review_code_print_next_steps_duplicated). */\nexport function printNextStepsTo(steps: readonly NextStep[], sink: \"stdout\" | \"stderr\"): void {\n if (steps.length === 0) return;\n const labelWidth = Math.max(...steps.map((s) => s.intent.length));\n const out = sink === \"stderr\" ? console.error : console.log;\n out(pc.dim(\"Next:\"));\n for (const step of steps) {\n const label = step.intent.padEnd(labelWidth);\n out(pc.dim(` ${label} : ${step.command}`));\n }\n}\n\n/**\n * Build a cli-table3 Table with the mu-standard safety belt:\n * `wordWrap: false` (cells wider than their column truncate with `…`\n * instead of wrapping to a second visual row), per-column max widths\n * applied only where the caller asks (`null` = auto), and a default\n * borderless style mirroring the HUD/workspace/workstream tables.\n *\n * Callers should pre-truncate values they care about via the\n * `truncate()` / `truncateFront()` helpers in cli.ts (the proactive\n * path); `wordWrap: false` is the safety belt for the cells they\n * miss. See HUD's `newHudTable` for the load-bearing rationale\n * (src/cli/state.ts — hud render mode).\n *\n * Surfaced live by `mu workspace list` blowing the terminal width on\n * the `path` column (tables_truncate_long_cols_audit). Don't try to\n * cap every column — apply `colWidths` only on the column(s) the\n * operator is least likely to read in full and most likely to be\n * long. Don't add a `--full` / `--no-truncate` flag per verb either;\n * `--json` already emits the full value.\n */\nexport function muTable(opts: {\n head: string[];\n /** Per-column max widths in cells (`null` = auto width). When\n * supplied, the array length must match `head`. cli-table3\n * truncates with `…` because we set `wordWrap: false`. */\n colWidths?: (number | null)[];\n /** Style override; defaults to `{ head: [], border: [] }` (mu's\n * borderless look). Pass `{ head: [] }` to keep cli-table3's\n * default border styling. */\n style?: { head?: string[]; border?: string[] };\n}): InstanceType<typeof Table> {\n return new Table({\n head: opts.head,\n ...(opts.colWidths !== undefined ? { colWidths: opts.colWidths } : {}),\n style: opts.style ?? { head: [], border: [] },\n wordWrap: false,\n });\n}\n\n/**\n * The typed-error wire format for `--json` output. Errors that carry\n * actionable resolutions (most of them) implement `errorNextSteps()`;\n * the handler in cli.ts wraps them in this shape and emits to stderr.\n */\nexport interface ErrorJson {\n /** Class name (e.g. \"ClaimerNotRegisteredError\"). */\n error: string;\n /** Human-readable message (the same one printed to stderr in non-JSON mode). */\n message: string;\n /** Actionable resolutions. May be empty. */\n nextSteps: NextStep[];\n /** Process exit code that will follow. */\n exitCode: number;\n}\n\n/**\n * Marker interface for typed errors that carry actionable resolutions.\n * The handler checks this with a duck-typed `typeof err.errorNextSteps\n * === \"function\"` rather than instanceof so cross-realm errors (e.g.\n * thrown from a different module instance after a hot-reload) still\n * surface their nextSteps.\n */\nexport interface HasNextSteps {\n errorNextSteps(): NextStep[];\n}\n\n/**\n * Detect whether the current invocation requested `--json`. Used by the\n * error handler to decide between human-prose stderr and structured\n * JSON stderr. Reads `process.argv` directly because commander has\n * already consumed it by the time the handler runs, and threading it\n * through every verb wrapper would be invasive.\n *\n * Tolerates `--json=...` form (commander supports both) but mu's verbs\n * only use the bare `--json` flag.\n */\nexport function isJsonMode(): boolean {\n return process.argv.some((a) => a === \"--json\" || a.startsWith(\"--json=\"));\n}\n\n/**\n * Has the current err object produced its own actionable nextSteps?\n * Encapsulates the duck-type check so the handler stays readable.\n */\nexport function hasNextSteps(err: unknown): err is HasNextSteps {\n return (\n typeof err === \"object\" &&\n err !== null &&\n typeof (err as { errorNextSteps?: unknown }).errorNextSteps === \"function\"\n );\n}\n\n// ─── Usage rendering for the validation-error contract ──────────────\n//\n// Every operator-error path (commander-thrown CommanderError, handler-\n// thrown UsageError, typed *Invalid* errors) gets the same surface:\n// (1) the error line, (2) the failing subcommand's --help. The human\n// path prints commander's own helpInformation() (so future commander\n// version bumps automatically pick up any rendering improvements);\n// the --json path renders a structured shape so a script orchestrator\n// can introspect the verb without re-shelling for --help.\n//\n// Why structured-not-string for JSON: the entire point of --json is\n// that consumers never have to free-text-parse mu output. Embedding a\n// multi-kilobyte rendered help blob in the JSON envelope defeats that\n// — every option is already structured on the Command object.\n\n/** Structured rendition of a verb's --help, for JSON error envelopes. */\nexport interface UsageJson {\n /** Full canonical name including parent commands (e.g. \"mu task add\"). */\n command: string;\n /** The single-line synopsis (e.g. \"mu task add [options] [id]\"). */\n synopsis: string;\n /** Verb description (the one-paragraph prose under the synopsis). */\n description: string;\n /** Positional arguments in declared order. */\n args: Array<{ name: string; required: boolean; variadic: boolean; description: string }>;\n /** Options in declared order. `mandatory: true` iff the option was\n * declared via `.requiredOption()` (i.e. the operator MUST pass it).\n * `valueRequired: true` for `<value>`-style options whose value is\n * required when the flag IS passed. The two are independent. */\n options: Array<{\n flags: string;\n description: string;\n mandatory: boolean;\n valueRequired: boolean;\n }>;\n}\n\n/** Walk parent chain so subcommand renderings include the full path\n * (\"mu task add\" not just \"add\"). */\nfunction commandFullName(cmd: Command): string {\n const parts: string[] = [];\n let cur: Command | null = cmd;\n while (cur) {\n parts.unshift(cur.name());\n cur = cur.parent;\n }\n return parts.join(\" \");\n}\n\n/** Extract the structured usage shape for `--json` error envelopes. */\nexport function renderUsageJson(cmd: Command): UsageJson {\n return {\n command: commandFullName(cmd),\n synopsis: `${commandFullName(cmd)} ${cmd.usage()}`,\n description: cmd.description(),\n args: cmd.registeredArguments.map((a) => ({\n name: a.name(),\n required: a.required,\n variadic: a.variadic,\n description: a.description ?? \"\",\n })),\n options: cmd.options.map((o) => ({\n flags: o.flags,\n description: o.description ?? \"\",\n mandatory: o.mandatory ?? false,\n valueRequired: o.required ?? false,\n })),\n };\n}\n\n/** Render the human --help block (commander's own `helpInformation()`)\n * to stderr. Single source of truth for the post-error help dump. */\nexport function printUsageHuman(cmd: Command): void {\n // helpInformation() returns the full \"Usage: ...\\n\\n<desc>\\n\\nOptions:\\n ...\" block.\n // Print to stderr (errors only) so success-path stdout is never polluted.\n process.stderr.write(`\\n${cmd.helpInformation()}`);\n}\n","// mu — spawnAgent + supporting helpers (resolveCliCommand,\n// awaitSpawnLiveness, createOrReusePane, defaultSpawnLivenessMs,\n// prestageWorkspace, finalizeAgentRow, rollbackSpawn).\n//\n// spawnAgent's flow is documented inline. The interesting bit is the\n// `--workspace` cycle (workspace row FKs agent.name; agent row needs\n// pane_id which needs workspace path as cwd) — see prestageWorkspace.\n//\n// Extracted from src/agents.ts as part of refactor_split_large_src_files.\n\nimport {\n type AgentRow,\n deleteAgent,\n getAgent,\n insertAgent,\n isValidAgentName,\n pendingPaneIdFor,\n refreshAgentTitle,\n} from \"../agents.js\";\nimport type { Db } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport { isJsonMode } from \"../output.js\";\nimport {\n capturePane,\n enableMuPaneBordersForPane,\n killPane,\n listWindows,\n newSessionWithPane,\n newWindow,\n paneExists,\n sessionExists,\n setPaneTitle,\n sleep,\n splitWindow,\n} from \"../tmux.js\";\nimport type { VcsBackendName } from \"../vcs.js\";\nimport { createWorkspace, freeWorkspace } from \"../workspace.js\";\nimport { AgentDiedOnSpawnError, AgentExistsError } from \"./errors.js\";\n\n/**\n * Smallest-unused-suffix naming convention for agent names: a role\n * (lowercase alpha + digits) followed by `-<n>` where `<n>` is one or\n * more digits. Examples that pass: `worker-1`, `reviewer-2`, `scout-12`.\n * Examples that warn: `worker-tests`, `alice`, `db-leader`, `x-y-1`.\n *\n * Source: feedback ws task fb_agent_naming_convention. The dogfood report\n * was operators (LLMs included) drifting to descriptive names like\n * `worker-tests` after a few spawns, breaking the convention silently.\n * The hint surfaced by `maybeWarnNonConventionalAgentName` is a lint, not\n * a rule — `isValidAgentName` still accepts the broader\n * `^[a-z][a-z0-9_-]{0,31}$` shape.\n */\nconst AGENT_NAME_CONVENTION_RE = /^[a-z][a-z0-9]*(?:-[0-9]+)$/;\n\n/**\n * Stderr lint when an agent name doesn't match the smallest-unused-suffix\n * convention. Mirrors the slugify-truncation stderr hint in cmdTaskAdd\n * (commit 28a13af): stderr-only, exit 0, suppressed under --json so\n * machine consumers stay clean. The spawn already succeeded by the time\n * this runs — we're just nudging the operator toward `<role>-<n>`.\n */\nfunction maybeWarnNonConventionalAgentName(name: string): void {\n if (AGENT_NAME_CONVENTION_RE.test(name)) return;\n if (isJsonMode()) return;\n process.stderr.write(\n `hint: agent name \"${name}\" does not match the smallest-unused-suffix convention (<role>-<n>; e.g. worker-1, reviewer-2). Accepted; consider renaming if you spawn additional workers.\\n`,\n );\n}\n\n/**\n * Resolve the actual executable to launch in an agent's pane for a given\n * `cli`. Honours the env var `MU_<UPPER_CLI>_COMMAND` (e.g. `MU_PI_COMMAND=\n * pi-alt` makes `--cli pi` actually exec `pi-alt`). Falls back to the cli\n * name itself, which is what users expect when their `pi` binary is on\n * `$PATH` under that exact name.\n *\n * Used by `spawnAgent` to pick the spawned command, and by reconcile's\n * orphan detector so externally-spawned panes running the resolved binary\n * are still recognised as agents.\n */\nexport function resolveCliCommand(cli: string): string {\n const envName = `MU_${cli.toUpperCase()}_COMMAND`;\n const override = process.env[envName];\n return override && override.trim() !== \"\" ? override : cli;\n}\n\nexport interface SpawnAgentOptions {\n name: string;\n workstream: string;\n /** Defaults to \"pi\". 0.1.0 only really supports \"pi\" but the column\n * accepts any string for forward-compat with future multi-CLI support\n * (claude/codex). */\n cli?: string;\n /** The actual command to run in the pane. Defaults to the cli value. */\n command?: string;\n /** Window name to group this agent under. Defaults to the agent's name\n * (so each agent gets its own window). Multiple agents sharing a `tab`\n * share a window with multiple panes. */\n tab?: string;\n /** \"full-access\" (default) or \"read-only\". The schema stores it; today\n * the role isn't enforced (deferred to a future capabilities pass). */\n role?: string;\n /** Initial working directory for the spawned pane (`tmux -c <path>`).\n * When `workspace: true` is passed, this is ignored — the workspace\n * path is used instead. */\n cwd?: string;\n /** Override the tmux session name. Defaults to `mu-<workstream>`. */\n tmuxSession?: string;\n /** Auto-create a VCS workspace for this agent before spawning the\n * pane and use the workspace path as cwd. Backend defaults to\n * detection (jj > sl > git > none). */\n workspace?: boolean;\n /** Force a specific VCS backend (only meaningful with `workspace: true`). */\n workspaceBackend?: VcsBackendName;\n /** Optional ref to base the workspace on (only meaningful with\n * `workspace: true`). Backend-specific. */\n workspaceFrom?: string;\n /** Project root the workspace branches from (only meaningful with\n * `workspace: true`). Defaults to `process.cwd()`. */\n workspaceProjectRoot?: string;\n}\n\n/**\n * Spawn a new agent in its tmux pane and register it in the DB.\n *\n * Phases:\n * 1. Validate name + uniqueness.\n * 2. If --workspace: prestageWorkspace() (placeholder agent row +\n * workspace dir + workspace row).\n * 3. createOrReusePane() in the workspace path (or opts.cwd).\n * 4. setPaneTitle + enableMuPaneBordersForPane.\n * 5. finalizeAgentRow() — patch placeholder pane_id to real (workspace\n * path), or insert a fresh agent row (no-workspace path).\n * 6. awaitSpawnLiveness().\n *\n * Failure between any of (3)–(6) calls rollbackSpawn() to undo the\n * pane + row + workspace. The caller-visible error is preserved.\n */\nexport async function spawnAgent(db: Db, opts: SpawnAgentOptions): Promise<AgentRow> {\n if (!isValidAgentName(opts.name)) {\n throw new TypeError(\n `invalid agent name: ${JSON.stringify(opts.name)} (expected /^[a-z][a-z0-9_-]{0,31}$/)`,\n );\n }\n // Per-workstream uniqueness check: v5 allows the same agent name in\n // different workstreams. Scope the existence check to the spawn's\n // workstream so two operators spawning 'worker-1' in wsA and wsB\n // both succeed (bug_v5_name_clash_silent_misroute).\n if (getAgent(db, opts.name, opts.workstream) !== undefined) {\n throw new AgentExistsError(opts.name);\n }\n\n const session = opts.tmuxSession ?? `mu-${opts.workstream}`;\n const windowName = opts.tab ?? opts.name;\n const cli = opts.cli ?? \"pi\";\n const command = opts.command ?? resolveCliCommand(cli);\n\n // Workspace pre-stage. See `prestageWorkspace` for the FK-ordering\n // rationale. Returns the workspace path (used as the pane's cwd) or\n // undefined when --workspace wasn't requested.\n const workspacePathStr = opts.workspace ? await prestageWorkspace(db, opts, cli) : undefined;\n\n // Inject identity env vars into the pane so anything running inside\n // (pi extensions, claim-protocol scripts, status segments) can branch\n // on 'I am a mu-managed worker' without scraping pane titles or DB\n // lookups. Set via tmux `-e KEY=VALUE` (per-pane; doesn't pollute the\n // tmux server's global env).\n //\n // These are NOT exposed via SpawnAgentOptions — mu identity is not\n // user-tunable. Adding more keys here means every spawned pane sees\n // them automatically.\n const paneEnv: Record<string, string> = {\n MU_MANAGED_AGENT: \"1\",\n MU_AGENT_NAME: opts.name,\n MU_WORKSTREAM: opts.workstream,\n };\n\n const paneId = await createOrReusePane({\n session,\n windowName,\n command,\n cwd: workspacePathStr ?? opts.cwd,\n env: paneEnv,\n });\n\n const hasWorkspace = workspacePathStr !== undefined;\n\n let agent: AgentRow;\n try {\n await setPaneTitle(paneId, opts.name);\n // Apply the mu pane border to the new window. Window-scoped option;\n // see enableMuPaneBorders docstring for why this is required per\n // window (and not just per session). Self-checks MU_BANNER_QUIET\n // and is best-effort — the border is decorative.\n await enableMuPaneBordersForPane(paneId);\n agent = finalizeAgentRow(db, { opts, cli, paneId, hasWorkspace });\n } catch (err) {\n await rollbackSpawn(db, opts.name, paneId, hasWorkspace, opts.workstream);\n throw err;\n }\n\n // Liveness check: wait briefly, then verify the pane is still alive.\n // Catches the silent-spawn-failure class of bugs where the CLI dies\n // immediately (lock conflict, bad credentials, etc.). On failure, roll\n // back the DB row and surface a typed error with whatever scrollback\n // tmux still has.\n try {\n await awaitSpawnLiveness(paneId, opts.name);\n } catch (err) {\n await rollbackSpawn(db, opts.name, paneId, hasWorkspace, opts.workstream);\n throw err;\n }\n emitEvent(\n db,\n opts.workstream,\n `agent spawn ${opts.name} (cli=${cli}, role=${opts.role ?? \"full-access\"}, pane=${paneId})`,\n );\n // Initial title push: the agent row is in 'spawning' state at this\n // point, which composeAgentTitle renders as bare name (no decoration\n // until the first detect cycle). Reconcile will re-push as soon as\n // the operator runs any status-reading verb.\n await refreshAgentTitle(db, opts.name, opts.workstream);\n // Lint-grade stderr nudge when the operator picked a name that\n // doesn't fit the <role>-<n> smallest-unused-suffix convention. The\n // spawn already succeeded; this is advice, not an error. Done LAST\n // so the hint trails the spawn's own stdout output and so a partial\n // failure (rolled back above) never emits it.\n maybeWarnNonConventionalAgentName(opts.name);\n return agent;\n}\n\n/**\n * Stage 1 of `--workspace` spawn: insert the agent row with a placeholder\n * pane id, then create the VCS workspace (whose row FKs the agent name).\n *\n * Why placeholder-first? `vcs_workspaces.agent` FK + ON DELETE CASCADE\n * means the workspace row needs an agents row to exist before we insert\n * it; but we can't insert agents without a pane_id (NOT NULL), and we\n * can't create the pane until we know the workspace's on-disk path\n * (used as cwd). The placeholder unblocks the cycle.\n *\n * The placeholder is a publicly-visible quirk — see `PENDING_PANE_PREFIX`\n * and the consumers it documents (refreshAgentTitle, the\n * mode: \"status-only\"/\"report-only\" rationale in `listLiveAgents`).\n * The deeper fix (eliminate the placeholder\n * by reordering ws-dir → pane → atomic dual-insert) is filed as a\n * separate refactor; this shape is the established equilibrium.\n *\n * Throws after best-effort rollback (deleteAgent) if workspace creation\n * fails. Returns the workspace's on-disk path.\n */\nasync function prestageWorkspace(db: Db, opts: SpawnAgentOptions, cli: string): Promise<string> {\n insertAgent(db, {\n name: opts.name,\n workstream: opts.workstream,\n cli,\n paneId: pendingPaneIdFor(opts.name),\n status: \"spawning\",\n role: opts.role,\n tab: opts.tab ?? null,\n });\n try {\n const wsOpts: Parameters<typeof createWorkspace>[1] = {\n agent: opts.name,\n workstream: opts.workstream,\n };\n if (opts.workspaceBackend !== undefined) wsOpts.backend = opts.workspaceBackend;\n if (opts.workspaceFrom !== undefined) wsOpts.parentRef = opts.workspaceFrom;\n if (opts.workspaceProjectRoot !== undefined) wsOpts.projectRoot = opts.workspaceProjectRoot;\n const ws = await createWorkspace(db, wsOpts);\n return ws.path;\n } catch (err) {\n deleteAgent(db, opts.name, opts.workstream);\n throw err;\n }\n}\n\n/**\n * Stage 2 of spawn (after the real pane exists): either patch the\n * placeholder row to the real pane id (workspace path), or insert a\n * fresh agent row (no-workspace path). Throws if the patch UPDATE\n * silently affects no row (the placeholder row vanished mid-spawn —\n * historically the bug_agent_spawn_workspace_fk_failure class, now\n * patched via mode: \"status-only\"/\"report-only\" on read verbs).\n */\nfunction finalizeAgentRow(\n db: Db,\n args: { opts: SpawnAgentOptions; cli: string; paneId: string; hasWorkspace: boolean },\n): AgentRow {\n const { opts, cli, paneId, hasWorkspace } = args;\n if (!hasWorkspace) {\n return insertAgent(db, {\n name: opts.name,\n workstream: opts.workstream,\n cli,\n paneId,\n status: \"spawning\",\n role: opts.role,\n tab: opts.tab ?? null,\n });\n }\n // Scope the patch to the spawn's workstream so a same-named worker\n // in another workstream isn't repointed at this pane\n // (bug_v5_name_clash_silent_misroute).\n db.prepare(\n `UPDATE agents SET pane_id = ?, updated_at = ?\n WHERE name = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)`,\n ).run(paneId, new Date().toISOString(), opts.name, opts.workstream);\n const row = getAgent(db, opts.name, opts.workstream);\n if (!row) throw new Error(`spawnAgent: agent vanished after workspace stage: ${opts.name}`);\n return row;\n}\n\n/**\n * Roll back a failed spawn. Idempotent and best-effort: every step\n * swallows its own errors so a partial-cleanup substrate (already-killed\n * pane, no agent row) never masks the original failure.\n */\nasync function rollbackSpawn(\n db: Db,\n name: string,\n paneId: string,\n hasWorkspace: boolean,\n workstream: string,\n): Promise<void> {\n await killPane(paneId).catch(() => {});\n if (hasWorkspace) {\n // Scope cleanup to the spawn's workstream so a same-named worker\n // elsewhere isn't torn down by accident\n // (bug_v5_name_clash_silent_misroute).\n await freeWorkspace(db, name, { workstream }).catch(() => {});\n }\n deleteAgent(db, name, workstream);\n}\n\n/**\n * Default liveness window in milliseconds. 0 disables the check (useful\n * for fast tests that don't want to wait). Override via env var\n * `MU_SPAWN_LIVENESS_MS`.\n */\nexport function defaultSpawnLivenessMs(): number {\n const raw = process.env.MU_SPAWN_LIVENESS_MS;\n if (raw === undefined) return 1500;\n const parsed = Number.parseInt(raw, 10);\n if (Number.isNaN(parsed) || parsed < 0) return 1500;\n return parsed;\n}\n\nasync function awaitSpawnLiveness(paneId: string, agentName: string): Promise<void> {\n const ms = defaultSpawnLivenessMs();\n if (ms === 0) return;\n await sleep(ms);\n // Capture-pane first so we have something to attach to the error if the\n // pane is in the process of being torn down (the buffer survives a beat\n // longer than the pane's existence in some tmux builds).\n const scrollback = await capturePane(paneId, { lines: 50 }).catch(() => undefined);\n if (await paneExists(paneId)) return;\n throw new AgentDiedOnSpawnError(agentName, paneId, scrollback);\n}\n\n/**\n * Three cases, all returning a stable pane id:\n * - session doesn't exist → create session+window+pane in one shot\n * - session exists, no such window → create a new window for this agent\n * - session and window both exist → split the window to add a pane\n */\nasync function createOrReusePane(opts: {\n session: string;\n windowName: string;\n command: string;\n cwd?: string;\n env?: Record<string, string>;\n}): Promise<string> {\n if (!(await sessionExists(opts.session))) {\n return newSessionWithPane(opts.session, {\n windowName: opts.windowName,\n command: opts.command,\n cwd: opts.cwd,\n env: opts.env,\n });\n }\n\n const windows = await listWindows(opts.session);\n const matching = windows.find((w) => w.name === opts.windowName);\n\n if (matching) {\n return splitWindow({\n target: `${opts.session}:${opts.windowName}`,\n command: opts.command,\n cwd: opts.cwd,\n env: opts.env,\n });\n }\n\n return newWindow({\n session: opts.session,\n name: opts.windowName,\n command: opts.command,\n cwd: opts.cwd,\n env: opts.env,\n });\n}\n","// mu — agent error classes.\n//\n// Every agent verb that can fail in a typed way has its own error class\n// here. The CLI's classifyError() (src/cli.ts) maps them to exit codes:\n// not found → 3 (AgentNotFoundError)\n// conflict → 4 (AgentExistsError, AgentNotInWorkstreamError,\n// AgentDiedOnSpawnError, WorkspacePreservedError)\n//\n// AgentDiedOnSpawnError reaches into spawn.ts for defaultSpawnLivenessMs\n// — a single, narrow cross-cluster import that documents itself in the\n// error message (\"agent died within Nms of spawn\").\n//\n// Extracted from src/agents.ts as part of refactor_split_large_src_files.\n\nimport type { HasNextSteps, NextStep } from \"../output.js\";\nimport { defaultSpawnLivenessMs } from \"./spawn.js\";\n\nexport class AgentExistsError extends Error implements HasNextSteps {\n override readonly name = \"AgentExistsError\";\n constructor(public readonly agentName: string) {\n // v5: agent names are UNIQUE per (workstream, name) — the same\n // name can legitimately exist in two different workstreams. The\n // pre-v5 message claimed global uniqueness, which (a) lied about\n // the schema and (b) misled operators into closing the existing\n // agent when the actual fix is `-w <other-ws>`.\n super(`agent already exists in this workstream: ${agentName}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n // v5: agents.workstream_id → workstreams.id; there is no\n // `agents.workstream` column. Use the join so this hint\n // actually runs against a v5 DB.\n intent: \"List which workstream(s) already have an agent by this name\",\n command: `mu sql \"SELECT a.name, ws.name AS workstream FROM agents a JOIN workstreams ws ON ws.id = a.workstream_id WHERE a.name = '${this.agentName}'\"`,\n },\n {\n intent: \"Spawn it in a different workstream (per-workstream unique → no clash)\",\n command: `mu agent spawn ${this.agentName} -w <other-workstream>`,\n },\n {\n intent: \"Or close the existing agent in this workstream and re-spawn\",\n command: `mu agent close ${this.agentName} && mu agent spawn ${this.agentName}`,\n },\n { intent: \"Or pick a different name\", command: \"mu agent spawn <new-name>\" },\n ];\n }\n}\n\nexport class AgentNotFoundError extends Error implements HasNextSteps {\n override readonly name = \"AgentNotFoundError\";\n constructor(\n public readonly agentName: string,\n /** Optional workstream context. When set, the message is enriched\n * with `(in workstream <ws>)` so the verb that hit the miss\n * (e.g. `mu workspace create <agent> -w <ws>`) doesn't leave the\n * operator guessing which scope was searched. Optional so existing\n * call sites that only know the agent name keep their original\n * one-line message. */\n public readonly workstream?: string,\n ) {\n super(\n workstream === undefined\n ? `no such agent: ${agentName}`\n : `no such agent: ${agentName} (in workstream ${workstream})`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List agents in current workstream\", command: \"mu agent list\" },\n { intent: \"List agents across ALL workstreams\", command: \"mu agent list -w *\" },\n {\n intent: \"Spawn it now\",\n command: `mu agent spawn ${this.agentName} -w <workstream>`,\n },\n ];\n }\n}\n\n/**\n * Thrown when an entity-targeted verb is invoked with `-w/--workstream\n * <name>` but the named agent lives in a different workstream.\n * Mirrors `TaskNotInWorkstreamError`. Maps to exit code 4 (conflict /\n * wrong scope). Distinguishes \"the user typo'd the workstream\" from\n * \"the agent doesn't exist anywhere\" (which surfaces as\n * `AgentNotFoundError`).\n */\nexport class AgentNotInWorkstreamError extends Error implements HasNextSteps {\n override readonly name = \"AgentNotInWorkstreamError\";\n constructor(\n public readonly agentName: string,\n public readonly expectedWorkstream: string,\n public readonly actualWorkstream: string,\n ) {\n super(`agent ${agentName} is in workstream ${actualWorkstream}, not ${expectedWorkstream}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Use the agent's actual workstream\",\n command: `mu agent show ${this.agentName} -w ${this.actualWorkstream}`,\n },\n {\n intent: \"List agents in the requested workstream\",\n command: `mu agent list -w ${this.expectedWorkstream}`,\n },\n ];\n }\n}\n\n/**\n * Thrown when an agent's pane is created and titled successfully but the\n * spawned process exits within the liveness window (default 1500ms;\n * configurable via `MU_SPAWN_LIVENESS_MS`). The most common cause is the\n * underlying CLI failing fast: a wrapper CLI blocking on a single-instance\n * lock, `claude` rejecting an invalid API key, etc. The agent's last\n * scrollback (when capturable) is attached to help diagnose.\n */\nexport class AgentDiedOnSpawnError extends Error implements HasNextSteps {\n override readonly name = \"AgentDiedOnSpawnError\";\n constructor(\n public readonly agentName: string,\n public readonly paneId: string,\n public readonly scrollback: string | undefined,\n ) {\n const tail = scrollback?.trim();\n const detail = tail ? `\\n\\n--- pane scrollback ---\\n${tail}\\n--- end scrollback ---` : \"\";\n super(\n `agent ${agentName} died within ${defaultSpawnLivenessMs()}ms of spawn (pane ${paneId}). Most common cause: the spawned CLI exited immediately (e.g. a wrapper CLI blocking on its instance lock; set MU_<UPPER_CLI>_COMMAND to a non-blocking variant to bypass).${detail}`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Inspect the dead pane's scrollback for the underlying error\",\n command: `mu agent read ${this.agentName} -n 100`,\n },\n {\n // agent_spawn_liveness_check_trips_on: per-spawn override is\n // the right scope for one-offs (e.g. wrapper CLIs blocking\n // on a per-project solo lock). Listed first so operators\n // reach for it before exporting an env var that leaks into\n // every subsequent spawn in the shell.\n intent: \"Override per spawn (one-off; no env-var leak)\",\n command: `mu agent spawn ${this.agentName} --command \"<cli> <bypass-flag>\" (e.g. pi-meta --no-solo)`,\n },\n {\n intent:\n \"Make the override the default for this CLI (applies to every subsequent spawn in this shell)\",\n command: 'export MU_PI_COMMAND=\"pi-alt --some-flag\"',\n },\n {\n intent: \"Disable the liveness check (CI / known long-lived sh subprocess)\",\n command: \"export MU_SPAWN_LIVENESS_MS=0\",\n },\n { intent: \"Run health check\", command: \"mu doctor\" },\n ];\n }\n}\n\n/**\n * Thrown when `closeAgent` is called on an agent that has an associated\n * workspace AND the caller didn't explicitly opt into discarding it.\n *\n * Background: the FK on `vcs_workspaces.agent` cascades on agent\n * delete, so a naive `closeAgent` drops the workspace registry row\n * but leaves the on-disk dir orphaned (mu can't see it via\n * `mu workspace list / free / path` afterwards). Surfaced during\n * the multi-agent dogfood teardown when three workspaces went\n * orphaned silently.\n *\n * The fix: refuse close if a workspace exists; force the caller to\n * decide. Two actionable resolutions:\n * - `mu workspace free <agent>` first, then close cleanly.\n * - `mu agent close <agent> --discard-workspace` to free the\n * workspace AND close the agent in one shot (lossy: pending\n * changes in the workspace are gone).\n *\n * Maps to exit code 4 (conflict) via the cli.ts handler.\n */\nexport class WorkspacePreservedError extends Error implements HasNextSteps {\n override readonly name = \"WorkspacePreservedError\";\n constructor(\n public readonly agentName: string,\n public readonly workspacePath: string,\n ) {\n super(\n `agent ${agentName} has a workspace at ${workspacePath}; refusing to close (would orphan the on-disk dir)`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Free the workspace first (preserves agent for next step)\",\n command: `mu workspace free ${this.agentName} (--commit to commit pending changes first)`,\n },\n {\n intent: \"Or close + discard the workspace in one shot (lossy)\",\n command: `mu agent close ${this.agentName} --discard-workspace`,\n },\n {\n intent: \"Or just inspect what's in the workspace\",\n command: `cd ${this.workspacePath}`,\n },\n ];\n }\n}\n","// mu — TaskStatus enum + helpers.\n//\n// Single source of truth for \"what statuses can a task have\". The\n// schema (db.ts) has a CHECK clause that mirrors TASK_STATUSES; if you\n// add a status, update both places.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nexport type TaskStatus = \"OPEN\" | \"IN_PROGRESS\" | \"CLOSED\" | \"REJECTED\" | \"DEFERRED\";\n\n/** Every legal task status, in canonical order (matches the schema\n * CHECK clause). Exported so CLI surfaces (`--status` validators,\n * --help text, error messages) name them all in one place; missing\n * one used to silently lie about the supported set. */\nexport const TASK_STATUSES: readonly TaskStatus[] = [\n \"OPEN\",\n \"IN_PROGRESS\",\n \"CLOSED\",\n \"REJECTED\",\n \"DEFERRED\",\n];\n\n/** Statuses that count as 'no longer scheduled work' — used by the\n * goals view and by the dependent-check on reject/defer.\n *\n * (The complement — 'statuses that satisfy a blocked-by edge' — is\n * just `[\"CLOSED\"]` and is hardcoded inline in the SQL views in\n * src/db.ts. A constant for it was tried and reverted: a one-element\n * array doesn't earn its keep, and parameterising the SQL views from\n * a TS const would be brittle.) */\nexport const STATUSES_TERMINAL_OR_PARKED: readonly TaskStatus[] = [\n \"CLOSED\",\n \"REJECTED\",\n \"DEFERRED\",\n];\n\nexport function isTaskStatus(s: string): s is TaskStatus {\n return (TASK_STATUSES as readonly string[]).includes(s);\n}\n\n/** Pipe-separated list of every legal status, e.g.\n * 'OPEN | IN_PROGRESS | CLOSED | REJECTED | DEFERRED'. Single source\n * of truth for --help text and error messages so adding a new status\n * doesn't leave stale lists rotting in the CLI surface. */\nexport const TASK_STATUS_LIST = TASK_STATUSES.join(\" | \");\n","// mu — waitForTasks: block until tasks reach a target status.\n//\n// The orchestrator pattern: dispatch N workers via mu task claim --for,\n// then wait until they're all done before reviewing/merging.\n//\n// Pre-existing alternatives + why this verb exists:\n//\n// awk pipe over `mu log --tail`: works for ONE event but the\n// awk script becomes stateful (tracking 'which of N have closed?')\n// for multi-task waits. Bad shape for SKILL examples.\n//\n// Implementation:\n//\n// 1. Initial check — if the wait condition is already satisfied,\n// exit immediately. No subscription needed.\n// 2. Otherwise, poll the tasks table every pollMs. Same cadence as\n// mu log --tail (default 1000ms). We don't subscribe to\n// agent_logs because (a) we'd still need to re-query tasks to\n// learn the current status, (b) some status changes happen via\n// mu sql which doesn't emit events, and (c) the polling cost is\n// one indexed SELECT every second — cheaper than parsing the\n// log stream.\n// 3. Exit on success (all/any reached) OR timeout. Caller maps\n// timeout to exit code 5.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nimport type { Db } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport { getTask } from \"../tasks.js\";\nimport { StallDetectedDuringWaitError, TaskNotFoundError } from \"./errors.js\";\nimport type { TaskStatus } from \"./status.js\";\n\n// ─── Test seams: poll-sleep + poll counter + stuck-warn writer ─────────\n//\n// Mirror src/tmux.ts's setSleepForTests pattern. Default sleep is a real\n// setTimeout; tests can swap in an instant + counted version to assert\n// poll cadence (the bug fixed alongside this hook silently sleeps a full\n// pollMs past the deadline when pollMs > timeoutMs — see test/tasks.test.ts\n// 'waitForTasks' regression cases).\n//\n// The stuck-warn writer is the second seam: agent_close_discipline_gap\n// added a per-poll \"this task is IN_PROGRESS but owner is needs_input\n// for too long\" warning emitted to stderr; tests intercept it via\n// setWaitStuckWarnForTests so they can assert exactly-once dedupe\n// without scraping process.stderr.\n\nlet currentWaitSleep: (ms: number) => Promise<void> = (ms) =>\n new Promise((resolve) => setTimeout(resolve, ms));\nlet pollCount = 0;\nconst defaultStuckWarn: (msg: string) => void = (msg) => {\n process.stderr.write(msg);\n};\nlet currentStuckWarn: (msg: string) => void = defaultStuckWarn;\n\nexport function setWaitSleepForTests(\n impl: ((ms: number) => Promise<void>) | undefined,\n): (ms: number) => Promise<void> {\n const previous = currentWaitSleep;\n currentWaitSleep = impl ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));\n return previous;\n}\n\n/** Test seam: swap the stderr writer used by the stuck-task warning so\n * unit tests can capture warnings without spying on process.stderr. */\nexport function setWaitStuckWarnForTests(\n impl: ((msg: string) => void) | undefined,\n): (msg: string) => void {\n const previous = currentStuckWarn;\n currentStuckWarn = impl ?? defaultStuckWarn;\n return previous;\n}\n\n/** Total number of polls performed across all `waitForTasks` calls in this\n * process. Tests typically reset before exercising and read after. */\nexport function getWaitPollCount(): number {\n return pollCount;\n}\n\nexport function resetWaitPollCount(): void {\n pollCount = 0;\n}\n\n/** A single task ref the wait verb is watching. Cross-workstream\n * waits arrive as a heterogeneous list of (workstream, name) pairs;\n * the legacy single-workstream call passes the same workstream on\n * every ref. task_wait_cross_workstream. */\nexport interface TaskWaitRef {\n /** The workstream the task lives in. Each ref carries its own so\n * the SDK doesn't need a single \"the workstream\" — cross-ws waits\n * pass refs from multiple workstreams in one call. */\n workstreamName: string;\n /** The task's per-workstream-unique local id. */\n name: string;\n}\n\nexport interface TaskWaitOptions {\n /** Target status. Default 'CLOSED'. */\n status?: TaskStatus;\n /** When true, succeed as soon as ONE listed task reaches the target.\n * Default false: every listed task must reach the target. */\n any?: boolean;\n /** Maximum time to wait, in milliseconds. Default 600_000 (10 min).\n * Pass 0 to wait forever. */\n timeoutMs?: number;\n /** Polling interval. Default 1000ms; overridable for tests. */\n pollMs?: number;\n /** Workstream context applied to bare-string ids. Required when the\n * caller passes `string[]`; ignored when the caller passes\n * `TaskWaitRef[]` (each ref carries its own ws). The legacy\n * single-ws SDK call site keeps its today's shape; the cross-ws\n * callers (CLI verb) pass `TaskWaitRef[]` and omit `workstream`.\n * task_wait_cross_workstream. */\n workstream?: string;\n /** Emit a yellow STUCK warning to stderr (once per task per wait call)\n * when an IN_PROGRESS task's owner has been in `needs_input` for at\n * least this many milliseconds since the agent row's last update.\n * Default 300_000 (5 min). Pass 0 to disable.\n *\n * Surfaced by agent_close_discipline_gap in mufeedback: workers\n * occasionally finish + commit + go idle without running\n * `mu task close <id>`, leaving wait blocked indefinitely. The\n * warning is observation-only — wait keeps polling so the operator\n * (or a wrapping policy) decides whether to force-close, re-prompt,\n * or escalate. */\n stuckAfterMs?: number;\n /** What to do when the `--stuck-after` predicate fires on a watched\n * task. `'warn'` (default) = today's behaviour: yellow STUCK line\n * to stderr (deduped per task per wait call) + corroborating\n * `kind='event'` agent_logs row; wait keeps polling. `'exit'` =\n * same emit + persist, but THEN throw `StallDetectedDuringWaitError`\n * so the CLI wrapper exits 7 (STALL_DETECTED). The exit-action is\n * the unattended-orchestrator escape: a wrapping policy can branch\n * on 7 (idle, ambiguous — operator decides poke vs release) vs 6\n * (dead pane, unambiguous — re-dispatch).\n *\n * Carve-out (lives at the call site, not here): the CLI passes\n * `'exit'` only when the wait target is CLOSED — mirrors exit-6's\n * reaper-flip suppression. With `--status OPEN` the worker reaching\n * needs_input might BE the success path. See\n * task_wait_stall_action_flag. */\n onStall?: \"warn\" | \"exit\";\n /** Optional async hook run BEFORE every snapshot (initial + each\n * poll iteration). The CLI uses this to reconcile the workstream\n * each tick (reaper flips IN_PROGRESS → OPEN for dead-pane\n * workers) and to throw a typed error when a reaper-flip on a\n * watched task should abandon the wait — see\n * task_wait_reconcile_dead_panes. Throwing from `beforePoll`\n * propagates out of `waitForTasks` unchanged.\n *\n * Kept as a generic seam (not a `--reconcile`-shaped option) so\n * the SDK module stays free of tmux/reconcile imports — that\n * layering belongs above the SDK in the CLI wrapper. */\n beforePoll?: () => Promise<void>;\n}\n\nexport interface TaskWaitTaskState {\n /** The workstream this task lives in. Cross-workstream waits\n * return a mixed list; the workstream is part of identity.\n * task_wait_cross_workstream. */\n workstreamName: string;\n /** The task's per-workstream-unique name. */\n name: string;\n /** Current status (at the moment we exit). */\n status: TaskStatus;\n /** Owner at exit time (NULL when unowned, after release, or after\n * the reaper flipped IN_PROGRESS → OPEN due to a dead pane). */\n owner: string | null;\n /** True when this task's status equals the target. */\n reachedTarget: boolean;\n /** True when the task is IN_PROGRESS, owned by a registered agent\n * whose detected status is `needs_input` for >= `stuckAfterMs`.\n * Surfaces the agent_close_discipline_gap pattern: worker finished +\n * committed but skipped `mu task close <id>`. Backwards-compatible\n * signal — callers ignoring it see no behaviour change. */\n stuck: boolean;\n}\n\nexport interface TaskWaitResult {\n /** Per-task state at exit time. Same length and order as the input list. */\n tasks: TaskWaitTaskState[];\n /** True when EVERY task reached the target (the --all condition). */\n allReached: boolean;\n /** True when AT LEAST ONE task reached the target (the --any condition). */\n anyReached: boolean;\n /** Wall-clock time spent waiting, in ms (always >= 0). */\n elapsedMs: number;\n /** True when we exited because of the timeout, not because the wait\n * condition was met. allReached / anyReached can still be true on\n * partial progress when timedOut is true. */\n timedOut: boolean;\n}\n\n/**\n * Block until a set of tasks reaches `opts.status` (default CLOSED).\n * Returns a result describing the final state — the caller decides\n * whether to treat partial-progress timeouts as success or failure\n * (the CLI maps a clean exit to 0, a timeout to 5).\n *\n * Pre-flight: every task in `localIds` MUST exist; missing ones throw\n * TaskNotFoundError before any waiting begins. This is loud-fail by\n * design — a typo'd id silently waiting forever is the worst-case UX.\n */\nexport async function waitForTasks(\n db: Db,\n input: readonly TaskWaitRef[] | readonly string[],\n opts: TaskWaitOptions,\n): Promise<TaskWaitResult> {\n if (input.length === 0) {\n throw new Error(\"waitForTasks: refs must be non-empty\");\n }\n // Normalise to TaskWaitRef[]. The string[] shape preserves today's\n // single-workstream SDK contract (callers pass `workstream` on\n // opts); the TaskWaitRef[] shape carries its own ws per ref so\n // cross-workstream waits don't need a sentinel.\n const refs: readonly TaskWaitRef[] = input.map((entry) => {\n if (typeof entry === \"string\") {\n if (opts.workstream === undefined)\n throw new Error(\n \"waitForTasks: string ids require opts.workstream; pass TaskWaitRef[] for cross-workstream waits\",\n );\n return { workstreamName: opts.workstream, name: entry };\n }\n return entry;\n });\n const target: TaskStatus = opts.status ?? \"CLOSED\";\n const wantAny = opts.any === true;\n const timeoutMs = opts.timeoutMs ?? 600_000;\n const pollMs = opts.pollMs ?? 1000;\n const stuckAfterMs = opts.stuckAfterMs ?? 300_000;\n const onStall: \"warn\" | \"exit\" = opts.onStall ?? \"warn\";\n const deadline = timeoutMs > 0 ? Date.now() + timeoutMs : Number.POSITIVE_INFINITY;\n const startedAt = Date.now();\n\n // Pre-flight: every ref must exist in its own workstream scope.\n // Cross-workstream waits validate each ref against its declared ws\n // — a typo in the workstream half of `<ws>/<name>` should land here\n // with a clear TaskNotFoundError, not silently wait forever.\n for (const ref of refs) {\n if (getTask(db, ref.name, ref.workstreamName) === undefined)\n throw new TaskNotFoundError(`${ref.workstreamName}/${ref.name}`);\n }\n\n // Per-(ws,name) dedupe: emit the STUCK warning at most ONCE per wait\n // call, not once per poll cycle. Operators don't want their stderr\n // filled with the same yellow line every second; one nudge is enough.\n const stuckWarned = new Set<string>();\n const refKey = (ref: TaskWaitRef): string => `${ref.workstreamName}/${ref.name}`;\n\n /**\n * Detect the agent_close_discipline_gap pattern for one task:\n * IN_PROGRESS in the DB, owned by a registered agent whose status\n * is `needs_input` and whose `updated_at` is older than\n * `stuckAfterMs`. We query agents directly (not via getAgent) to\n * avoid an import cycle (src/agents.ts already imports from\n * src/tasks.ts).\n */\n const isStuck = (status: TaskStatus, owner: string | null, workstream: string): boolean => {\n if (stuckAfterMs <= 0) return false;\n if (status !== \"IN_PROGRESS\" || !owner) return false;\n // owner is the operator-facing agent name; agents.name is\n // per-workstream unique in v5. Scope the lookup by workstream so\n // a same-named worker elsewhere doesn't spuriously mark this task\n // stuck.\n const row = db\n .prepare(\n `SELECT a.status AS status, a.updated_at AS updated_at\n FROM agents a\n JOIN workstreams ws ON ws.id = a.workstream_id\n WHERE a.name = ? AND ws.name = ?`,\n )\n .get(owner, workstream) as { status: string; updated_at: string } | undefined;\n if (!row || row.status !== \"needs_input\") return false;\n const ageMs = Date.now() - new Date(row.updated_at).getTime();\n return ageMs >= stuckAfterMs;\n };\n\n /** Read current state of all tasks; returns the result shape. */\n const snapshot = (): TaskWaitResult => {\n const tasks: TaskWaitTaskState[] = refs.map((ref) => {\n const row = getTask(db, ref.name, ref.workstreamName);\n // Defensive: if a task was deleted mid-wait, treat as 'never\n // reached'. (Not the same as TaskNotFoundError pre-flight —\n // deletion mid-wait shouldn't crash the wait; it's a legitimate\n // state change.)\n const status = (row?.status ?? \"OPEN\") as TaskStatus;\n const owner = row?.ownerName ?? null;\n const stuck = isStuck(status, owner, ref.workstreamName);\n const key = refKey(ref);\n if (stuck && !stuckWarned.has(key)) {\n stuckWarned.add(key);\n // Yellow ANSI escape inline (no picocolors import — keeps the\n // SDK module dep-free; the CLI layer already pulls picocolors).\n // The message is one line, prefixed with `mu task wait:` so\n // log greppers can target it. Cross-ws waits include the\n // qualified `<ws>/<name>` so the operator sees which.\n currentStuckWarn(\n `\\x1b[33mmu task wait: ${key} stuck — owner=${owner ?? \"<none>\"} in needs_input ` +\n `(>= ${stuckAfterMs}ms since last status change). ` +\n `Worker likely committed but skipped \\`mu task close ${ref.name}\\`.\\x1b[0m\\n`,\n );\n // Persist a corroborating kind='event' row so other consumers\n // (mu state, mu log --kind event, dashboards) see the same\n // signal that the new derived `idle` flag surfaces in the\n // agents table. The stderr warning stays — observation-only,\n // operator decides recovery. See idle_assigned_agent_detection.\n const ageSecs = Math.round(stuckAfterMs / 1000);\n emitEvent(\n db,\n ref.workstreamName,\n `agent stalled ${owner ?? \"<none>\"} owns ${ref.name} for ${ageSecs}s`,\n owner ?? \"system\",\n );\n // task_wait_stall_action_flag: with `--on-stall exit` (the\n // unattended-orchestrator escape), the same emit + persist\n // happens but we then throw — the CLI's classifyError maps\n // this to exit code 7 (STALL_DETECTED). The carve-out\n // (target=CLOSED only) lives at the call site: the CLI\n // wrapper passes `onStall: 'exit'` only when target=CLOSED.\n // Precedence vs exit-6 (dead pane): the reaper-flip check\n // throws inside `beforePoll` BEFORE snapshot() runs, so a\n // watched task that's both reaper-flipped AND stale never\n // reaches this branch (status would already be OPEN).\n if (onStall === \"exit\") {\n throw new StallDetectedDuringWaitError(ref.name, owner, ref.workstreamName, ageSecs);\n }\n }\n return {\n workstreamName: ref.workstreamName,\n name: ref.name,\n status,\n owner,\n reachedTarget: status === target,\n stuck,\n };\n });\n const reachedCount = tasks.filter((t) => t.reachedTarget).length;\n return {\n tasks,\n allReached: reachedCount === tasks.length,\n anyReached: reachedCount > 0,\n elapsedMs: Date.now() - startedAt,\n timedOut: false,\n };\n };\n\n /** Has the wait condition been met? */\n const isDone = (snap: TaskWaitResult): boolean => (wantAny ? snap.anyReached : snap.allReached);\n\n // Initial check: maybe we're already done. Run beforePoll first so\n // the CLI's per-poll reconcile (task_wait_reconcile_dead_panes) runs\n // even on the immediate-exit path — a dead-pane worker that died\n // BEFORE the operator typed `mu task wait` should still fail fast.\n if (opts.beforePoll) await opts.beforePoll();\n let snap = snapshot();\n if (isDone(snap)) return snap;\n\n // Poll loop.\n //\n // Sleep is clamped to `min(pollMs, deadline - now)` so the function\n // returns within `timeoutMs + small slack`, never `pollMs` later.\n // Without the clamp, `pollMs=10000, timeoutMs=100` sleeps a full 10s\n // before noticing the deadline expired. When the clamp goes <= 0 we\n // skip the sleep entirely and re-snapshot before bailing on the\n // timeout — gives the wait one last chance at a winning state right\n // at the deadline boundary, and avoids passing 0 / negatives to\n // setTimeout (which has implementation-defined behaviour).\n for (;;) {\n const now = Date.now();\n if (now >= deadline) {\n return { ...snap, timedOut: true };\n }\n const sleepMs =\n deadline === Number.POSITIVE_INFINITY ? pollMs : Math.min(pollMs, deadline - now);\n if (sleepMs > 0) {\n await currentWaitSleep(sleepMs);\n }\n pollCount += 1;\n if (opts.beforePoll) await opts.beforePoll();\n snap = snapshot();\n if (isDone(snap)) return snap;\n }\n}\n","// mu — task lifecycle verbs: setTaskStatus, closeTask, openTask,\n// rejectTask, deferTask + supporting types.\n//\n// Lifecycle = \"transition a task from one status to another, with\n// the right side effects (auto-snapshot before mutating, emit\n// agent_logs event, refresh pane title via the caller, validate\n// guard rails like 'reject would strand dependents')\".\n//\n// EvidenceOption is shared with claim/release (in tasks/claim.ts) and\n// re-exported here as the canonical home; claim.ts imports from this\n// file.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nimport type { Db } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport { captureSnapshot } from \"../snapshots.js\";\nimport { getTask, getTaskEdgesWithStatus } from \"../tasks.js\";\nimport { TaskHasOpenDependentsError, TaskNotFoundError } from \"./errors.js\";\nimport type { TaskStatus } from \"./status.js\";\n\nexport interface SetStatusResult {\n /** Status before the call. */\n previousStatus: TaskStatus;\n /** Status after the call (== requested status). */\n status: TaskStatus;\n /** True iff the row actually changed. False on idempotent no-op. */\n changed: boolean;\n}\n\n/**\n * Optional evidence string carried on lifecycle verbs (close / open /\n * claim / release). Lands in the auto-emitted `kind='event'` payload\n * verbatim, prefixed with `evidence=`. The first inch of distinguishing\n * \"observed\" from \"claimed\" state per an internal critique: the\n * verb still trusts the caller (it's not a verifier), but the audit\n * trail records what the caller said it relied on.\n */\nexport interface EvidenceOption {\n evidence?: string;\n}\n\n/** Render the optional `--evidence \"<text>\"` payload as the trailing\n * ' evidence=\"...\"' on every state-changing event. Exported because\n * claimTask/releaseTask in src/tasks/claim.ts also use it. */\nexport function evidenceSuffix(opts: EvidenceOption | undefined): string {\n if (!opts || opts.evidence === undefined) return \"\";\n return ` evidence=${JSON.stringify(opts.evidence)}`;\n}\n\n/**\n * Flip a task's status to any of OPEN / IN_PROGRESS / CLOSED.\n * Idempotent: setting a task to its current status is a no-op (returns\n * `changed: false`) rather than throwing. Owner is unchanged.\n */\nexport function setTaskStatus(\n db: Db,\n localId: string,\n status: TaskStatus,\n opts: EvidenceOption & { workstream: string },\n): SetStatusResult {\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n if (before.status === status) {\n return { previousStatus: before.status, status, changed: false };\n }\n // v5: tasks.local_id is per-workstream unique. Scope to the row's\n // workstream so the UPDATE doesn't accidentally touch a same-named\n // task in another workstream.\n db.prepare(\n `UPDATE tasks SET status = ?, updated_at = ?\n WHERE local_id = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)`,\n ).run(status, new Date().toISOString(), localId, before.workstreamName);\n emitEvent(\n db,\n before.workstreamName,\n `task status ${localId} (${before.status} → ${status})${evidenceSuffix(opts)}`,\n );\n return { previousStatus: before.status, status, changed: true };\n}\n\n/** Result of `closeTask` when called with `ifReady: true` and the\n * task is NOT yet ready to close (still has at least one OPEN /\n * IN_PROGRESS blocker). Distinguished from a regular `SetStatusResult`\n * by the literal `skipped` field; the CLI keys on it to switch\n * between the \"closed\" and \"waiting\" rendering paths.\n *\n * Surfaced in `fb_umbrella_no_auto_close` (impact=60): a wave umbrella\n * with N blockers stayed OPEN after every blocker reached a terminal\n * status. `--if-ready` is the cheap fix: bare `mu task close` is\n * unchanged (closes regardless), `--if-ready` is a no-op unless every\n * blocker is in a terminal status (CLOSED / REJECTED / DEFERRED).\n * Reject and defer satisfy the predicate too because `--if-ready`'s\n * job is to fire when the umbrella has nothing left to wait for, and\n * a rejected/deferred blocker is no longer being waited on. */\nexport interface CloseSkippedResult {\n /** Always 'not_ready' when set; future cause-codes can extend this\n * without reshaping the JSON payload (the literal-union narrows\n * safely in the CLI rendering path). */\n skipped: \"not_ready\";\n /** Status before the call (always the current status, no change). */\n previousStatus: TaskStatus;\n /** Status after the call (== previousStatus, since we no-op). */\n status: TaskStatus;\n /** Always false on a skip (no row mutated). */\n changed: false;\n /** Local ids of every blocker still in OPEN or IN_PROGRESS, sorted\n * alphabetically for deterministic rendering. Empty list is\n * impossible on this branch — the no-op only fires when ≥1\n * blocker is non-terminal. */\n blockingIds: string[];\n}\n\nexport interface CloseTaskOptions extends EvidenceOption {\n workstream: string;\n /** When true, no-op the close unless every blocker is in a terminal\n * status (CLOSED / REJECTED / DEFERRED). Returns a\n * `CloseSkippedResult` carrying the still-blocking ids; the CLI\n * renders the skip with a Next: hint pointing at `mu task wait`.\n * When false / omitted, behaves as bare `closeTask` (closes\n * regardless of blocker status). */\n ifReady?: boolean;\n}\n\n/** Convenience: setTaskStatus(db, id, \"CLOSED\"). Accepts evidence.\n * Pre-snapshots the DB (snap_design §CAPTURE STRATEGY > WHEN). Skipped\n * for the idempotent no-op (already CLOSED) so we don't accumulate\n * empty-delta snapshots on retry loops.\n *\n * With `ifReady: true`, returns a `CloseSkippedResult` (no mutation,\n * no snapshot) when any blocker is still OPEN / IN_PROGRESS. Used by\n * `mu task close --if-ready` so an orchestrator can fire-and-forget\n * the umbrella close after every blocker resolves without first\n * re-querying the graph. */\nexport function closeTask(\n db: Db,\n localId: string,\n opts: CloseTaskOptions,\n): SetStatusResult | CloseSkippedResult {\n const before = getTask(db, localId, opts.workstream);\n if (opts.ifReady && before) {\n // Inspect direct blockers only — the umbrella convention is one\n // hop (umbrella -[blocked-by]→ each wave task). Transitive depth\n // doesn't matter: if any direct blocker is non-terminal, the\n // umbrella isn't ready; if every direct blocker is terminal,\n // their own ancestry was satisfied as a precondition for them\n // closing/rejecting/deferring in the first place.\n const edges = getTaskEdgesWithStatus(db, localId, before.workstreamName);\n const blocking = edges.blockers\n .filter((e) => e.status === \"OPEN\" || e.status === \"IN_PROGRESS\")\n .map((e) => e.name)\n .sort();\n if (blocking.length > 0) {\n return {\n skipped: \"not_ready\",\n previousStatus: before.status,\n status: before.status,\n changed: false,\n blockingIds: blocking,\n };\n }\n }\n if (before && before.status !== \"CLOSED\") {\n captureSnapshot(db, `task close ${localId}`, before.workstreamName);\n }\n return setTaskStatus(db, localId, \"CLOSED\", opts);\n}\n\n/** Convenience: setTaskStatus(db, id, \"OPEN\"). Owner intentionally NOT\n * cleared — use `releaseTask` for that. Accepts evidence. */\nexport function openTask(\n db: Db,\n localId: string,\n opts: EvidenceOption & { workstream: string },\n): SetStatusResult {\n return setTaskStatus(db, localId, \"OPEN\", opts);\n}\n\n// ─── rejectTask / deferTask (terminal-but-blocking transitions) ────\n//\n// REJECTED and DEFERRED both leave the task off the active scheduler\n// (gone from `ready`, `goals`, track count) but, unlike CLOSED, do NOT\n// satisfy a `--blocked-by` edge. A REJECTED / DEFERRED task therefore\n// silently strands every OPEN/IN_PROGRESS dependent. We refuse the\n// transition unless either there are no open dependents OR the caller\n// passes `--cascade` to apply the same status to every transitive\n// dependent.\n\nexport interface RejectDeferOptions extends EvidenceOption {\n /** Workstream context for the root task. All internal task lookups\n * (including the dependent walk) scope to this workstream. */\n workstream: string;\n /** If true, walk the transitive dependent closure and (with `yes`)\n * apply the same status to every dependent, atomically. Without\n * `yes`, runs as a dry-run: returns the list of tasks that WOULD\n * be swept (changedIds) with `dryRun: true` and changes nothing.\n * Logs one event per task (via setTaskStatus) on commit. */\n cascade?: boolean;\n /** Required to actually commit a `cascade` operation. Without it,\n * cascade is dry-run only — prints the affected dependents so the\n * caller can verify before sweeping. Mirrors `mu workstream destroy\n * --yes`. Surfaced in mufeedback bug_cascade_reject_too_aggressive\n * when an accidentally-cascaded reject swept hud_dogfood (which had\n * independent merit and needed reopening). */\n yes?: boolean;\n}\n\nexport interface RejectDeferResult {\n /** Tasks that actually changed status, in cascade order (root first). */\n changedIds: string[];\n /** The status now stamped on every changedId. */\n status: TaskStatus;\n /** True iff anything changed. False on a clean idempotent no-op\n * (root task already in target status, no dependents). */\n changed: boolean;\n /** True iff this was a `cascade` dry-run (cascade requested without\n * `yes`). In that case `changedIds` lists tasks that WOULD be\n * swept; the DB is unchanged. */\n dryRun: boolean;\n /** Tasks that would be touched by a cascade. Same as `changedIds`\n * on a dry-run; populated even on a commit so the caller can\n * report what was swept. */\n affectedIds: string[];\n}\n\n/** Reject a task: terminal 'won't do' (out of scope, duplicate, wontfix).\n * Refuses if dependents are open unless `--cascade`.\n * Pre-snapshots once at the verb level so a cascade onto N children\n * produces a single snapshot, not N. Skipped for the idempotent no-op. */\nexport function rejectTask(db: Db, localId: string, opts: RejectDeferOptions): RejectDeferResult {\n const before = getTask(db, localId, opts.workstream);\n if (before && before.status !== \"REJECTED\") {\n captureSnapshot(db, `task reject ${localId}`, before.workstreamName);\n }\n return setTerminalOrParked(db, localId, \"REJECTED\", opts);\n}\n\n/** Defer a task: parked, may revisit. Same dependent-stranding semantics\n * as reject (DEFERRED also doesn't satisfy a `--blocked-by` edge).\n * Pre-snapshots once at the verb level. Skipped for the idempotent no-op. */\nexport function deferTask(db: Db, localId: string, opts: RejectDeferOptions): RejectDeferResult {\n const before = getTask(db, localId, opts.workstream);\n if (before && before.status !== \"DEFERRED\") {\n captureSnapshot(db, `task defer ${localId}`, before.workstreamName);\n }\n return setTerminalOrParked(db, localId, \"DEFERRED\", opts);\n}\n\nfunction setTerminalOrParked(\n db: Db,\n localId: string,\n status: \"REJECTED\" | \"DEFERRED\",\n opts: RejectDeferOptions,\n): RejectDeferResult {\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n\n // Find all open (OPEN or IN_PROGRESS) tasks that transitively depend\n // on this one. Forward-edge recursive CTE from localId, scoped by\n // the root task's workstream.\n const openDependents = findOpenDependents(db, localId, before.workstreamName);\n\n if (openDependents.length > 0 && !opts.cascade) {\n const verb = status === \"REJECTED\" ? \"reject\" : \"defer\";\n throw new TaskHasOpenDependentsError(localId, verb, openDependents);\n }\n\n const affectedIds =\n openDependents.length > 0 && opts.cascade ? [localId, ...openDependents] : [localId];\n\n // Cascade dry-run: cascade requested but --yes missing. Don't touch\n // the DB; return the would-be-affected list so the CLI can render\n // a 'about to sweep these N tasks; rerun with --yes' preview.\n // Mirrors `mu workstream destroy` semantics. Single-task case\n // (openDependents == 0, cascade flag irrelevant) skips the dry-run\n // since there's nothing to preview.\n if (opts.cascade && !opts.yes && openDependents.length > 0) {\n return {\n changedIds: affectedIds,\n status,\n changed: false,\n dryRun: true,\n affectedIds,\n };\n }\n\n // Apply to root first, then dependents in BFS order. setTaskStatus\n // emits one event per task and is idempotent (no-op if already in\n // target status). Every UPDATE scopes to the root's workstream\n // (dependents must share it — cross-ws edges are forbidden).\n const childOpts: EvidenceOption & { workstream: string } = {\n workstream: before.workstreamName,\n ...(opts.evidence !== undefined ? { evidence: opts.evidence } : {}),\n };\n const changedIds: string[] = [];\n for (const id of affectedIds) {\n const r = setTaskStatus(db, id, status, childOpts);\n if (r.changed) changedIds.push(id);\n }\n\n return {\n changedIds,\n status,\n changed: changedIds.length > 0,\n dryRun: false,\n affectedIds,\n };\n}\n\n/** Open dependents that would be stranded if `taskId` were rejected /\n * deferred. The walk PRUNES at CLOSED nodes: a CLOSED intermediate\n * has already satisfied its blocked-by edge, so its downstream is\n * independent of whatever happens to `taskId` and must NOT be swept.\n * REJECTED / DEFERRED intermediates also stop the walk — their\n * downstream is already stranded by them, not by `taskId`, and a\n * cascade from here would (a) double-flip them or (b) overwrite a\n * previous explicit decision.\n *\n * Ordering: BFS-equivalent via DISTINCT + ORDER BY local_id; cascade\n * applies one row at a time so each setTaskStatus is logged. */\nfunction findOpenDependents(db: Db, taskLocalId: string, workstream: string): string[] {\n // Resolve the seed task to its surrogate id, then walk forward\n // edges in surrogate-id space; project back to local_id at the end.\n // Scope the seed by workstream (v5 per-workstream local_id) so a\n // same-named task elsewhere can't seed a cascade in this workstream\n // (bug_v5_name_clash_silent_misroute).\n const seed = db\n .prepare(\n `SELECT id FROM tasks WHERE local_id = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)`,\n )\n .get(taskLocalId, workstream) as { id: number } | undefined;\n if (!seed) return [];\n const rows = db\n .prepare(\n `WITH RECURSIVE forward(node) AS (\n SELECT e.to_task_id\n FROM task_edges e\n JOIN tasks t ON t.id = e.to_task_id\n WHERE e.from_task_id = ?\n AND t.status IN ('OPEN', 'IN_PROGRESS')\n UNION\n SELECT e.to_task_id\n FROM task_edges e\n JOIN forward f ON f.node = e.from_task_id\n JOIN tasks t ON t.id = e.to_task_id\n WHERE t.status IN ('OPEN', 'IN_PROGRESS')\n )\n SELECT DISTINCT t.local_id AS local_id FROM forward f\n JOIN tasks t ON t.id = f.node\n ORDER BY t.local_id`,\n )\n .all(seed.id) as { local_id: string }[];\n return rows.map((r) => r.local_id);\n}\n","// mu — claim/release/resolveActorIdentity verbs.\n//\n// claimTask is the heart of mu's coordination protocol: an atomic\n// CAS via a single SQL UPDATE, with two flavours:\n//\n// \"worker claim\" : --for <name> sets owner=<name> (FK to agents.name)\n// \"anonymous claim\": --self keeps owner=NULL but flips status to\n// IN_PROGRESS and records the actor\n// in agent_logs\n//\n// resolveActorIdentity is the env-aware identity helper: pane title\n// > MU_AGENT_NAME > USER > 'orchestrator'. Used by --self.\n//\n// Extracted from src/tasks.ts as part of refactor_split_large_src_files.\n\nimport type { Db } from \"../db.js\";\nimport { emitEvent, formatClaimEvent } from \"../logs.js\";\nimport { captureSnapshot } from \"../snapshots.js\";\nimport { getTask } from \"../tasks.js\";\nimport { currentAgentName } from \"../tmux.js\";\nimport { ClaimerNotRegisteredError, TaskAlreadyOwnedError, TaskNotFoundError } from \"./errors.js\";\nimport { type EvidenceOption, evidenceSuffix } from \"./lifecycle.js\";\nimport type { TaskStatus } from \"./status.js\";\n\nexport interface ReleaseResult {\n /** The previous owner (null if the task was already unowned). */\n previousOwnerName: string | null;\n /** Status before the release. */\n previousStatus: TaskStatus;\n /** Status after the release. */\n status: TaskStatus;\n /** True iff owner OR status actually changed. */\n changed: boolean;\n}\n\nexport interface ReleaseTaskOptions extends EvidenceOption {\n /** Workstream context for the task (v5: tasks.local_id is\n * per-workstream unique). */\n workstream: string;\n /** Force `status = OPEN` regardless of the current status. Without\n * this flag, `IN_PROGRESS` is also flipped to `OPEN` automatically\n * (so a released task isn't left structurally stranded with\n * `owner=NULL, status=IN_PROGRESS`); CLOSED / REJECTED / DEFERRED\n * are preserved. `--reopen` is the override for the rarer \"un-\n * close and hand back to the pool\" workflow. */\n reopen?: boolean;\n}\n\n/**\n * Release a task: clear `tasks.owner`.\n *\n * Status side-effects (review_release_open_in_progress_inconsistency):\n * - IN_PROGRESS → OPEN automatically (without it, the task is\n * stranded: no owner to drive it forward, but `mu task next`\n * skips it because it's not OPEN).\n * - OPEN / CLOSED / REJECTED / DEFERRED preserved.\n * - `--reopen` forces OPEN regardless of current status — the\n * escape hatch for un-closing a CLOSED owned task in one verb.\n *\n * Idempotent: releasing an already-unowned task with no `--reopen` and\n * no IN_PROGRESS status is a no-op (returns `changed: false`).\n * Throws TaskNotFoundError on missing.\n */\nexport function releaseTask(db: Db, localId: string, opts: ReleaseTaskOptions): ReleaseResult {\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n\n // Default: auto-flip IN_PROGRESS → OPEN so the released task isn't\n // left in the structurally weird owner=NULL/IN_PROGRESS limbo.\n // --reopen still wins for any status (CLOSED / REJECTED / DEFERRED).\n const newStatus: TaskStatus = opts.reopen\n ? \"OPEN\"\n : before.status === \"IN_PROGRESS\"\n ? \"OPEN\"\n : before.status;\n const ownerChanges = before.ownerName !== null;\n const statusChanges = newStatus !== before.status;\n\n if (!ownerChanges && !statusChanges) {\n return {\n previousOwnerName: before.ownerName,\n previousStatus: before.status,\n status: before.status,\n changed: false,\n };\n }\n\n // Pre-mutation snapshot — release wipes ownership which is\n // irrecoverable from history (we'd lose 'who was working on this').\n captureSnapshot(db, `task release ${localId}`, before.workstreamName);\n\n db.prepare(\n `UPDATE tasks SET owner_id = NULL, status = ?, updated_at = ?\n WHERE local_id = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)`,\n ).run(newStatus, new Date().toISOString(), localId, before.workstreamName);\n const statusBit = statusChanges ? `, ${before.status} → ${newStatus}` : \"\";\n emitEvent(\n db,\n before.workstreamName,\n `task release ${localId} (was owner=${before.ownerName ?? \"none\"}${statusBit})${evidenceSuffix(opts)}`,\n );\n return {\n previousOwnerName: before.ownerName,\n previousStatus: before.status,\n status: newStatus,\n changed: true,\n };\n}\n\n// ─── claimTask (verb) ──────────────────────────────────────────────────\n\nexport interface ClaimTaskOptions extends EvidenceOption {\n /** Workstream context for both the task and the claiming agent.\n * v5: agents.name and tasks.local_id are per-workstream unique;\n * the task lookup AND the agent FK lookup scope to this\n * workstream so a same-named task or worker elsewhere can't be\n * silently picked. The CLI always passes this from the resolved\n * -w / $MU_SESSION. */\n workstream: string;\n /**\n * Override the agent name. If omitted, derived from the current pane's\n * title via `tmux display-message -t $TMUX_PANE -p '#{pane_title}'`.\n *\n * Mutually exclusive with `self: true`.\n */\n agentName?: string;\n /**\n * Workstream that the claimer agent lives in. When omitted, defaults\n * to `opts.workstream` (today's same-workstream behaviour). Set by\n * the CLI when `mu task claim X -w A --for B/worker-1` qualifies the\n * `--for` ref with a different workstream prefix\n * (`task_claim_for_cross_workstream`).\n *\n * Cross-workstream ownership is structurally allowed by the schema:\n * `tasks.owner_id` is an INTEGER FK to `agents.id` with no\n * workstream qualifier on the agent side. The per-workstream UNIQUE\n * on `agents(workstream_id, name)` is what previously made the\n * SDK's name → id lookup scope to one workstream; this option\n * widens that lookup to a different workstream when the operator\n * dispatches across a workstream boundary. The agent's own\n * workstream remains unchanged — only the task's `owner_id` points\n * out-of-workstream.\n */\n agentWorkstream?: string;\n /**\n * Anonymous claim: write `owner = NULL` instead of resolving an agent\n * name and checking the FK. Use when the actor is the orchestrator\n * (or a script, or a human) doing direct work in a workstream they\n * aren't a registered worker in.\n *\n * The actor name is still recorded — it ends up in `agent_logs.source`\n * for the auto-emitted `task claim` event — so provenance is preserved.\n * Just not in the FK column.\n *\n * Resolution order for the actor name (used as the log source):\n * 1. `actor` if explicitly passed.\n * 2. Current pane title (when `$TMUX_PANE` is set).\n * 3. `$USER`.\n * 4. The literal string 'unknown'.\n *\n * Mutually exclusive with `agentName` (the two are alternative\n * answers to \"who's the actor for this claim?\"). Passing both is a\n * usage error.\n */\n self?: boolean;\n /**\n * Override the actor name used for the log source when `self: true`.\n * Ignored when `self: false`. Useful when the orchestrator wants to\n * attribute the work to a meaningful name rather than the pane\n * title (e.g. \"deploy-bot\" rather than \"pi-mu\").\n */\n actor?: string;\n}\n\nexport interface ClaimResult {\n /** The agent now owning the task, or null when the claim was anonymous (--self). */\n ownerName: string | null;\n /** The actor recorded in the agent_logs event — the agent name for a\n * registered-worker claim, or the resolved actor for --self. */\n actorName: string;\n /** The previous owner (null if it was unowned). */\n previousOwnerName: string | null;\n /** The status BEFORE the claim; post-claim is IN_PROGRESS unless was CLOSED. */\n previousStatus: TaskStatus;\n /** The status AFTER the claim. */\n status: TaskStatus;\n}\n\n/**\n * Claim a task. Two modes:\n *\n * Worker claim (default):\n * Resolve an agent name from `opts.agentName` or from $TMUX_PANE's\n * pane title. The name MUST exist in the agents table (FK on\n * tasks.owner). Sets `owner = <name>`. This is what mu-spawned\n * workers do, and what `mu task claim --for <worker>` does for\n * orchestrator dispatch.\n *\n * Anonymous claim (--self):\n * Skip the name -> agents FK lookup entirely. Sets `owner = NULL`.\n * Records the actor in `agent_logs.source` instead. This is the\n * orchestrator-doing-direct-work path — the actor is logged but\n * not registered as a worker pane.\n *\n * Status side-effect: OPEN -> IN_PROGRESS; IN_PROGRESS / CLOSED unchanged.\n *\n * Concurrency: the worker-claim path uses a single-statement CAS UPDATE\n * with `WHERE owner IS NULL OR owner = ?` so two workers racing to\n * claim the same task can't both win. The anonymous path uses\n * `WHERE owner IS NULL` (anonymous claims don't 'own' the task in any\n * exclusive sense; if it's already owned by anyone, the anonymous claim\n * is a TaskAlreadyOwnedError just like a worker claim would be).\n */\nexport async function claimTask(\n db: Db,\n localId: string,\n opts: ClaimTaskOptions,\n): Promise<ClaimResult> {\n if (opts.self === true && opts.agentName !== undefined) {\n throw new Error(\"claimTask: --self and --for are mutually exclusive\");\n }\n\n if (opts.self === true) {\n return claimSelf(db, localId, opts);\n }\n\n // ── Worker claim path (registered agent owns the task) ──\n // currentAgentName() parses 'name · status · task' titles back to\n // just the name token — the registry FK is keyed on agents.name,\n // so the parser is essential after composeAgentTitle decorates.\n const agentName = opts.agentName ?? (await currentAgentName());\n if (!agentName) {\n throw new Error(\n \"claimTask: no agent name (pass opts.agentName, run inside an mu-spawned pane with $TMUX_PANE set, or pass --self for an anonymous claim)\",\n );\n }\n\n // Resolve the claiming agent to its surrogate id within the agent's\n // workstream — defaults to opts.workstream (today's same-ws path),\n // or opts.agentWorkstream when the CLI dispatched across a\n // workstream boundary via a qualified `--for <ws>/<name>` ref\n // (task_claim_for_cross_workstream).\n //\n // The schema permits cross-workstream owner_id assignment (FK to\n // agents.id only); the per-workstream UNIQUE on agents.name is the\n // only reason this SELECT was scoped narrowly before. Bare-name\n // dispatch keeps that scope to honour today's behaviour; qualified\n // dispatch widens it to the named workstream so the agent resolves\n // there.\n const claimerWorkstream = opts.agentWorkstream ?? opts.workstream;\n const claimerRow = db\n .prepare(\n `SELECT a.id AS id\n FROM agents a JOIN workstreams ws ON ws.id = a.workstream_id\n WHERE a.name = ? AND ws.name = ?`,\n )\n .get(agentName, claimerWorkstream) as { id: number } | undefined;\n if (!claimerRow) {\n const paneIdFromEnv = opts.agentName === undefined ? (process.env.TMUX_PANE ?? null) : null;\n throw new ClaimerNotRegisteredError(agentName, paneIdFromEnv);\n }\n\n return db.transaction(() => {\n // Resolve the task within opts.workstream. This locks the\n // (workstream, local_id) pair for the rest of the transaction.\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n\n const now = new Date().toISOString();\n const result = db\n .prepare(\n `UPDATE tasks\n SET owner_id = ?,\n status = CASE WHEN status = 'OPEN' THEN 'IN_PROGRESS' ELSE status END,\n updated_at = ?\n WHERE local_id = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)\n AND (owner_id IS NULL OR owner_id = ?)`,\n )\n .run(claimerRow.id, now, localId, opts.workstream, claimerRow.id);\n\n if (result.changes === 0) {\n throw new TaskAlreadyOwnedError(localId, before.ownerName ?? \"<unknown>\");\n }\n\n const after = getTask(db, localId, opts.workstream);\n if (!after) throw new Error(`claimTask: row missing after update: ${localId}`);\n const statusBit = after.status !== before.status ? `, ${before.status} → ${after.status}` : \"\";\n emitEvent(\n db,\n opts.workstream,\n formatClaimEvent({\n localId,\n actor: agentName,\n anonymous: false,\n prose: `task claim ${localId} by ${agentName} (was owner=${before.ownerName ?? \"none\"}${statusBit})${evidenceSuffix(opts)}`,\n }),\n agentName,\n );\n return {\n ownerName: agentName,\n actorName: agentName,\n previousOwnerName: before.ownerName,\n previousStatus: before.status,\n status: after.status,\n };\n })();\n}\n\n/**\n * Resolve the current actor's identity for attribution in task notes,\n * --self claims, and any other write that wants 'who did this?'.\n *\n * Resolution order:\n * 1. $MU_AGENT_NAME env var (set by mu spawnAgent on every managed\n * pane; surfaced from the f3d4bdd commit). Authoritative when\n * present — you're inside a mu-spawned worker, no ambiguity.\n * 2. tmux pane title (the pane-title identity step). Works\n * when running inside any pane mu manages OR adopted.\n * 3. $USER (when running outside tmux entirely).\n * 4. The literal 'orchestrator' as a last-resort default.\n *\n * Why prefer env over pane title: pane titles are a tmux-server-wide\n * resource that anything can rewrite. The env var is set per-pane at\n * spawn time and is unforgeable from outside without explicit\n * `--actor` override. Pane title is the only identity available for\n * adopted panes that didn't go through mu's spawn path.\n */\nexport async function resolveActorIdentity(): Promise<string> {\n const muAgent = process.env.MU_AGENT_NAME;\n if (muAgent !== undefined && muAgent !== \"\") return muAgent;\n const paneTitle = await currentAgentName();\n if (paneTitle !== undefined && paneTitle !== \"\") return paneTitle;\n const user = process.env.USER;\n if (user !== undefined && user !== \"\") return user;\n return \"orchestrator\";\n}\n\nasync function claimSelf(db: Db, localId: string, opts: ClaimTaskOptions): Promise<ClaimResult> {\n const actor =\n opts.actor !== undefined && opts.actor !== \"\" ? opts.actor : await resolveActorIdentity();\n return db.transaction(() => {\n // Scope by the operator's workstream so a same-named task\n // elsewhere can't be self-claimed by accident.\n const before = getTask(db, localId, opts.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n\n // Anonymous claim: owner stays NULL, status flips OPEN -> IN_PROGRESS.\n // Gate on `owner_id IS NULL` so an in-flight worker claim can't be\n // silently overwritten.\n const now = new Date().toISOString();\n const result = db\n .prepare(\n `UPDATE tasks\n SET status = CASE WHEN status = 'OPEN' THEN 'IN_PROGRESS' ELSE status END,\n updated_at = ?\n WHERE local_id = ?\n AND workstream_id = (SELECT id FROM workstreams WHERE name = ?)\n AND owner_id IS NULL`,\n )\n .run(now, localId, before.workstreamName);\n\n if (result.changes === 0) {\n // Task exists but is already owned (by someone). Mirror the\n // worker-path error so callers can pattern-match consistently.\n throw new TaskAlreadyOwnedError(localId, before.ownerName ?? \"<unknown>\");\n }\n\n const after = getTask(db, localId, before.workstreamName);\n if (!after) throw new Error(`claimTask: row missing after update: ${localId}`);\n const statusBit = after.status !== before.status ? `, ${before.status} → ${after.status}` : \"\";\n emitEvent(\n db,\n before.workstreamName,\n formatClaimEvent({\n localId,\n actor,\n anonymous: true,\n prose: `task claim ${localId} by ${actor} --self (anonymous, owner stays NULL${statusBit})${evidenceSuffix(opts)}`,\n }),\n actor,\n );\n return {\n ownerName: null,\n actorName: actor,\n previousOwnerName: before.ownerName,\n previousStatus: before.status,\n status: after.status,\n };\n })();\n}\n","// mu — task graph: CRUD primitives, verbs (addTask, addNote, claimTask),\n// view reads (ready / blocked / goals), and DAG traversal helpers.\n//\n// The schema (db.ts) does the heavy lifting: PRIMARY KEYs, CHECK\n// constraints on impact/effort/status, FK cascades on edges and notes.\n// This module adds:\n// - the cycle check on edge insertion (SQL CHECK can't express it)\n// - the claim protocol (atomic CAS on tasks.owner via single UPDATE)\n// - row-shape mapping (snake_case columns → camelCase TS)\n\nimport { type Db, resolveWorkstreamId, tryResolveWorkstreamId } from \"./db.js\";\nimport { emitEvent } from \"./logs.js\";\nimport { captureSnapshot } from \"./snapshots.js\";\nimport {\n CrossWorkstreamEdgeError,\n CycleError,\n TaskExistsError,\n TaskIdInvalidError,\n TaskNotFoundError,\n} from \"./tasks/errors.js\";\nimport type { TaskStatus } from \"./tasks/status.js\";\nimport { ensureWorkstream } from \"./workstream.js\";\n\n// Re-export status enum + helpers and error classes from the cluster\n// modules. Public callers continue to `import { ... } from \"./tasks.js\"`\n// regardless of which sub-file the symbol lives in.\nexport {\n STATUSES_TERMINAL_OR_PARKED,\n TASK_STATUS_LIST,\n TASK_STATUSES,\n type TaskStatus,\n isTaskStatus,\n} from \"./tasks/status.js\";\nexport {\n ClaimerNotRegisteredError,\n CrossWorkstreamEdgeError,\n CycleError,\n ReaperDetectedDuringWaitError,\n StallDetectedDuringWaitError,\n TaskAlreadyOwnedError,\n TaskExistsError,\n TaskHasOpenDependentsError,\n TaskIdInvalidError,\n TaskNotFoundError,\n TaskNotInWorkstreamError,\n} from \"./tasks/errors.js\";\nexport {\n getWaitPollCount,\n resetWaitPollCount,\n setWaitSleepForTests,\n setWaitStuckWarnForTests,\n type TaskWaitOptions,\n type TaskWaitRef,\n type TaskWaitResult,\n type TaskWaitTaskState,\n waitForTasks,\n} from \"./tasks/wait.js\";\nexport {\n type CloseSkippedResult,\n type CloseTaskOptions,\n type EvidenceOption,\n type RejectDeferOptions,\n type RejectDeferResult,\n type SetStatusResult,\n closeTask,\n deferTask,\n openTask,\n rejectTask,\n setTaskStatus,\n} from \"./tasks/lifecycle.js\";\nexport {\n type ClaimResult,\n type ClaimTaskOptions,\n type ReleaseResult,\n type ReleaseTaskOptions,\n claimTask,\n releaseTask,\n resolveActorIdentity,\n} from \"./tasks/claim.js\";\n\n// ─── Domain types ──────────────────────────────────────────────────────\n\nexport interface TaskRow {\n /** Per-workstream-unique TEXT name. The operator-facing identifier. */\n name: string;\n /** Alias for `name` — the per-workstream-unique TEXT id. Emitted alongside\n * `name` so JSON consumers can dot-access the canonical field name without\n * having to know that, for tasks specifically, `name` plays the localId\n * role. Always equal to `name`. */\n localId: string;\n /** Foreign-name reference to the owning workstream. */\n workstreamName: string;\n title: string;\n status: TaskStatus;\n impact: number;\n effortDays: number;\n /** Foreign-name reference to the owning agent (NULL when unowned). */\n ownerName: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface TaskNoteRow {\n author: string | null;\n content: string;\n createdAt: string;\n}\n\ninterface RawTaskRow {\n /** Surrogate id (v5). */\n id: number;\n local_id: string;\n /** Joined from workstreams.name. */\n workstream: string;\n title: string;\n status: string;\n impact: number;\n effort_days: number;\n /** Joined from agents.name via owner_id. NULL when unowned. */\n owner: string | null;\n created_at: string;\n updated_at: string;\n}\n\ninterface RawTaskNoteRow {\n author: string | null;\n content: string;\n created_at: string;\n}\n\n// SELECT clause for v5 task reads. Joins workstreams + agents to expose\n// the operator-facing names as `workstream` and `owner`. Used by every\n// read path so callers don't see surrogate ids.\nconst SELECT_TASK_COLS = `\n t.id AS id,\n t.local_id AS local_id,\n ws.name AS workstream,\n t.title AS title,\n t.status AS status,\n t.impact AS impact,\n t.effort_days AS effort_days,\n ag.name AS owner,\n t.created_at AS created_at,\n t.updated_at AS updated_at\n`;\n\nconst TASK_FROM_JOIN = `\n FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n LEFT JOIN agents ag ON ag.id = t.owner_id\n`;\n\nconst SELECT_NOTE_COLS = `\n n.author AS author,\n n.content AS content,\n n.created_at AS created_at\n`;\n\nfunction rowFromDb(row: RawTaskRow): TaskRow {\n return {\n name: row.local_id,\n localId: row.local_id,\n workstreamName: row.workstream,\n title: row.title,\n status: row.status as TaskStatus,\n impact: row.impact,\n effortDays: row.effort_days,\n ownerName: row.owner,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction noteFromDb(row: RawTaskNoteRow): TaskNoteRow {\n return {\n author: row.author,\n content: row.content,\n createdAt: row.created_at,\n };\n}\n\n/** Look up a task by local_id across every workstream. Returns the\n * first match (sorted by workstream name for determinism). Used by\n * edge verbs (addBlockEdge / reparentTask) to resolve a blocker so a\n * cross-workstream blocker can surface `CrossWorkstreamEdgeError`\n * rather than the less-actionable `TaskNotFoundError`. NOT for\n * operator-facing reads — use `getTask(db, localId, workstream)`\n * for those. */\nfunction lookupTaskAnyWorkstream(db: Db, localId: string): TaskRow | undefined {\n const row = db\n .prepare(\n `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE t.local_id = ? ORDER BY ws.name LIMIT 1`,\n )\n .get(localId) as RawTaskRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\n/** Resolve a (workstream, localId) pair to the surrogate task id.\n * Returns null on miss. */\nfunction taskIdFor(db: Db, localId: string, workstream: string): number | null {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return null;\n const row = db\n .prepare(\"SELECT id FROM tasks WHERE workstream_id = ? AND local_id = ?\")\n .get(wsId, localId) as { id: number } | undefined;\n return row ? row.id : null;\n}\n\n/**\n * Bump `tasks.updated_at` on a surrogate task id. Used by every write\n * that mutates a child row (notes, edges) without touching the task\n * row itself, so `mu task list --sort recency` reflects 'last write of\n * any kind' rather than only 'last status/field change'\n * (task_updatedat_not_bumped_by_reparent). Status changes, field\n * updates, and claim/release already update `updated_at` directly in\n * their own UPDATE statements; don't double-bump from here.\n *\n * Always called inside the same transaction as the child-row mutation\n * so the bump rolls back together with the mutation on error.\n */\nexport function touchTask(db: Db, taskId: number, now?: string): void {\n db.prepare(\"UPDATE tasks SET updated_at = ? WHERE id = ?\").run(\n now ?? new Date().toISOString(),\n taskId,\n );\n}\n\n// ─── ID validation ─────────────────────────────────────────────────────\n\n/** Lowercase alpha first, then alnum / underscore / hyphen, ≤64 chars. */\nconst TASK_ID_RE = /^[a-z][a-z0-9_-]{0,63}$/;\n\nexport function isValidTaskId(id: string): boolean {\n return TASK_ID_RE.test(id);\n}\n\n/**\n * Derive a task id from a free-form title.\n *\n * \"Build the auth module\" → \"build_the_auth_module\"\n * \"FILES: foo.ts (refactor)\" → \"files_foo_ts_refactor\"\n *\n/**\n * Soft cap for auto-generated slugs. The collision-suffix loop in\n * idFromTitle can push past this (`_2`, `_3`, ...) without going past\n * the hard ceiling. 40 chars hits the sweet spot of 'short enough to\n * type and to look reasonable in mu task tree' without losing too\n * much of the title's meaning.\n */\nconst SLUG_SOFT_CAP = 40;\n\n/**\n * Hard ceiling for any generated id. Schema has no length limit, but\n * 64 keeps ids comfortable in tables, JSON, and tmux pane titles.\n */\nconst SLUG_HARD_CAP = 64;\n\n/**\n * Lowercase title; collapse non-alnum runs into single `_`; trim\n * leading/trailing `_`; prefix `t_` if the result starts with a digit\n * (schema requires first char letter); apply the soft cap with\n * word-boundary trim (cut at the last `_` at-or-before SLUG_SOFT_CAP\n * when one exists, else hard-truncate). Mirrors `tg`'s `id_from_title`\n * but adds the soft cap.\n *\n * Throws if `title` yields an empty slug after stripping.\n */\nexport function slugifyTitle(title: string): string {\n return slugifyTitleVerbose(title).slug;\n}\n\n/**\n * Result of `slugifyTitleVerbose`: the slug plus enough metadata for\n * the CLI to decide whether to warn the user that meaning was lost.\n *\n * slug — the same string `slugifyTitle` returns.\n * strippedLength — length of the post-strip pre-cap slug. When this\n * exceeds the SLUG_SOFT_CAP the verbose form had to\n * cut at a word boundary (or hard-truncate); the\n * cut clauses are gone with no in-band signal.\n * truncated — true iff `slug.length < strippedLength` AFTER the\n * `t_` digit-prefix correction, i.e. real bytes were\n * dropped. False for any title that fits under the\n * soft cap or whose only diff vs the stripped slug\n * is the `t_` prefix.\n *\n * The CLI's `mu task add` uses `truncated` to print a one-line stderr\n * hint pointing at the `<id>` positional override\n * (slugifytitle_silently_drops_clauses).\n */\nexport interface SlugifyResult {\n slug: string;\n strippedLength: number;\n truncated: boolean;\n}\n\n/**\n * Verbose sibling of `slugifyTitle`: returns the slug AND a\n * `truncated` flag so the CLI can hint to the user when the soft cap\n * dropped clauses (the meaning-shift hazard documented in\n * slugifytitle_silently_drops_clauses).\n *\n * Algorithm is byte-for-byte identical to `slugifyTitle`; this just\n * surfaces the metadata that the plain form throws away.\n */\nexport function slugifyTitleVerbose(title: string): SlugifyResult {\n const stripped = title\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\");\n if (stripped.length === 0) {\n throw new Error(`title yields empty slug: ${JSON.stringify(title)}`);\n }\n // Soft cap with word-boundary preference: if the slug exceeds the\n // soft cap, look for the last `_` at-or-before the cap and cut there\n // (so we never break a word). If no underscore is in the cap window\n // (ie the title is one giant word), fall back to a hard truncate at\n // the soft cap. The result is always <= SLUG_SOFT_CAP after this.\n let trimmed: string;\n if (stripped.length <= SLUG_SOFT_CAP) {\n trimmed = stripped;\n } else {\n const window = stripped.slice(0, SLUG_SOFT_CAP);\n const lastSep = window.lastIndexOf(\"_\");\n trimmed = lastSep > 0 ? window.slice(0, lastSep) : window;\n }\n // First char must be a letter → prefix `t_` if it isn't. v5 has no\n // global namespace and no reserved prefix; `mu_foo` is a perfectly\n // valid local_id (per-workstream unique).\n const slug = /^[a-z]/.test(trimmed)\n ? trimmed.slice(0, SLUG_HARD_CAP)\n : `t_${trimmed}`.slice(0, SLUG_HARD_CAP);\n // `truncated` reflects whether real characters were dropped — the\n // `t_` prefix doesn't count as truncation. We compare the trimmed\n // length against the stripped length (both pre-prefix) so a digit-\n // led title that fit under the cap still reports truncated=false.\n return {\n slug,\n strippedLength: stripped.length,\n truncated: trimmed.length < stripped.length,\n };\n}\n\n/**\n * Generate a unique task id from a title. v5: tasks.local_id is\n * per-workstream unique, so the collision check scopes to one\n * workstream. On collision, appends `_2`, `_3`, … until unique.\n */\nexport function idFromTitle(db: Db, workstream: string, title: string): string {\n return idFromTitleVerbose(db, workstream, title).id;\n}\n\n/**\n * Result of `idFromTitleVerbose`: the unique-in-workstream id plus the\n * truncated flag from the underlying slugify pass. Used by `mu task\n * add` to decide whether to surface the stderr hint about lost clauses\n * (slugifytitle_silently_drops_clauses).\n */\nexport interface IdFromTitleResult {\n id: string;\n truncated: boolean;\n}\n\n/**\n * Verbose sibling of `idFromTitle`: returns both the unique id and the\n * `truncated` flag from the slugify pass. Collision-suffixing (`_2`,\n * `_3`, …) does not flip `truncated` — the underlying slug's lossiness\n * is what the CLI hint cares about.\n */\nexport function idFromTitleVerbose(db: Db, workstream: string, title: string): IdFromTitleResult {\n const { slug: base, truncated } = slugifyTitleVerbose(title);\n if (getTask(db, base, workstream) === undefined) return { id: base, truncated };\n for (let i = 2; i < 1000; i++) {\n const candidate = `${base}_${i}`.slice(0, SLUG_HARD_CAP);\n if (getTask(db, candidate, workstream) === undefined) return { id: candidate, truncated };\n }\n throw new Error(`could not derive a unique id from title in workstream ${workstream}: ${title}`);\n}\n\n/**\n * Sanitise a free-form string into a candidate task id.\n *\n * Lowercases, replaces every non-`[a-z0-9_-]` char with `_`, trims any\n * leading non-letter (the schema requires the first char to be a\n * letter), truncates to 64 chars. Returns `\"task\"` when the input has\n * no usable letters at all so the suggestion in\n * `TaskIdInvalidError.errorNextSteps()` is always a runnable command.\n *\n * Mirrors `slugifyTitle`'s prefix corrections so suggested ids will\n * pass `isValidTaskId` if the user runs them verbatim. Lives next to\n * `slugifyTitle` rather than in `tasks/errors.ts` because it's a slug\n * helper, not an error helper — the only caller happens to be\n * `TaskIdInvalidError.errorNextSteps()`.\n */\nexport function sanitiseTaskId(input: string): string {\n const s = input\n .toLowerCase()\n .replace(/[^a-z0-9_-]/g, \"_\")\n .replace(/^[^a-z]+/, \"\")\n .slice(0, SLUG_HARD_CAP);\n return s.length === 0 ? \"task\" : s;\n}\n\n// ─── Read primitives ───────────────────────────────────────────────────\n\nexport function getTask(db: Db, localId: string, workstream: string): TaskRow | undefined {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return undefined;\n const row = db\n .prepare(\n `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE t.workstream_id = ? AND t.local_id = ?`,\n )\n .get(wsId, localId) as RawTaskRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\n/**\n * List tasks. With no `workstream` arg returns every row — used by `mu sql`\n * and by tests; CLI surfaces always pass a workstream so users only see\n * their own.\n */\nexport interface ListTasksOptions {\n /** Filter to one or more lifecycle statuses. Omitted = all statuses. */\n status?: TaskStatus | readonly TaskStatus[];\n}\n\nexport function listTasks(db: Db, workstream?: string, opts: ListTasksOptions = {}): TaskRow[] {\n const statuses =\n opts.status === undefined\n ? undefined\n : Array.isArray(opts.status)\n ? (opts.status as TaskStatus[])\n : [opts.status as TaskStatus];\n\n const where: string[] = [];\n const params: unknown[] = [];\n if (workstream !== undefined) {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n where.push(\"t.workstream_id = ?\");\n params.push(wsId);\n }\n if (statuses !== undefined) {\n where.push(`t.status IN (${statuses.map(() => \"?\").join(\", \")})`);\n params.push(...statuses);\n }\n const sql =\n where.length === 0\n ? `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} ORDER BY t.local_id`\n : `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE ${where.join(\" AND \")} ORDER BY t.local_id`;\n const rows = db.prepare(sql).all(...params) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\n// The three views (ready, blocked, goals) project tasks.* directly,\n// so they expose v5 columns (id, workstream_id, owner_id). We wrap\n// them with the same JOINs as TASK_FROM_JOIN to translate back to the\n// operator-facing TaskRow shape (workstream + owner as TEXT names).\nconst VIEW_FROM_JOIN = (view: string) => `\n FROM ${view} v\n JOIN workstreams ws ON ws.id = v.workstream_id\n LEFT JOIN agents ag ON ag.id = v.owner_id\n`;\nconst SELECT_VIEW_COLS = `\n v.id AS id,\n v.local_id AS local_id,\n ws.name AS workstream,\n v.title AS title,\n v.status AS status,\n v.impact AS impact,\n v.effort_days AS effort_days,\n ag.name AS owner,\n v.created_at AS created_at,\n v.updated_at AS updated_at\n`;\n\n/** Options for listReady. The optional `statuses` filter composes\n * on top of the `ready` view (which itself constrains to\n * `status='OPEN'`); passing only OPEN is identical to today's no-\n * filter shape, passing only non-OPEN values returns []. Exists so\n * `mu task next --status` can mirror the multi-status flag shape\n * shipped on `mu task list` (task_list_multi_status_union). */\nexport interface ListReadyOptions {\n status?: TaskStatus | readonly TaskStatus[];\n}\n\nexport function listReady(db: Db, workstream: string, opts: ListReadyOptions = {}): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const statuses =\n opts.status === undefined\n ? undefined\n : Array.isArray(opts.status)\n ? (opts.status as TaskStatus[])\n : [opts.status as TaskStatus];\n const where: string[] = [\"v.workstream_id = ?\"];\n const params: unknown[] = [wsId];\n if (statuses !== undefined && statuses.length > 0) {\n where.push(`v.status IN (${statuses.map(() => \"?\").join(\", \")})`);\n params.push(...statuses);\n }\n const rows = db\n .prepare(\n `SELECT ${SELECT_VIEW_COLS} ${VIEW_FROM_JOIN(\"ready\")} WHERE ${where.join(\" AND \")} ORDER BY v.local_id`,\n )\n .all(...params) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\nexport function listBlocked(db: Db, workstream: string): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_VIEW_COLS} ${VIEW_FROM_JOIN(\"blocked\")} WHERE v.workstream_id = ? ORDER BY v.local_id`,\n )\n .all(wsId) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\nexport function listGoals(db: Db, workstream: string): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_VIEW_COLS} ${VIEW_FROM_JOIN(\"goals\")} WHERE v.workstream_id = ? ORDER BY v.local_id`,\n )\n .all(wsId) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\n/** All IN_PROGRESS tasks in a workstream, most-recently-touched first.\n * Used by `mu state` and `mu hud` to populate their in-progress slice;\n * exposed as a named SDK helper so those CLI verbs don't re-derive\n * the row-shape conversion (review_code_raw_task_state_duplicate). */\nexport function listInProgress(db: Db, workstream: string): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE t.workstream_id = ? AND t.status = 'IN_PROGRESS' ORDER BY t.updated_at DESC`,\n )\n .all(wsId) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\n/** Most-recently-closed tasks in a workstream, newest first, capped at\n * `limit` (default 5). Used by `mu state` for its 'recent closed'\n * slice; exposed as a named SDK helper so the CLI no longer needs the\n * raw-row type that was duplicating RawTaskRow\n * (review_code_raw_task_state_duplicate). */\nexport function listRecentClosed(db: Db, workstream: string, limit = 5): TaskRow[] {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN} WHERE t.workstream_id = ? AND t.status = 'CLOSED' ORDER BY t.updated_at DESC LIMIT ?`,\n )\n .all(wsId, limit) as RawTaskRow[];\n return rows.map(rowFromDb);\n}\n\n/** List notes for a task. Operator-facing local_id; resolves to the\n * surrogate task id via taskIdFor (with optional workstream scope). */\nexport function listNotes(db: Db, taskLocalId: string, workstream: string): TaskNoteRow[] {\n const taskId = taskIdFor(db, taskLocalId, workstream);\n if (taskId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_NOTE_COLS} FROM task_notes n JOIN tasks t ON t.id = n.task_id\n WHERE n.task_id = ? ORDER BY n.id`,\n )\n .all(taskId) as RawTaskNoteRow[];\n return rows.map(noteFromDb);\n}\n\n/**\n * All tasks currently owned by `agent` in a given workstream\n * (v5: agents.name is per-workstream unique). Sorted by local_id.\n *\n * Defaults to **excluding CLOSED** since the verb's purpose is \"what\n * is X currently working on?\" and a closed task is no longer being\n * worked on. closeTask intentionally preserves `owner` as a\n * historical record (so audit/notes can attribute decisions); pass\n * `{ includeClosed: true }` to surface that history.\n */\nexport function listTasksByOwner(\n db: Db,\n workstream: string,\n owner: string,\n opts: { includeClosed?: boolean } = {},\n): TaskRow[] {\n // 'Live work' = not in any terminal-or-parked state. CLOSED is the\n // obvious one; REJECTED and DEFERRED are also off the agent's plate\n // (the user has decided 'won't do' or 'not now'). includeClosed\n // re-includes ALL of those so historical attribution is recoverable.\n // Filter on the joined ag.name so the operator-facing owner string\n // still drives the lookup; FK is now via owner_id.\n const filter = opts.includeClosed ? \"\" : \"AND t.status NOT IN ('CLOSED', 'REJECTED', 'DEFERRED')\";\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return [];\n const sql = `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN}\n WHERE ag.name = ? AND t.workstream_id = ? ${filter}\n ORDER BY t.local_id`;\n return (db.prepare(sql).all(owner, wsId) as RawTaskRow[]).map(rowFromDb);\n}\n\n/**\n * Cross-workstream variant of `listTasksByOwner`. Returns tasks owned\n * by ANY agent of the given name across every workstream. Used by\n * `mu task owned-by --all` for the genuine cross-workstream view\n * (audit / dashboards). The bare name is the join key, so two\n * distinct same-named agents in different workstreams contribute\n * their tasks to the same result list.\n */\nexport function listTasksByOwnerCrossWorkstream(\n db: Db,\n owner: string,\n opts: { includeClosed?: boolean } = {},\n): TaskRow[] {\n const filter = opts.includeClosed ? \"\" : \"AND t.status NOT IN ('CLOSED', 'REJECTED', 'DEFERRED')\";\n const sql = `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN}\n WHERE ag.name = ? ${filter}\n ORDER BY ws.name, t.local_id`;\n return (db.prepare(sql).all(owner) as RawTaskRow[]).map(rowFromDb);\n}\n\nexport interface SearchTasksOptions {\n /** Restrict to one workstream; undefined = search across all. */\n workstream?: string;\n /** Also search `task_notes.content` (default false: titles + ids only). */\n includeNotes?: boolean;\n}\n\n/**\n * Substring search on task `title` and `local_id`, case-insensitive.\n * With `includeNotes: true` also searches `task_notes.content`. The\n * pattern is wrapped in `%...%` automatically so callers don't need\n * SQL LIKE knowledge — for explicit globs (or regex), use `mu sql`.\n */\nexport function searchTasks(db: Db, pattern: string, opts: SearchTasksOptions = {}): TaskRow[] {\n const like = `%${pattern.toLowerCase()}%`;\n const wsClause = opts.workstream === undefined ? \"\" : \"ws.name = ? AND\";\n const wsParams = opts.workstream === undefined ? [] : [opts.workstream];\n const orderBy =\n opts.workstream === undefined ? \"ORDER BY ws.name, t.local_id\" : \"ORDER BY t.local_id\";\n\n if (opts.includeNotes) {\n const sql = `SELECT DISTINCT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN}\n LEFT JOIN task_notes n ON n.task_id = t.id\n WHERE ${wsClause} (\n LOWER(t.title) LIKE ?\n OR LOWER(t.local_id) LIKE ?\n OR LOWER(n.content) LIKE ?\n )\n ${orderBy}`;\n return (db.prepare(sql).all(...wsParams, like, like, like) as RawTaskRow[]).map(rowFromDb);\n }\n\n const sql = `SELECT ${SELECT_TASK_COLS} ${TASK_FROM_JOIN}\n WHERE ${wsClause} (LOWER(t.title) LIKE ? OR LOWER(t.local_id) LIKE ?)\n ${orderBy}`;\n return (db.prepare(sql).all(...wsParams, like, like) as RawTaskRow[]).map(rowFromDb);\n}\n\nexport interface TaskEdges {\n /** Tasks that must close before this one can start (blockers). */\n blockers: string[];\n /** Tasks that this one blocks (dependents). */\n dependents: string[];\n}\n\n/** One end of an edge with the neighbour's current status attached.\n * Used by `mu task show` to group blockers/dependents into\n * \"still gating\" vs \"satisfied\" buckets without making the renderer\n * do a second round-trip to the DB per neighbour. */\nexport interface TaskEdgeWithStatus {\n name: string;\n status: TaskStatus;\n}\n\nexport interface TaskEdgesWithStatus {\n /** Tasks that must close before this one can start (blockers),\n * carrying each blocker's current status. */\n blockers: TaskEdgeWithStatus[];\n /** Tasks that this one blocks (dependents), carrying each\n * dependent's current status. */\n dependents: TaskEdgeWithStatus[];\n}\n\n/**\n * Direct (one-hop) edges for a task. For transitive prerequisites, use\n * `getPrerequisites()`; this helper is the immediate-neighbour view used\n * by `mu task show`.\n */\nexport function getTaskEdges(db: Db, taskLocalId: string, workstream: string): TaskEdges {\n const taskId = taskIdFor(db, taskLocalId, workstream);\n if (taskId === null) return { blockers: [], dependents: [] };\n const blockers = (\n db\n .prepare(\n `SELECT t.local_id AS id FROM task_edges e\n JOIN tasks t ON t.id = e.from_task_id\n WHERE e.to_task_id = ? ORDER BY t.local_id`,\n )\n .all(taskId) as { id: string }[]\n ).map((r) => r.id);\n const dependents = (\n db\n .prepare(\n `SELECT t.local_id AS id FROM task_edges e\n JOIN tasks t ON t.id = e.to_task_id\n WHERE e.from_task_id = ? ORDER BY t.local_id`,\n )\n .all(taskId) as { id: string }[]\n ).map((r) => r.id);\n return { blockers, dependents };\n}\n\n/**\n * Same one-hop edge view as `getTaskEdges`, but each neighbour is\n * returned as `{ name, status }` so callers can group / colour by\n * status without an N+1 round-trip. Used by `mu task show` to split\n * \"blocked by\" (still-gating) from \"satisfied\" (already-CLOSED)\n * blockers, and the symmetric split on the dependents side\n * (task_show_blocked_by_renders_closed). The status is the neighbour's\n * full TaskStatus, not just OPEN/CLOSED — REJECTED/DEFERRED still\n * gate downstream work, so the renderer keeps them in the\n * still-gating bucket.\n */\nexport function getTaskEdgesWithStatus(\n db: Db,\n taskLocalId: string,\n workstream: string,\n): TaskEdgesWithStatus {\n const taskId = taskIdFor(db, taskLocalId, workstream);\n if (taskId === null) return { blockers: [], dependents: [] };\n const blockers = db\n .prepare(\n `SELECT t.local_id AS name, t.status AS status FROM task_edges e\n JOIN tasks t ON t.id = e.from_task_id\n WHERE e.to_task_id = ? ORDER BY t.local_id`,\n )\n .all(taskId) as TaskEdgeWithStatus[];\n const dependents = db\n .prepare(\n `SELECT t.local_id AS name, t.status AS status FROM task_edges e\n JOIN tasks t ON t.id = e.to_task_id\n WHERE e.from_task_id = ? ORDER BY t.local_id`,\n )\n .all(taskId) as TaskEdgeWithStatus[];\n return { blockers, dependents };\n}\n\n/**\n * All tasks transitively reachable from `taskId` via reverse-edge\n * traversal (i.e. the set of tasks that block this one), including the\n * task itself.\n */\nexport function getPrerequisites(db: Db, taskLocalId: string, workstream: string): Set<string> {\n const taskId = taskIdFor(db, taskLocalId, workstream);\n if (taskId === null) return new Set([taskLocalId]);\n // Walk reverse edges in surrogate-id space, then translate back to\n // local_id strings. The seed must be the surrogate id; the result\n // includes the seed task itself (callers like the tracks union-find\n // rely on the inclusive set).\n const rows = db\n .prepare(\n `WITH RECURSIVE reach(node) AS (\n SELECT ?\n UNION\n SELECT from_task_id FROM task_edges, reach WHERE to_task_id = reach.node\n )\n SELECT t.local_id AS local_id FROM reach r JOIN tasks t ON t.id = r.node`,\n )\n .all(taskId) as { local_id: string }[];\n return new Set(rows.map((r) => r.local_id));\n}\n\n// ─── Internal: cycle check ─────────────────────────────────────────────\n\n/**\n * Adding edge `from -> to` creates a cycle iff there's already a path\n * `to -> ... -> from`. SQL recursive CTE expresses this exactly.\n */\nfunction wouldCreateCycle(db: Db, fromId: number, toId: number): boolean {\n if (fromId === toId) return true;\n const result = db\n .prepare(\n `WITH RECURSIVE forward(node) AS (\n SELECT ?\n UNION\n SELECT to_task_id FROM task_edges, forward WHERE from_task_id = forward.node\n )\n SELECT 1 AS hit FROM forward WHERE node = ? LIMIT 1`,\n )\n .get(toId, fromId) as { hit: number } | undefined;\n return result !== undefined;\n}\n\n// ─── addTask (verb) ────────────────────────────────────────────────────\n\nexport interface AddTaskOptions {\n localId: string;\n workstream: string;\n title: string;\n /** 1..100; enforced by schema CHECK. */\n impact: number;\n /** > 0; enforced by schema CHECK. */\n effortDays: number;\n /**\n * Tasks that block this one. Edges inserted as `blocker -> newTask`.\n * Each blocker must already exist AND share this task's workstream\n * (cross-workstream edges are forbidden); cycle check guards each\n * edge. The CLI surfaces this as `--blocked-by`; the SDK key matches.\n */\n blockedBy?: string[];\n}\n\n/**\n * Atomically create a task and (optionally) its incoming blocked-by\n * edges.\n *\n * The task insert + every edge insert + cycle check happen inside one\n * SQLite transaction. If any blocker is missing or any edge would\n * create a cycle, the entire add rolls back.\n *\n * Cycle check for `addTask` is structurally trivial (a fresh task has\n * no outgoing edges, so `to -> ... -> from` is impossible). It's still\n * called here so the same primitive is exercised by tests.\n */\nexport function addTask(db: Db, opts: AddTaskOptions): TaskRow {\n if (!isValidTaskId(opts.localId)) {\n throw new TaskIdInvalidError(opts.localId);\n }\n\n return db.transaction(() => {\n // Auto-create the workstream row so tasks.workstream_id FK is\n // satisfied (preserves spawn-without-init ergonomics).\n ensureWorkstream(db, opts.workstream);\n const wsId = resolveWorkstreamId(db, opts.workstream);\n\n // Per-workstream uniqueness: a duplicate local_id within the same\n // workstream throws TaskExistsError. Different workstreams may\n // legitimately share local_ids in v5.\n const existing = db\n .prepare(\"SELECT id FROM tasks WHERE workstream_id = ? AND local_id = ?\")\n .get(wsId, opts.localId) as { id: number } | undefined;\n if (existing) {\n throw new TaskExistsError(opts.localId);\n }\n\n const now = new Date().toISOString();\n const insertResult = db\n .prepare(\n `INSERT INTO tasks (workstream_id, local_id, title, status, impact, effort_days, created_at, updated_at)\n VALUES (?, ?, ?, 'OPEN', ?, ?, ?, ?)`,\n )\n .run(wsId, opts.localId, opts.title, opts.impact, opts.effortDays, now, now);\n const newTaskId = Number(insertResult.lastInsertRowid);\n\n if (opts.blockedBy && opts.blockedBy.length > 0) {\n // Prefer the same-workstream blocker first (v5 per-workstream\n // local_id), then fall back to a global lookup so a cross-ws\n // blocker still surfaces CrossWorkstreamEdgeError (not\n // TaskNotFoundError). Without the same-ws preference, two\n // blockers of the same local_id (one in this ws, one elsewhere)\n // could silently bind to the wrong row\n // (bug_v5_name_clash_silent_misroute).\n const blockerLookupSameWs = db.prepare(\n `SELECT t.id AS id, ws.name AS workstream FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE t.local_id = ? AND t.workstream_id = ?`,\n );\n const blockerLookupAnyWs = db.prepare(\n `SELECT t.id AS id, ws.name AS workstream FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE t.local_id = ? LIMIT 1`,\n );\n const insertEdge = db.prepare(\n \"INSERT INTO task_edges (from_task_id, to_task_id, created_at) VALUES (?, ?, ?)\",\n );\n for (const blocker of opts.blockedBy) {\n const row = (blockerLookupSameWs.get(blocker, wsId) ?? blockerLookupAnyWs.get(blocker)) as\n | { id: number; workstream: string }\n | undefined;\n if (!row) {\n throw new TaskNotFoundError(blocker);\n }\n if (row.workstream !== opts.workstream) {\n throw new CrossWorkstreamEdgeError(\n blocker,\n row.workstream,\n opts.localId,\n opts.workstream,\n );\n }\n if (wouldCreateCycle(db, row.id, newTaskId)) {\n throw new CycleError(blocker, opts.localId);\n }\n insertEdge.run(row.id, newTaskId, now);\n }\n }\n\n const row = getTask(db, opts.localId, opts.workstream);\n if (!row) throw new Error(`addTask: row missing after insert: ${opts.localId}`);\n const blockedBy =\n opts.blockedBy && opts.blockedBy.length > 0 ? `, blocked-by=${opts.blockedBy.join(\",\")}` : \"\";\n emitEvent(\n db,\n opts.workstream,\n `task add ${opts.localId} (impact=${opts.impact}, effort=${opts.effortDays}${blockedBy})`,\n );\n return row;\n })();\n}\n\n// ─── addNote (verb) ────────────────────────────────────────────────────\n\nexport interface AddNoteOptions {\n /** Free-form author label. Convention: agent name, \"user\", or \"orchestrator\". */\n author?: string;\n /** Workstream context (operator-facing name). v5: tasks.local_id is\n * per-workstream unique, so this is required to disambiguate. */\n workstream: string;\n}\n\nexport function addNote(\n db: Db,\n taskLocalId: string,\n content: string,\n opts: AddNoteOptions,\n): TaskNoteRow {\n const task = getTask(db, taskLocalId, opts.workstream);\n if (!task) {\n throw new TaskNotFoundError(taskLocalId);\n }\n const taskId = taskIdFor(db, task.name, task.workstreamName);\n if (taskId === null) throw new TaskNotFoundError(taskLocalId);\n const now = new Date().toISOString();\n const result = db.transaction(() => {\n const r = db\n .prepare(\"INSERT INTO task_notes (task_id, author, content, created_at) VALUES (?, ?, ?, ?)\")\n .run(taskId, opts.author ?? null, content, now);\n // Bump the parent task so `mu task list --sort recency` surfaces\n // freshly-noted tasks (task_updatedat_not_bumped_by_reparent).\n touchTask(db, taskId, now);\n return r;\n })();\n const noteId = Number(result.lastInsertRowid);\n emitEvent(\n db,\n task.workstreamName,\n `task note ${taskLocalId} (note #${noteId} by ${opts.author ?? \"orchestrator\"})`,\n opts.author ?? \"system\",\n );\n return {\n author: opts.author ?? null,\n content,\n createdAt: now,\n };\n}\n\n// ─── addBlockEdge / removeBlockEdge ────────────────────────────────────────\n\nexport interface BlockEdgeResult {\n /** True iff a row was actually inserted (vs. already present). */\n added: boolean;\n}\n\n/**\n * Add the edge `blocker → blocked` ('blocker blocks blocked').\n * Idempotent (existing edge → `added: false`). Validates:\n *\n * - both tasks exist\n * - same workstream (cross-workstream edges forbidden)\n * - no cycle (the new edge wouldn't form a path blocked → ... → blocker)\n * - blocker ≠ blocked (no self-reference)\n */\nexport function addBlockEdge(\n db: Db,\n workstream: string,\n blocked: string,\n blocker: string,\n): BlockEdgeResult {\n if (blocked === blocker) {\n // Surface as a typed CycleError so the CLI maps it to exit 4 (conflict)\n // rather than letting the schema CHECK fire as a generic SQL error.\n throw new CycleError(blocker, blocked);\n }\n const blockedRow = getTask(db, blocked, workstream);\n if (!blockedRow) throw new TaskNotFoundError(blocked);\n // Resolve the blocker globally so a cross-workstream blocker surfaces\n // CrossWorkstreamEdgeError (clearer than TaskNotFoundError). Cycle\n // check + same-workstream guard run after.\n const blockerRow = lookupTaskAnyWorkstream(db, blocker);\n if (!blockerRow) throw new TaskNotFoundError(blocker);\n if (blockedRow.workstreamName !== blockerRow.workstreamName) {\n throw new CrossWorkstreamEdgeError(\n blocker,\n blockerRow.workstreamName,\n blocked,\n blockedRow.workstreamName,\n );\n }\n const blockedId = taskIdFor(db, blocked, blockedRow.workstreamName);\n const blockerId = taskIdFor(db, blocker, blockerRow.workstreamName);\n if (blockedId === null || blockerId === null) throw new TaskNotFoundError(blocked);\n if (wouldCreateCycle(db, blockerId, blockedId)) {\n throw new CycleError(blocker, blocked);\n }\n const now = new Date().toISOString();\n const added = db.transaction(() => {\n const result = db\n .prepare(\n \"INSERT OR IGNORE INTO task_edges (from_task_id, to_task_id, created_at) VALUES (?, ?, ?)\",\n )\n .run(blockerId, blockedId, now);\n if (result.changes > 0) {\n // Bump the BLOCKED task — its blocker set changed. The blocker\n // itself is unaffected. Aligned with reparentTask, which also\n // bumps the FROM_TASK side (the task whose blockers shifted).\n touchTask(db, blockedId, now);\n return true;\n }\n return false;\n })();\n if (added) emitEvent(db, blockedRow.workstreamName, `task block ${blocked} by ${blocker}`);\n return { added };\n}\n\nexport interface RemoveBlockEdgeResult {\n /** True iff a row was actually deleted (vs. no such edge). */\n removed: boolean;\n}\n\n/**\n * Remove the edge `blocker → blocked`. Idempotent (no edge →\n * `removed: false`). Does NOT validate task existence — if the\n * edge is gone there's nothing to do, regardless of whether the\n * tasks are gone too.\n */\nexport function removeBlockEdge(\n db: Db,\n workstream: string,\n blocked: string,\n blocker: string,\n): RemoveBlockEdgeResult {\n const blockedRow = getTask(db, blocked, workstream);\n if (!blockedRow) return { removed: false };\n const blockerRow = getTask(db, blocker, workstream);\n if (!blockerRow) return { removed: false };\n const blockedId = taskIdFor(db, blocked, blockedRow.workstreamName);\n const blockerId = taskIdFor(db, blocker, blockerRow.workstreamName);\n if (blockedId === null || blockerId === null) return { removed: false };\n const removed = db.transaction(() => {\n const result = db\n .prepare(\"DELETE FROM task_edges WHERE from_task_id = ? AND to_task_id = ?\")\n .run(blockerId, blockedId);\n if (result.changes > 0) {\n // Bump the BLOCKED task — its blocker set just shrank.\n touchTask(db, blockedId);\n return true;\n }\n return false;\n })();\n if (removed) {\n emitEvent(db, blockedRow.workstreamName, `task unblock ${blocked} by ${blocker}`);\n }\n return { removed };\n}\n\n// ─── deleteTask ───────────────────────────────────────────────────────────\n\nexport interface DeleteTaskResult {\n /** True iff the row existed and was deleted. False on a dry-run\n * (preview) AND on the idempotent missing-row case. */\n deleted: boolean;\n /** Number of `task_edges` rows cascaded out (informational). On a\n * dry-run, this is the would-be count. */\n deletedEdges: number;\n /** Number of `task_notes` rows cascaded out (informational). On a\n * dry-run, this is the would-be count. */\n deletedNotes: number;\n /** True iff this was a dry-run (`opts.dryRun: true`). On a\n * dry-run `deleted` is false and the counts are the would-be\n * counts; the DB is unchanged. Always false on a commit / on a\n * missing-row idempotent no-op. */\n dryRun: boolean;\n /** True iff a matching task row was found at the time of the\n * call. Discriminator for the CLI: a dry-run that found nothing\n * (`present: false`) renders differently from a dry-run that\n * found an existing task with zero edges and zero notes\n * (`present: true, deletedEdges: 0, deletedNotes: 0`). */\n present: boolean;\n}\n\nexport interface DeleteTaskOptions {\n /** When true, return the cascade preview (would-be edge / note\n * counts) without mutating and without snapshotting. The CLI uses\n * this to power the bare `mu task delete <id>` two-phase pattern\n * (mirrors `mu workstream destroy` / `mu archive delete` /\n * `mu snapshot prune`). Surfaced by feedback ws task\n * fb_task_delete_no_yes (impact=30): a dogfood report typed\n * `mu task delete X --yes` (mirroring workstream destroy) and got\n * 'unknown option --yes' — the verb took no confirmation flag at\n * all. Two failed deletes left long-named tasks lingering. */\n dryRun?: boolean;\n}\n\n/**\n * Delete a task. FK CASCADE on `task_edges` (from + to) and\n * `task_notes` cleans the joined rows automatically. Idempotent on\n * a missing task (returns `deleted: false`).\n *\n * Pre-counts the cascade victims for reporting because SQLite's\n * `changes()` only reports rows directly affected by the DELETE.\n *\n * With `opts.dryRun: true`, returns the would-be counts without\n * touching the DB and without taking a snapshot (no mutation = no\n * snapshot — same reasoning that gates the closeTask snap on the\n * idempotent no-op path). The CLI bare `mu task delete <id>` form\n * uses this; `--yes` calls through with `dryRun: false`.\n */\nexport function deleteTask(\n db: Db,\n localId: string,\n workstream: string,\n opts: DeleteTaskOptions = {},\n): DeleteTaskResult {\n const dryRun = opts.dryRun === true;\n const before = getTask(db, localId, workstream);\n if (!before) {\n // Idempotent on a missing row regardless of dryRun.\n return { deleted: false, deletedEdges: 0, deletedNotes: 0, dryRun, present: false };\n }\n const taskId = taskIdFor(db, localId, before.workstreamName);\n if (taskId === null) {\n return { deleted: false, deletedEdges: 0, deletedNotes: 0, dryRun, present: false };\n }\n const edgesBefore = (\n db\n .prepare(\"SELECT COUNT(*) AS n FROM task_edges WHERE from_task_id = ? OR to_task_id = ?\")\n .get(taskId, taskId) as { n: number }\n ).n;\n const notesBefore = (\n db.prepare(\"SELECT COUNT(*) AS n FROM task_notes WHERE task_id = ?\").get(taskId) as {\n n: number;\n }\n ).n;\n if (dryRun) {\n return {\n deleted: false,\n deletedEdges: edgesBefore,\n deletedNotes: notesBefore,\n dryRun: true,\n present: true,\n };\n }\n // Pre-mutation snapshot. delete cascades into task_edges and\n // task_notes; no per-row history can reconstruct it. Taken AFTER\n // the dry-run early-return so a preview never touches snapshots.\n captureSnapshot(db, `task delete ${localId}`, before.workstreamName);\n const result = db.prepare(\"DELETE FROM tasks WHERE id = ?\").run(taskId);\n const deleted = result.changes > 0;\n if (deleted) {\n emitEvent(\n db,\n before.workstreamName,\n `task delete ${localId} (cascade: ${edgesBefore} edges, ${notesBefore} notes)`,\n );\n }\n return {\n deleted,\n deletedEdges: edgesBefore,\n deletedNotes: notesBefore,\n dryRun: false,\n present: true,\n };\n}\n\n// ─── updateTask ───────────────────────────────────────────────────────────\n\nexport interface UpdateTaskOptions {\n title?: string;\n /** 1..100; enforced by schema CHECK. */\n impact?: number;\n /** > 0; enforced by schema CHECK. */\n effortDays?: number;\n}\n\nexport interface UpdateTaskResult {\n /** True iff at least one field actually changed. */\n updated: boolean;\n /** The fields whose values differ post-update (in `UpdateTaskOptions`'s\n * camelCase shape). Empty when `updated: false`. */\n changedFields: string[];\n}\n\n/**\n * Update scalar fields on a task. Each option is independently optional;\n * passing none is a typed no-op (returns `updated: false, changedFields: []`).\n * Fields whose new value equals the current value are skipped (no row change).\n *\n * NOT for status (use `closeTask` / `openTask` / `setTaskStatus`), owner\n * (use `claimTask` / `releaseTask`), local_id (rename is deferred), or\n * workstream (cross-workstream moves are deferred).\n */\nexport interface UpdateTaskScopeOption {\n workstream: string;\n}\n\nexport function updateTask(\n db: Db,\n localId: string,\n opts: UpdateTaskOptions,\n scope: UpdateTaskScopeOption,\n): UpdateTaskResult {\n const before = getTask(db, localId, scope.workstream);\n if (!before) throw new TaskNotFoundError(localId);\n const taskId = taskIdFor(db, before.name, before.workstreamName);\n if (taskId === null) throw new TaskNotFoundError(localId);\n\n const setters: string[] = [];\n const params: unknown[] = [];\n const changedFields: string[] = [];\n\n if (opts.title !== undefined && opts.title !== before.title) {\n setters.push(\"title = ?\");\n params.push(opts.title);\n changedFields.push(\"title\");\n }\n if (opts.impact !== undefined && opts.impact !== before.impact) {\n setters.push(\"impact = ?\");\n params.push(opts.impact);\n changedFields.push(\"impact\");\n }\n if (opts.effortDays !== undefined && opts.effortDays !== before.effortDays) {\n setters.push(\"effort_days = ?\");\n params.push(opts.effortDays);\n changedFields.push(\"effortDays\");\n }\n\n if (setters.length === 0) {\n return { updated: false, changedFields: [] };\n }\n\n setters.push(\"updated_at = ?\");\n params.push(new Date().toISOString());\n params.push(taskId);\n\n db.prepare(`UPDATE tasks SET ${setters.join(\", \")} WHERE id = ?`).run(...params);\n emitEvent(\n db,\n before.workstreamName,\n `task update ${localId} (changed: ${changedFields.join(\", \")})`,\n );\n return { updated: true, changedFields };\n}\n\n// ─── reparentTask ─────────────────────────────────────────────────────────\n\nexport interface ReparentTaskResult {\n /** Edges removed (i.e. all incoming `to_task = taskId` edges). */\n removedEdges: number;\n /** Edges added (== blockers.length on success). */\n addedEdges: number;\n}\n\n/**\n * Atomically replace every incoming edge of `taskId` with new ones\n * `blocker[i] → taskId`. Pass an empty `blockers` array to clear all\n * incoming edges (the task becomes ready iff its status allows).\n *\n * Validates ALL new blockers up-front (existence + same workstream +\n * cycle check); if any fails, no DELETE happens — the call is fully\n * atomic via a single transaction.\n *\n * Cycle reasoning: removing the existing incoming edges to `taskId`\n * doesn't change `taskId`'s OUTGOING reachability, so\n * `wouldCreateCycle(db, blocker, taskId)` evaluated against the\n * pre-state gives the right answer for each new edge.\n */\nexport function reparentTask(\n db: Db,\n taskLocalId: string,\n blockers: readonly string[],\n scope: { workstream: string },\n): ReparentTaskResult {\n const task = getTask(db, taskLocalId, scope.workstream);\n if (!task) throw new TaskNotFoundError(taskLocalId);\n const taskSurrogateId = taskIdFor(db, task.name, task.workstreamName);\n if (taskSurrogateId === null) throw new TaskNotFoundError(taskLocalId);\n\n // Resolve every blocker up-front to its surrogate id; do all\n // existence + same-workstream + cycle checks before any DELETE.\n // Look up blockers across all workstreams so a blocker that exists in\n // a DIFFERENT workstream surfaces CrossWorkstreamEdgeError (clearer\n // than TaskNotFoundError).\n const blockerIds: number[] = [];\n for (const blockerLocalId of blockers) {\n if (blockerLocalId === taskLocalId) {\n throw new CycleError(blockerLocalId, taskLocalId);\n }\n const blocker = lookupTaskAnyWorkstream(db, blockerLocalId);\n if (!blocker) throw new TaskNotFoundError(blockerLocalId);\n if (blocker.workstreamName !== task.workstreamName) {\n throw new CrossWorkstreamEdgeError(\n blockerLocalId,\n blocker.workstreamName,\n taskLocalId,\n task.workstreamName,\n );\n }\n const blockerId = taskIdFor(db, blocker.name, blocker.workstreamName);\n if (blockerId === null) throw new TaskNotFoundError(blockerLocalId);\n if (wouldCreateCycle(db, blockerId, taskSurrogateId)) {\n throw new CycleError(blockerLocalId, taskLocalId);\n }\n blockerIds.push(blockerId);\n }\n\n return db.transaction(() => {\n const removed = db.prepare(\"DELETE FROM task_edges WHERE to_task_id = ?\").run(taskSurrogateId);\n const insertEdge = db.prepare(\n \"INSERT INTO task_edges (from_task_id, to_task_id, created_at) VALUES (?, ?, ?)\",\n );\n const now = new Date().toISOString();\n for (const blockerId of blockerIds) {\n insertEdge.run(blockerId, taskSurrogateId, now);\n }\n // Bump the reparented task itself — its blocker set just changed.\n // No-op when both removed and added were 0 (effectively a no-op\n // call); skip in that case so an idempotent `reparent --blocked-by\n // <same-set>` stays a true no-op for `--sort recency`.\n if (removed.changes > 0 || blockerIds.length > 0) {\n touchTask(db, taskSurrogateId, now);\n }\n const blockersBit = blockers.length > 0 ? `, new=${[...blockers].join(\",\")}` : \"\";\n emitEvent(\n db,\n task.workstreamName,\n `task reparent ${taskLocalId} (removed ${removed.changes} edges, added ${blockers.length}${blockersBit})`,\n );\n return { removedEdges: removed.changes, addedEdges: blockers.length };\n })();\n}\n","// mu — adoptAgent: register an existing tmux pane as a managed agent.\n//\n// The inverse of `mu agent spawn` — instead of creating the pane,\n// adoptAgent hooks an already-existing pane into mu's registry.\n//\n// Two flavours of resolution:\n// - by paneId ('%15') : look up directly via tmux\n// - by paneTitle ('worker-1') : scan tmux panes for matching title\n//\n// Common cases for adopt:\n// - operator manually started a CLI in a pane before installing mu\n// - mu agent close was followed by re-attaching the pane via tmux\n// - migrating from a previous orchestrator\n//\n// Extracted from src/agents.ts as part of refactor_split_large_src_files.\n\nimport {\n type AgentRow,\n getAgent,\n getAgentByPane,\n insertAgent,\n isValidAgentName,\n} from \"../agents.js\";\nimport type { Db } from \"../db.js\";\nimport { emitEvent } from \"../logs.js\";\nimport {\n PaneNotFoundError,\n type TmuxPane,\n listPanesInSession,\n paneExists,\n parseAgentNameFromTitle,\n setPaneTitle,\n} from \"../tmux.js\";\nimport { AgentExistsError, AgentNotInWorkstreamError } from \"./errors.js\";\n\nexport interface AdoptAgentOptions {\n /** tmux pane id (e.g. '%15'). Must already exist on the tmux server. */\n paneId: string;\n /** Workstream to adopt the pane into. The pane MUST be in the\n * matching tmux session (`mu-<workstream>`); cross-session adopt is\n * rejected. */\n workstream: string;\n /** Override the pane's title with this name. When omitted, the pane's\n * current title becomes the agent name (zero-config adoption). */\n name?: string;\n /** Defaults to 'pi' via the schema DEFAULT. */\n cli?: string;\n /** 'full-access' (default) or 'read-only'. */\n role?: string;\n /** Override the tmux session lookup. Defaults to `mu-<workstream>`. */\n tmuxSession?: string;\n}\n\nexport interface AdoptAgentResult {\n agent: AgentRow;\n /** True when the pane already had a matching agents row — the call\n * was a no-op (idempotent). */\n alreadyAdopted: boolean;\n /** The pane title before adopt, or null if the pane had no title. */\n previousTitle: string | null;\n /** The title the pane was set to (== agent.name post-adopt). Equal to\n * previousTitle when no retitle happened. */\n paneTitleSetTo: string;\n}\n\n/**\n * Register an existing tmux pane as a managed mu agent. Inverse of the\n * 'orphan' state surfaced by `mu agent list`: a pane that looks like an\n * agent (running pi/claude/codex) but has no DB row.\n *\n * Identity contract (matches the claim protocol invariant):\n * - Post-adopt, the pane's title equals the agent's name.\n * - When `name` is omitted, the pane's existing title becomes the\n * agent name verbatim. Adopting a pane titled 'pi' would fail name\n * validation — caller must supply --name in that case.\n *\n * Idempotent: adopting the same pane twice with the same name is a\n * no-op (returns alreadyAdopted=true). Adopting a different pane under\n * an existing agent name throws AgentExistsError.\n *\n * Validation order (matches the design in note #100):\n * 1. Pane id format -> assertValidPaneId via paneExists / setPaneTitle\n * 2. Pane exists -> PaneNotFoundError\n * 3. Pane is in session -> AgentNotInWorkstreamError (cross-session)\n * 4. Resolved name OK -> isValidAgentName / Error('agent name invalid')\n * 5. Idempotent check -> if pane already owned by an agent of this\n * name, return alreadyAdopted=true\n * 6. Name not taken -> AgentExistsError (else)\n * 7. Insert + retitle.\n *\n * Status starts at 'free' — reconcile/detect will update it on the next\n * `mu agent list` based on actual pane content (the pi prompt yields\n * 'free'; an agent mid-thought yields 'busy'; etc.). We don't run\n * detection inline here because the caller may not have $TMUX, and\n * adoption shouldn't depend on a captured-pane probe succeeding.\n */\nexport async function adoptAgent(db: Db, opts: AdoptAgentOptions): Promise<AdoptAgentResult> {\n // Step 1+2: pane format + existence.\n if (!(await paneExists(opts.paneId))) {\n throw new PaneNotFoundError(opts.paneId);\n }\n\n // Step 3: pane must be in the workstream's tmux session.\n const expectedSession = opts.tmuxSession ?? `mu-${opts.workstream}`;\n const panesInSession: TmuxPane[] = await listPanesInSession(expectedSession);\n const matchingPane = panesInSession.find((p) => p.paneId === opts.paneId);\n if (!matchingPane) {\n // Pane exists (passed step 2) but isn't in the expected session.\n // Synthesise the cross-session error using the same shape as the\n // existing AgentNotInWorkstreamError path so the CLI's exit-code\n // mapping handles it identically. We don't know the actual session\n // name without another tmux query; the message just says 'a\n // different session' — actionable enough.\n throw new AgentNotInWorkstreamError(\n `pane ${opts.paneId}`,\n opts.workstream,\n \"a different tmux session\",\n );\n }\n\n // Step 4: resolved name. Default to the pane's current title —\n // unwrapping a possibly-composed mu title ('name · <STATUS_EMOJI> · task')\n // back to just the name token. Re-adoption of a pane that mu previously\n // owned must work; without parseAgentNameFromTitle the ' · <glyph>'\n // suffix would fail isValidAgentName.\n const previousTitle = matchingPane.title.length > 0 ? matchingPane.title : null;\n const candidate =\n opts.name ?? (previousTitle !== null ? parseAgentNameFromTitle(previousTitle) : \"\");\n const resolvedName = candidate;\n if (!isValidAgentName(resolvedName)) {\n if (opts.name === undefined) {\n throw new Error(\n `pane ${opts.paneId} title '${previousTitle ?? \"(empty)\"}' is not a valid agent name; pass --name <name>`,\n );\n }\n throw new Error(`agent name invalid: '${resolvedName}'`);\n }\n\n // Step 5: idempotency. If an agent already owns this pane id, check\n // whether it's the same name; same name == no-op, different name ==\n // conflict (we don't move agents between pane ids silently).\n const existingByPane = getAgentByPane(db, opts.paneId);\n if (existingByPane) {\n if (existingByPane.name === resolvedName) {\n return {\n agent: existingByPane,\n alreadyAdopted: true,\n previousTitle,\n paneTitleSetTo: resolvedName,\n };\n }\n throw new AgentExistsError(existingByPane.name);\n }\n\n // Step 6: name not taken in THIS workstream. v5 makes agent names\n // per-workstream unique — 'worker-1' may legally exist in another\n // workstream; only refuse when it collides in the workstream we're\n // adopting into (bug_v5_name_clash_silent_misroute).\n const existingByName = getAgent(db, resolvedName, opts.workstream);\n if (existingByName) {\n throw new AgentExistsError(resolvedName);\n }\n\n // Step 7: insert + (conditional) retitle.\n const inserted = insertAgent(db, {\n name: resolvedName,\n workstream: opts.workstream,\n paneId: opts.paneId,\n status: \"free\",\n cli: opts.cli,\n role: opts.role,\n });\n if (resolvedName !== previousTitle) {\n await setPaneTitle(opts.paneId, resolvedName);\n }\n emitEvent(\n db,\n opts.workstream,\n `agent adopt ${resolvedName} (pane ${opts.paneId}, was title='${previousTitle ?? \"\"}')`,\n );\n return {\n agent: inserted,\n alreadyAdopted: false,\n previousTitle,\n paneTitleSetTo: resolvedName,\n };\n}\n","// mu — agent registry CRUD primitives + the five high-level verbs\n// (spawn, send, read, list, close) that the CLI in step 7 will wrap.\n//\n// Layering inside this file:\n//\n// - Types & raw-row mapping (RawAgentRow / rowFromDb)\n// - CRUD primitives (insertAgent, getAgent, listAgents,\n// updateAgentStatus, deleteAgent)\n// - Verbs (spawnAgent, sendToAgent, readAgent,\n// closeAgent, listLiveAgents)\n//\n// The verbs compose the CRUD primitives with src/tmux.ts and\n// src/reconcile.ts. They are deliberately thin — each one is essentially\n// \"look up the agent, do the tmux thing, update the registry.\"\n\nimport { type Db, resolveWorkstreamId, tryResolveWorkstreamId } from \"./db.js\";\nimport type { AgentStatus } from \"./detect.js\";\nimport { emitEvent } from \"./logs.js\";\nimport { type ReconcileMode, type ReconcileReport, reconcile } from \"./reconcile.js\";\nimport { captureSnapshot } from \"./snapshots.js\";\nimport { addNote, listTasksByOwner } from \"./tasks.js\";\n// Re-export the cluster modules so external callers continue to\n// `import { AgentNotFoundError, spawnAgent, ... } from \"./agents.js\"`.\nexport {\n AgentDiedOnSpawnError,\n AgentExistsError,\n AgentNotFoundError,\n AgentNotInWorkstreamError,\n WorkspacePreservedError,\n} from \"./agents/errors.js\";\nexport {\n type SpawnAgentOptions,\n defaultSpawnLivenessMs,\n resolveCliCommand,\n spawnAgent,\n} from \"./agents/spawn.js\";\nexport {\n type AdoptAgentOptions,\n type AdoptAgentResult,\n adoptAgent,\n} from \"./agents/adopt.js\";\nimport { AgentNotFoundError, WorkspacePreservedError } from \"./agents/errors.js\";\nimport {\n type CaptureOptions,\n type SendOptions,\n type TmuxPane,\n capturePane,\n killPane,\n sendToPane,\n setPaneTitle,\n} from \"./tmux.js\";\nimport { freeWorkspace, getWorkspaceForAgent } from \"./workspace.js\";\n// (freeWorkspace is used by the spawn rollback paths below, not by closeAgent.\n// Closing an agent is intentionally a separate concern from freeing its workspace;\n// see the closeAgent docstring.)\nimport { ensureWorkstream } from \"./workstream.js\";\n\nexport type { AgentStatus };\n\nexport interface AgentRow {\n name: string;\n /** Foreign-name reference to the owning workstream. */\n workstreamName: string;\n cli: string;\n paneId: string;\n status: AgentStatus;\n role: string;\n /** Window name; null when the agent has its own window named after itself. */\n tab: string | null;\n /** ISO 8601 timestamp. */\n createdAt: string;\n /** ISO 8601 timestamp. */\n updatedAt: string;\n /**\n * Derived 'idle but assigned' flag (idle_assigned_agent_detection).\n * Set ONLY by `listLiveAgents` (and the helper `computeAgentIdle`);\n * never stored in the DB. Predicate:\n * status === 'needs_input'\n * AND owns ≥1 IN_PROGRESS task in this workstream\n * AND (now - updated_at) >= MU_IDLE_THRESHOLD_MS (default 300_000ms)\n *\n * Surfaces the third lifecycle state (alive but assigned, no recent\n * progress) to `mu state` renders + `mu state --json`. Omitted (i.e.\n * absent — NOT `false`) when the predicate doesn't fire, so JSON\n * consumers can do a simple `if (agent.idle)` check and the field\n * stays out of the way for callers that don't care.\n */\n idle?: boolean;\n}\n\n/** Default idle threshold. Matches today's `mu task wait --stuck-after`\n * default so the two paths agree on what counts as 'stalled'. */\nconst DEFAULT_IDLE_THRESHOLD_MS = 300_000;\n\n/**\n * Read the operator-tunable idle threshold (`MU_IDLE_THRESHOLD_MS`).\n * Returns the default on any unparsable / negative value rather than\n * throwing — env-var typos shouldn't crash `mu state`.\n */\nexport function idleThresholdMs(): number {\n const env = process.env.MU_IDLE_THRESHOLD_MS;\n if (env === undefined || env === \"\") return DEFAULT_IDLE_THRESHOLD_MS;\n const n = Number.parseInt(env, 10);\n if (!Number.isFinite(n) || n < 0) return DEFAULT_IDLE_THRESHOLD_MS;\n return n;\n}\n\n/**\n * Decide whether an agent is in the 'idle but assigned' state. Pure\n * read on (agents, tasks); no side effects. Exported so `listLiveAgents`,\n * the renderers, and tests can share one source of truth.\n */\nexport function computeAgentIdle(db: Db, agent: AgentRow, now: number = Date.now()): boolean {\n if (agent.status !== \"needs_input\") return false;\n const threshold = idleThresholdMs();\n if (threshold <= 0) return false;\n const updated = Date.parse(agent.updatedAt);\n if (!Number.isFinite(updated)) return false;\n if (now - updated < threshold) return false;\n const wsId = tryResolveWorkstreamId(db, agent.workstreamName);\n if (wsId === null) return false;\n const row = db\n .prepare(\n `SELECT COUNT(*) AS n\n FROM tasks t\n JOIN agents a ON a.id = t.owner_id\n WHERE a.name = ? AND a.workstream_id = ? AND t.status = 'IN_PROGRESS'`,\n )\n .get(agent.name, wsId) as { n: number };\n return row.n > 0;\n}\n\nexport interface InsertAgentInput {\n name: string;\n workstream: string;\n paneId: string;\n status: AgentStatus;\n /** Defaults to \"pi\" via schema DEFAULT. */\n cli?: string;\n /** Defaults to \"full-access\" via schema DEFAULT. */\n role?: string;\n tab?: string | null;\n}\n\ninterface RawAgentRow {\n /** Surrogate id (v5). Carried through so internal helpers don't have\n * to re-resolve when they need it. */\n id: number;\n name: string;\n /** Joined from workstreams.name. */\n workstream: string;\n cli: string;\n pane_id: string;\n status: string;\n role: string;\n tab: string | null;\n created_at: string;\n updated_at: string;\n}\n\n/** SELECT clause that joins agents to workstreams, exposing the\n * operator-facing workstream name as `workstream`. Used by every\n * read path. */\nconst SELECT_AGENT_COLS = `\n a.id AS id,\n a.name AS name,\n ws.name AS workstream,\n a.cli AS cli,\n a.pane_id AS pane_id,\n a.status AS status,\n a.role AS role,\n a.tab AS tab,\n a.created_at AS created_at,\n a.updated_at AS updated_at\n`;\n\nconst AGENT_FROM_JOIN = \"FROM agents a JOIN workstreams ws ON ws.id = a.workstream_id\";\n\nfunction rowFromDb(row: RawAgentRow): AgentRow {\n return {\n name: row.name,\n workstreamName: row.workstream,\n cli: row.cli,\n paneId: row.pane_id,\n status: row.status as AgentStatus,\n role: row.role,\n tab: row.tab,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\n/** Resolve an agent's surrogate id by (workstream, name). Returns\n * null on miss. */\nfunction agentIdByName(db: Db, name: string, workstream: string): number | null {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return null;\n const row = db\n .prepare(\"SELECT id FROM agents WHERE name = ? AND workstream_id = ?\")\n .get(name, wsId) as { id: number } | undefined;\n return row ? row.id : null;\n}\n\nexport function insertAgent(db: Db, input: InsertAgentInput): AgentRow {\n // Auto-create the workstreams row if missing so the FK on\n // agents.workstream_id is always satisfied. Preserves the ergonomics\n // where you could spawn without explicit `mu init`.\n ensureWorkstream(db, input.workstream);\n const workstreamId = resolveWorkstreamId(db, input.workstream);\n const now = new Date().toISOString();\n db.prepare(\n `INSERT INTO agents (name, workstream_id, cli, pane_id, status, role, tab, created_at, updated_at)\n VALUES (@name, @workstreamId, COALESCE(@cli, 'pi'), @paneId, @status,\n COALESCE(@role, 'full-access'), @tab, @now, @now)`,\n ).run({\n name: input.name,\n workstreamId,\n cli: input.cli ?? null,\n paneId: input.paneId,\n status: input.status,\n role: input.role ?? null,\n tab: input.tab ?? null,\n now,\n });\n const row = getAgent(db, input.name, input.workstream);\n if (!row) throw new Error(`agents.insertAgent: row not found after insert: ${input.name}`);\n return row;\n}\n\n/**\n * Look up an agent by its tmux pane id (e.g. `%4`). Returns undefined if\n * no agent currently owns that pane. Used by `mu me` and friends to\n * answer \"which agent am I?\" from `$TMUX_PANE` without the LLM having to\n * remember its own name.\n *\n * Note: `pane_id` is not declared UNIQUE in the schema (a managed agent\n * could in theory be re-spawned into the same recycled pane id) but in\n * practice tmux pane ids are unique within a server's lifetime, and\n * reconcile prunes ghosts. We return the first match.\n */\nexport function getAgentByPane(db: Db, paneId: string): AgentRow | undefined {\n const row = db\n .prepare(`SELECT ${SELECT_AGENT_COLS} ${AGENT_FROM_JOIN} WHERE a.pane_id = ? LIMIT 1`)\n .get(paneId) as RawAgentRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\nexport function getAgent(db: Db, name: string, workstream: string): AgentRow | undefined {\n // v5: agents.name is per-workstream unique, not globally unique.\n // Workstream is required so the same name in two workstreams\n // resolves unambiguously.\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return undefined;\n const row = db\n .prepare(\n `SELECT ${SELECT_AGENT_COLS} ${AGENT_FROM_JOIN} WHERE a.name = ? AND a.workstream_id = ?`,\n )\n .get(name, wsId) as RawAgentRow | undefined;\n return row ? rowFromDb(row) : undefined;\n}\n\nexport function listAgents(db: Db, opts: { workstream?: string } = {}): AgentRow[] {\n if (opts.workstream === undefined) {\n const rows = db\n .prepare(`SELECT ${SELECT_AGENT_COLS} ${AGENT_FROM_JOIN} ORDER BY ws.name, a.name`)\n .all() as RawAgentRow[];\n return rows.map(rowFromDb);\n }\n const wsId = tryResolveWorkstreamId(db, opts.workstream);\n if (wsId === null) return [];\n const rows = db\n .prepare(\n `SELECT ${SELECT_AGENT_COLS} ${AGENT_FROM_JOIN} WHERE a.workstream_id = ? ORDER BY a.name`,\n )\n .all(wsId) as RawAgentRow[];\n return rows.map(rowFromDb);\n}\n\n/**\n * Update an agent's status. Returns true if a row was matched.\n * Also bumps updated_at. Workstream is required (v5: agents.name is\n * per-workstream unique).\n */\nexport function updateAgentStatus(\n db: Db,\n name: string,\n status: AgentStatus,\n workstream: string,\n): boolean {\n const wsId = tryResolveWorkstreamId(db, workstream);\n if (wsId === null) return false;\n const result = db\n .prepare(\"UPDATE agents SET status = ?, updated_at = ? WHERE name = ? AND workstream_id = ?\")\n .run(status, new Date().toISOString(), name, wsId);\n return result.changes > 0;\n}\n\n/** rowFromDb retains the surrogate id but the public AgentRow shape\n * stays operator-facing; the id is internal-only. Strip it on the way\n * out by destructuring. */\n\n/**\n * Decide whether a scrollback-detected status should overwrite the\n * persisted one.\n *\n * `free` is sticky until the agent shows real activity:\n * - free + needs_input → stay free (user explicitly marked it free;\n * idle prompt isn't activity)\n * - free + busy → flip to busy\n * - free + needs_permission → flip (a permission prompt IS activity)\n *\n * Every other persisted status is auto-derived; overwrite freely. This\n * lets `spawning → busy/needs_input/needs_permission` happen on the\n * first reconcile after spawn.\n *\n * Lives on the agent (not on reconcile) because it's a property of the\n * agent's status field — both the periodic-reconcile loop and the\n * inline single-agent reconcile in `mu agent show` share this policy.\n */\nexport function shouldOverwriteAgentStatus(current: AgentStatus, detected: AgentStatus): boolean {\n if (current === \"free\") {\n return detected === \"busy\" || detected === \"needs_permission\";\n }\n return true;\n}\n\n// ─── Pane title composition (mu's interpreted state on the border) ───\n//\n// The pane border (set by enableMuPaneBorders) renders\n// `[mu] #{pane_title}` as tmux chrome. mu owns the pane title and uses\n// it to carry interpreted state at a glance. The status glyph in each\n// example below is whatever STATUS_EMOJI resolves to today (see the\n// table 30 lines down) — do NOT duplicate the codepoints in this\n// comment, they have drifted from production once already.\n//\n// worker-a (no claim, status not\n// worth surfacing yet)\n// worker-a · <STATUS_EMOJI.busy> (busy, no claim)\n// worker-a · <STATUS_EMOJI.busy> · build_x (busy, owns one task)\n// worker-a · <STATUS_EMOJI.needs_input> · build_x\n// worker-a · <STATUS_EMOJI.needs_permission> · build_x\n// worker-a · <STATUS_EMOJI.free> (free, no claim)\n// worker-a · <STATUS_EMOJI.busy> · ⊕2 tasks (multi-claim case)\n//\n// The agent name MUST remain the first ' · '-separated token so the\n// claim protocol's pane-title-as-identity fallback (currentPaneTitle\n// in src/tmux.ts) keeps working. Adopted panes that haven't been\n// re-titled by mu just have the name (one token) — still parses.\n\n/** Plain-text emoji map for the agent status. Mirrors statusIcon in\n * cli.ts but without picocolors (tmux pane titles don't render ANSI\n * colour). 'spawning' is omitted on purpose — the title gets the\n * initial render before status detection runs, and 'spawning' is a\n * transient state. */\n// Single-codepoint, single-cell-width Nerd Font glyphs (nf-fa family).\n// Picked over Unicode emoji so cli-table3's column widths line up:\n// Unicode emoji like a gear-with-variation-selector are TWO\n// codepoints, which cli-table3 counts as length-2 and uses to size\n// columns; but terminals render them as ONE cell wide, so adjacent\n// rows that mix 1-codepoint and 2-codepoint emoji misalign. Nerd Font\n// glyphs are private-use codepoints, all length-1 and all 1-cell-wide.\n//\n// Requires a Nerd Font on the operator's terminal (mu's substrate is\n// pi, which assumes Nerd Fonts; the rest of mu's TUI uses Nerd Font\n// glyphs already in cli-table3 box-drawing). Without one, every\n// glyph below renders as a placeholder box — the columns still align\n// (which was the bug we were fixing).\nexport const STATUS_EMOJI: Record<AgentStatus, string> = {\n spawning: \"\\uf251\", // nf-fa-hourglass_start\n busy: \"\\uf013\", // nf-fa-cog\n needs_input: \"\\uf186\", // nf-fa-moon_o\n needs_permission: \"\\uf023\", // nf-fa-lock\n free: \"\\uf058\", // nf-fa-check_circle\n unreachable: \"\\uf059\", // nf-fa-question_circle\n terminated: \"\\uf057\", // nf-fa-times_circle\n};\n\n/** Maximum total length for a composed pane title. tmux truncates\n * silently in some chrome positions; we truncate the task id\n * ourselves so the suffix is predictable. */\nconst MAX_TITLE_LEN = 64;\n\n/**\n * Placeholder pane-id prefix used during the `--workspace` pre-stage in\n * spawnAgent (src/agents/spawn.ts).\n *\n * The placeholder unblocks the FK-ordering cycle:\n * - vcs_workspaces.agent FK requires an agents row\n * - agents.pane_id is NOT NULL\n * - pane creation needs the workspace path as cwd\n * So we insert the agent with a placeholder pane_id, then create the\n * workspace, then the real pane, then patch pane_id.\n *\n * Because no real tmux pane has this prefix, ANY mutating reconcile\n * pass would treat the placeholder row as a ghost and prune it\n * (→ FK-failure on the workspace insert mid-spawn). Callers MUST:\n * - either guard against it explicitly (refreshAgentTitle), OR\n * - run reconcile in mode: \"status-only\" or \"report-only\"\n * (status pollers and read-only diagnostic verbs; see\n * `listLiveAgents.mode` rationale below). status-only also\n * skips status detection on placeholder rows directly\n * (reconcile() uses isPendingPaneId for that).\n *\n * Bug surfaced as bug_agent_spawn_workspace_fk_failure.\n */\nexport const PENDING_PANE_PREFIX = \"%pending-\";\n\n/** Build the placeholder pane id for an agent during workspace pre-stage. */\nexport function pendingPaneIdFor(agentName: string): string {\n return `${PENDING_PANE_PREFIX}${agentName}`;\n}\n\n/** True iff `paneId` is a `--workspace` pre-stage placeholder (not yet patched\n * to the real tmux pane id). */\nexport function isPendingPaneId(paneId: string): boolean {\n return paneId.startsWith(PENDING_PANE_PREFIX);\n}\n\n/** Build the pane title for `agent` based on current DB state.\n * Pure (no tmux side effect; no DB write). Read-only on the DB. */\nexport function composeAgentTitle(db: Db, agent: AgentRow): string {\n // 'spawning' is the initial state at row insert. Don't decorate —\n // surfaces as just the agent name until detection runs.\n const showStatus = agent.status !== \"spawning\";\n // Scope by the agent's workstream so a same-named worker in another\n // workstream can't pollute this title's task list.\n const tasks = listTasksByOwner(db, agent.workstreamName, agent.name);\n let title = agent.name;\n if (showStatus) {\n title += ` · ${STATUS_EMOJI[agent.status]}`;\n }\n if (tasks.length === 1) {\n title += ` · ${tasks[0]?.name}`;\n } else if (tasks.length > 1) {\n title += ` · ⊕${tasks.length} tasks`;\n }\n if (title.length > MAX_TITLE_LEN) {\n // Truncate from the END (preserves agent name + status prefix).\n title = `${title.slice(0, MAX_TITLE_LEN - 1)}…`;\n }\n return title;\n}\n\n/** Push a fresh pane title for `agentName`. Best-effort — a missing\n * agent, a placeholder pane id, or a tmux failure are all swallowed\n * silently (titles are decorative; never block the calling verb). */\nexport async function refreshAgentTitle(\n db: Db,\n agentName: string,\n workstream: string,\n): Promise<void> {\n const agent = getAgent(db, agentName, workstream);\n if (!agent) return;\n if (isPendingPaneId(agent.paneId)) return; // workspace pre-stage placeholder; see PENDING_PANE_PREFIX\n const title = composeAgentTitle(db, agent);\n await setPaneTitle(agent.paneId, title).catch(() => {});\n}\n\n/**\n * Delete an agent row. Returns true if a row was matched. Idempotent;\n * deleting an agent that doesn't exist returns false without throwing.\n *\n * Reaper side-effect: any task that was IN_PROGRESS owned by this\n * agent gets flipped back to OPEN with a `[reaper]` task_note and a\n * `task reap` event in `agent_logs`. The FK on `tasks.owner` is\n * `ON DELETE SET NULL` so the owner column resets automatically; the\n * extra step here is the status revert. Without this an agent that\n * crashed (or was explicitly closed mid-task) leaves the task graph\n * in a wrong state — IN_PROGRESS forever, with no owner to release.\n */\nexport function deleteAgent(db: Db, name: string, workstream: string): boolean {\n // Snapshot the stuck tasks BEFORE the DELETE; the FK CASCADE\n // (SET NULL on owner_id) makes the post-delete query indistinguishable\n // from \"never owned by this agent.\"\n const agentId = agentIdByName(db, name, workstream);\n if (agentId === null) {\n // Already gone — idempotent return. (Could happen if reconcile\n // pruned a ghost concurrently.) The DELETE is a no-op.\n return false;\n }\n const stuck = db\n .prepare(\n `SELECT t.id AS taskId, t.local_id AS localId, ws.name AS workstream\n FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE t.owner_id = ? AND t.status = 'IN_PROGRESS'`,\n )\n .all(agentId) as Array<{ taskId: number; localId: string; workstream: string }>;\n\n const result = db.prepare(\"DELETE FROM agents WHERE id = ?\").run(agentId);\n if (result.changes === 0) return false;\n\n for (const t of stuck) {\n db.prepare(\"UPDATE tasks SET status = 'OPEN', updated_at = ? WHERE id = ?\").run(\n new Date().toISOString(),\n t.taskId,\n );\n addNote(\n db,\n t.localId,\n `[reaper] previous owner ${name} gone (agent removed); status reverted IN_PROGRESS → OPEN, owner cleared`,\n { author: \"reaper\", workstream: t.workstream },\n );\n emitEvent(\n db,\n t.workstream,\n `task reap ${t.localId} (previous owner ${name} gone, IN_PROGRESS → OPEN)`,\n );\n }\n return true;\n}\n\n// ────────────────────────────────────────────────────────────────────────\n// High-level verbs (spawn, send, read, list, close)\n// ────────────────────────────────────────────────────────────────────────\n\n/** Allowed agent name shape: lowercase alpha first, then alnum/underscore/\n * hyphen. Mirrors VOCABULARY.md §\"Naming conventions\". */\nconst AGENT_NAME_RE = /^[a-z][a-z0-9_-]{0,31}$/;\n\nexport function isValidAgentName(name: string): boolean {\n return AGENT_NAME_RE.test(name);\n}\n\n/**\n * Send a single line of text to an agent's pane and submit it. Uses the\n * canonical bracketed-paste protocol from src/tmux.ts.\n */\nexport async function sendToAgent(\n db: Db,\n name: string,\n text: string,\n opts: SendOptions & { workstream: string },\n): Promise<void> {\n const agent = getAgent(db, name, opts.workstream);\n if (!agent) throw new AgentNotFoundError(name);\n await sendToPane(agent.paneId, text, opts);\n}\n\n/**\n * Read scrollback from an agent's pane. With no options, returns the full\n * scrollback (`-S - -E -`); with `lines: N`, returns only the last N lines.\n */\nexport async function readAgent(\n db: Db,\n name: string,\n opts: CaptureOptions & { workstream: string },\n): Promise<string> {\n const agent = getAgent(db, name, opts.workstream);\n if (!agent) throw new AgentNotFoundError(name);\n return capturePane(agent.paneId, opts);\n}\n\n// ─── freeAgent (verb) ─────────────────────────────────────────────────────\n\nexport interface FreeAgentResult {\n /** Status before the call. */\n previousStatus: AgentStatus;\n /** Status after the call (always 'free' on success). */\n status: AgentStatus;\n /** True iff the row actually changed. False on idempotent no-op. */\n changed: boolean;\n}\n\n/**\n * Mark an agent's status as `free` — the explicit \"I'm done with you\n * for now; you're available\" signal. The agent's pane and DB row are\n * untouched; reconcile treats `free` as sticky (only flips back to busy\n * on real activity, never on an idle prompt) so this verb composes\n * cleanly with the existing scrollback detector.\n *\n * Idempotent: setting an already-free agent to free is a no-op (returns\n * `changed: false`). Throws AgentNotFoundError on missing.\n */\nexport function freeAgent(db: Db, name: string, workstream: string): FreeAgentResult {\n const before = getAgent(db, name, workstream);\n if (!before) throw new AgentNotFoundError(name);\n if (before.status === \"free\") {\n return { previousStatus: before.status, status: \"free\", changed: false };\n }\n updateAgentStatus(db, name, \"free\", before.workstreamName);\n emitEvent(db, before.workstreamName, `agent free ${name} (was ${before.status})`);\n return { previousStatus: before.status, status: \"free\", changed: true };\n}\n\nexport interface CloseAgentOptions {\n /**\n * When true, free the agent's workspace BEFORE deleting the agent\n * (so we control the order rather than relying on FK cascade, which\n * leaves the on-disk dir orphaned). Lossy: any pending changes in\n * the workspace are gone unless the caller frees with `--commit`\n * separately first.\n *\n * When false (default) and a workspace exists, throws\n * WorkspacePreservedError so the caller has to decide explicitly.\n * Surfaced as a real bug in the multi-agent dogfood teardown.\n */\n discardWorkspace?: boolean;\n}\n\nexport interface CloseAgentResult {\n killedPane: boolean;\n deletedRow: boolean;\n /** True iff the agent had an associated workspace AND the caller\n * passed `discardWorkspace: true` so we proactively freed it.\n * False on the no-workspace path (nothing to free) and on the\n * refused path (we threw before doing anything). */\n workspaceFreed: boolean;\n}\n\n/**\n * Close an agent: kill its tmux pane and remove its DB row. Idempotent:\n * - if the agent doesn't exist in the DB, returns a no-op result\n * - if the tmux pane is already gone, killPane swallows the error\n *\n * Workspace handling: closing an agent and freeing its workspace are\n * separate concerns (agent lifecycle vs disk artifacts), so by default\n * `closeAgent` REFUSES if the agent has a workspace — you'd otherwise\n * orphan the on-disk dir (the FK cascade drops the registry row but\n * not the directory). Two ways to proceed:\n *\n * 1. `freeWorkspace(db, name)` first, then `closeAgent(db, name)`.\n * Preserves the option to `--commit` pending changes.\n * 2. `closeAgent(db, name, { discardWorkspace: true })`. One-shot;\n * lossy.\n *\n * The CLI surfaces these as the two actionable nextSteps on the\n * `WorkspacePreservedError` thrown by the refuse path.\n */\nexport async function closeAgent(\n db: Db,\n name: string,\n opts: CloseAgentOptions & { workstream: string },\n): Promise<CloseAgentResult> {\n const agent = getAgent(db, name, opts.workstream);\n if (!agent) {\n return { killedPane: false, deletedRow: false, workspaceFreed: false };\n }\n const ws = getWorkspaceForAgent(db, name, agent.workstreamName);\n if (ws !== undefined && opts.discardWorkspace !== true) {\n throw new WorkspacePreservedError(name, ws.path);\n }\n // Pre-mutation snapshot (snap_design §CAPTURE STRATEGY > WHEN).\n // Captures the agent row + the FK SET NULL ripple onto tasks.owner +\n // (when --discard-workspace) the vcs_workspaces row. Workstream is\n // recorded so this snapshot is filterable in `mu snapshot list`.\n captureSnapshot(db, `agent close ${name}`, agent.workstreamName);\n // Free the workspace BEFORE the agent (so the on-disk dir is\n // removed cleanly, not orphaned by FK cascade). freeWorkspace is\n // idempotent on missing rows.\n let workspaceFreed = false;\n if (ws !== undefined && opts.discardWorkspace === true) {\n await freeWorkspace(db, name, { commit: false, workstream: agent.workstreamName });\n workspaceFreed = true;\n }\n await killPane(agent.paneId).catch(() => {\n /* idempotent — pane may already be gone */\n });\n const deletedRow = deleteAgent(db, name, agent.workstreamName);\n emitEvent(\n db,\n agent.workstreamName,\n `agent close ${name} (pane=${agent.paneId}${workspaceFreed ? \", workspace discarded\" : \"\"})`,\n );\n return {\n killedPane: true,\n deletedRow,\n workspaceFreed,\n };\n}\n\nexport interface ListLiveAgentsOptions {\n workstream: string;\n tmuxSession?: string;\n /**\n * Which kind of reconciliation pass to run. Forwarded to\n * `reconcile()`'s same-name option. Default `\"full\"` (the\n * documented mutating behaviour `mu agent list` has always had).\n *\n * Read-only callers split two ways:\n * - `mu hud`, `mu state`, bare `mu`, `mu agent attach` →\n * `\"status-only\"`: refresh status + title (writes to DB),\n * skip prune + reap. The operator's primary signal\n * (busy/needs_input) stays fresh without a periodic poll\n * racing in-flight spawns.\n * - `mu doctor`, `mu undo` → `\"report-only\"`: count drift,\n * mutate nothing. `mu undo` MUST use this so a post-restore\n * reconcile doesn't delete the rows the snapshot just\n * restored (snap_undo_reconcile_destroys_recovered_agents).\n *\n * Skipping prune is what protects mid-spawn placeholders (pane\n * id `%pending-<name>`) from being treated as ghosts and pruned\n * out from under `createWorkspace`'s FK insert\n * (bug_agent_spawn_workspace_fk_failure).\n *\n * BREAKING: this replaces the previous `dryRun?: boolean`\n * option. Migration: `dryRun: true` → `mode: \"report-only\"`;\n * default (`dryRun: false` / unset) → `mode: \"full\"`.\n */\n mode?: ReconcileMode;\n}\n\nexport interface LiveAgentsView {\n /** All registered agents in the workstream, post-reconcile. */\n agents: AgentRow[];\n /** Panes in the tmux session that look like agents but aren't registered. */\n orphans: TmuxPane[];\n /** Diagnostic numbers from the reconcile pass; useful for `mu doctor`. */\n report: ReconcileReport;\n}\n\n/**\n * Return the live, reality-reconciled view of agents in a workstream.\n * `mu agent list` calls this with `mode: \"full\"` (mutating); status\n * pollers (`mu hud`, `mu state`, bare `mu`, `mu agent attach`) call\n * it with `mode: \"status-only\"` to refresh status without pruning;\n * read-only diagnostic / restore paths (`mu doctor`, `mu undo`)\n * call it with `mode: \"report-only\"` to mutate nothing at all.\n */\nexport async function listLiveAgents(db: Db, opts: ListLiveAgentsOptions): Promise<LiveAgentsView> {\n const report = await reconcile(db, {\n workstream: opts.workstream,\n ...(opts.tmuxSession !== undefined ? { tmuxSession: opts.tmuxSession } : {}),\n ...(opts.mode !== undefined ? { mode: opts.mode } : {}),\n });\n const baseAgents = listAgents(db, { workstream: opts.workstream });\n // Enrich with the derived `idle` flag (idle_assigned_agent_detection).\n // One COUNT per agent — cheap; the agents table in any one workstream\n // is small (typical wave: <10 rows). We add the field only when\n // idle=true, so non-idle rows JSON-serialize without the noise.\n const now = Date.now();\n const agents: AgentRow[] = baseAgents.map((a) =>\n computeAgentIdle(db, a, now) ? { ...a, idle: true } : a,\n );\n return { agents, orphans: report.orphans, report };\n}\n","// mu — `mu task` query verbs (read-only; no DB writes).\n//\n// list, next, owned-by.\n// Plus the `mu me tasks` / `mu me next` agent-self subcommands (also\n// read-only; they query against `resolveSelf(db)` instead of -w —\n// wired in src/cli/agents.ts via `wireSelfCommands`).\n//\n// Extracted from src/cli/tasks.ts as part of refactor_split_large_src_files.\n//\n// Removed in audit_cleanups_post_schema_v5_wave: `task blocked`,\n// `task goals`, `task search`, `task ready` (the latter merged into\n// `task next -n 0`, which now means \"all ready, unlimited\"). The\n// underlying SDK helpers (`listBlocked`, `listGoals`, `searchTasks`)\n// survive — `mu state` / `mu tracks` consume them, and `searchTasks`\n// keeps its unit-test coverage as reusable surface. The audit's SQL\n// recipes for the removed verbs live in docs/USAGE_GUIDE.md\n// \"What's NOT in 0.2.0\".\n\nimport {\n type TaskSortKey,\n byRoiDesc,\n emitJsonCollection,\n formatTaskListTable,\n parseSortOption,\n parseStatusesOption,\n relTimeBasisForSort,\n resolveSelf,\n resolveWorkstream,\n sortTasks,\n withRoiAll,\n} from \"../../cli.js\";\nimport type { Db } from \"../../db.js\";\nimport { pc } from \"../../output.js\";\nimport {\n listReady,\n listTasks,\n listTasksByOwner,\n listTasksByOwnerCrossWorkstream,\n} from \"../../tasks.js\";\n\nexport async function cmdMyTasks(\n db: Db,\n opts: { json?: boolean; includeClosed?: boolean } = {},\n): Promise<void> {\n const self = resolveSelf(db);\n // Scope by self.workstream so a same-named worker in another\n // workstream can't pollute this list.\n const tasks = listTasksByOwner(db, self.workstreamName, self.name, {\n includeClosed: opts.includeClosed ?? false,\n });\n if (opts.json) {\n emitJsonCollection(withRoiAll(tasks));\n return;\n }\n if (tasks.length === 0) {\n console.log(pc.dim(`(${self.name} owns no tasks)`));\n return;\n }\n console.log(formatTaskListTable(tasks));\n}\n\nexport async function cmdMyNext(db: Db, opts: { lines?: number; json?: boolean }): Promise<void> {\n const self = resolveSelf(db);\n const k = opts.lines ?? 1;\n const sorted = listReady(db, self.workstreamName).sort(byRoiDesc);\n const tasks = k === 0 ? sorted : sorted.slice(0, k);\n if (opts.json) {\n emitJsonCollection(withRoiAll(tasks));\n return;\n }\n if (tasks.length === 0) {\n console.log(pc.dim(`(no ready tasks in ${self.workstreamName})`));\n return;\n }\n console.log(formatTaskListTable(tasks));\n}\n\nexport async function cmdTaskList(\n db: Db,\n opts: { workstream?: string; json?: boolean; status?: string[]; sort?: string },\n): Promise<void> {\n const workstream = await resolveWorkstream(opts.workstream);\n const listOpts: Parameters<typeof listTasks>[2] = {};\n // --status accepts repeat OR comma-separate OR mix; parseStatusesOption\n // handles parseCsvFlag + per-element validate + dedup. Returns\n // undefined for no filter (matches today's no-flag shape).\n const statuses = parseStatusesOption(opts.status);\n if (statuses !== undefined) {\n listOpts.status = statuses;\n }\n // Default sort for `mu task list` is `id` (preserves prior\n // behaviour: SQL ORDER BY local_id). The other read verbs default\n // to `roi` because their primary use is \"what should I do next\".\n const sortKey: TaskSortKey = opts.sort === undefined ? \"id\" : parseSortOption(opts.sort);\n const tasks = sortTasks(listTasks(db, workstream, listOpts), sortKey);\n if (opts.json) {\n emitJsonCollection(withRoiAll(tasks));\n return;\n }\n console.log(pc.bold(`mu-${workstream}`));\n const tableOpts: Parameters<typeof formatTaskListTable>[1] = {};\n const basis = relTimeBasisForSort(sortKey);\n if (basis !== null) tableOpts.relTimeBasis = basis;\n console.log(formatTaskListTable(tasks, tableOpts));\n}\n\n// ROI = impact / effort_days. Higher first. Tasks with effortDays=0\n// (would divide by zero) sort to the top by treating their ROI as Infinity.\n//\n// `-n 0` means \"unlimited\" — the merged-in `task ready` semantics\n// from audit_merge_task_ready_into_next. Default K=1 keeps the\n// historical \"what should I do right now?\" shape.\nexport async function cmdTaskNext(\n db: Db,\n opts: { workstream?: string; lines?: number; json?: boolean; sort?: string; status?: string[] },\n): Promise<void> {\n const workstream = await resolveWorkstream(opts.workstream);\n const k = opts.lines ?? 1;\n const sortKey: TaskSortKey = opts.sort === undefined ? \"roi\" : parseSortOption(opts.sort);\n const readyOpts: Parameters<typeof listReady>[2] = {};\n const statuses = parseStatusesOption(opts.status);\n if (statuses !== undefined) {\n readyOpts.status = statuses;\n }\n const sorted = sortTasks(listReady(db, workstream, readyOpts), sortKey);\n const tasks = k === 0 ? sorted : sorted.slice(0, k);\n if (opts.json) {\n emitJsonCollection(withRoiAll(tasks));\n return;\n }\n if (tasks.length === 0) {\n console.log(pc.dim(\"(no ready tasks)\"));\n return;\n }\n const tableOpts: Parameters<typeof formatTaskListTable>[1] = {};\n const basis = relTimeBasisForSort(sortKey);\n if (basis !== null) tableOpts.relTimeBasis = basis;\n console.log(formatTaskListTable(tasks, tableOpts));\n}\n\nexport async function cmdTaskOwnedBy(\n db: Db,\n agent: string,\n opts: { json?: boolean; includeClosed?: boolean; workstream?: string; all?: boolean } = {},\n): Promise<void> {\n // Default behaviour: scope to the resolved workstream (so the common\n // case 'mu task owned-by worker-1' returns only this workstream's\n // worker-1, not every workstream's). --all explicitly opts into the\n // cross-workstream view via listTasksByOwnerCrossWorkstream.\n const includeClosed = opts.includeClosed ?? false;\n const tasks = opts.all\n ? listTasksByOwnerCrossWorkstream(db, agent, { includeClosed })\n : listTasksByOwner(db, await resolveWorkstream(opts.workstream), agent, { includeClosed });\n if (opts.json) {\n emitJsonCollection(withRoiAll(tasks));\n return;\n }\n if (tasks.length === 0) {\n console.log(pc.dim(`(no tasks owned by ${agent})`));\n return;\n }\n // Surface the workstream column when we're showing cross-workstream\n // results (--all); for the scoped (default) case the column would\n // be redundant.\n console.log(formatTaskListTable(tasks, { withWorkstream: opts.all === true }));\n}\n","// mu — `mu task` create/read/edit verbs (add / show / notes / note / update).\n//\n// add → addTask, with auto-id derivation when localId omitted.\n// show → row + edges + notes (+ lastClaimActor when owner=NULL).\n// notes / note → list + append on task_notes.\n// update → scalar edits (title/impact/effortDays).\n//\n// Plus the helpers:\n// unescapeNoteText — \\n / \\t / \\r / \\\\ inside a quoted string body\n// printNote — shared note formatter for show + notes\n//\n// `who claimed this task?` for the owner=NULL --self path comes from\n// the SDK's lastClaimActor (src/logs.ts) directly — the prior CLI-side\n// thin-wrapper was a vestigial re-export from the cluster split, gone\n// in schema_v5_cleanups.\n//\n// Extracted from src/cli/tasks.ts as part of the wire-out follow-up\n// to refactor_split_large_src_files.\n\nimport {\n UsageError,\n assertTaskInWorkstream,\n colorStatus,\n emitJson,\n emitJsonCollection,\n parseCsvFlag,\n resolveEntityRef,\n resolveWorkstream,\n withRoiAll,\n} from \"../../cli.js\";\nimport type { Db } from \"../../db.js\";\nimport { lastClaimActor } from \"../../logs.js\";\nimport { type NextStep, pc, printNextSteps } from \"../../output.js\";\nimport {\n type TaskEdgeWithStatus,\n TaskNotFoundError,\n type TaskNoteRow,\n type UpdateTaskOptions,\n addNote,\n addTask,\n getTask,\n getTaskEdgesWithStatus,\n idFromTitleVerbose,\n listNotes,\n resolveActorIdentity,\n updateTask,\n} from \"../../tasks.js\";\n\n/**\n * Translate the small set of shell-style escapes (\\n, \\t, \\r, \\\\)\n * inside a note body so a heredoc-free shell call can write a\n * multi-line note in one quoted string:\n *\n * mu task note auth \"FILES: a.rs:45\\nDECISION: chose JWT\"\n *\n * Backslashes are protected via a NUL placeholder so `\\\\n` stays as\n * a literal `\\n` in the output rather than being processed twice.\n */\nexport function unescapeNoteText(s: string): string {\n // Single-pass regex: match a backslash followed by one of the four\n // recognised escape characters and decide per-match what to emit.\n // This avoids the in-band-sentinel pattern (where a placeholder string\n // could in principle collide with legitimate note content) and means\n // `\\\\n` correctly yields a literal `\\n` rather than a newline, because\n // the leading `\\\\` consumes the backslash before `\\n` can match.\n return s.replace(/\\\\([\\\\ntr])/g, (_, ch: string) => {\n switch (ch) {\n case \"\\\\\":\n return \"\\\\\";\n case \"n\":\n return \"\\n\";\n case \"t\":\n return \"\\t\";\n case \"r\":\n return \"\\r\";\n default:\n return _;\n }\n });\n}\n\nexport function printNote(n: TaskNoteRow): void {\n const author = n.author ?? \"<orchestrator>\";\n console.log(` ${pc.dim(n.createdAt)} ${pc.bold(author)}`);\n for (const line of n.content.split(\"\\n\")) {\n console.log(` ${line}`);\n }\n}\n\n/** Split a list of edge neighbours into (still-gating, satisfied)\n * buckets. CLOSED is the only status that satisfies a `blocks` edge\n * per src/tasks/status.ts; REJECTED/DEFERRED still gate downstream\n * work and stay in the still-gating bucket so the operator sees\n * them. (task_show_blocked_by_renders_closed.) */\nfunction partitionEdges(edges: readonly TaskEdgeWithStatus[]): {\n stillGating: TaskEdgeWithStatus[];\n satisfied: TaskEdgeWithStatus[];\n} {\n const stillGating: TaskEdgeWithStatus[] = [];\n const satisfied: TaskEdgeWithStatus[] = [];\n for (const e of edges) {\n if (e.status === \"CLOSED\") satisfied.push(e);\n else stillGating.push(e);\n }\n return { stillGating, satisfied };\n}\n\n/** Render one edge bucket as a comma-separated `<name> [<STATUS>]`\n * list. The status is colour-coded the same way the task table\n * renders it (src/cli/format.ts colorStatus); satisfied buckets are\n * additionally dimmed so they recede visually. An empty bucket\n * renders as an em-dash (—) to match the prior \"no edges\" rendering\n * for back-compat with operator-eyed scripts. */\nfunction formatEdgeList(edges: readonly TaskEdgeWithStatus[], dim: boolean): string {\n if (edges.length === 0) return pc.dim(\"—\");\n const parts = edges.map((e) => {\n const piece = `${e.name} [${colorStatus(e.status)}]`;\n return dim ? pc.dim(piece) : piece;\n });\n return parts.join(\", \");\n}\n\nexport async function cmdTaskAdd(\n db: Db,\n localId: string | undefined,\n opts: {\n title: string;\n impact: number;\n effortDays: number;\n blockedBy?: string[];\n workstream?: string;\n json?: boolean;\n },\n): Promise<void> {\n const workstream = await resolveWorkstream(opts.workstream);\n // Derive the id from the title if the user didn't provide one. The\n // CLI's `<id>` positional is now optional; idFromTitleVerbose handles\n // collisions with `_2`, `_3`, … suffixes AND surfaces a `truncated`\n // flag so we can warn the user when the SLUG_SOFT_CAP word-boundary\n // cut dropped clauses (slugifytitle_silently_drops_clauses).\n let derivation: { id: string; truncated: boolean };\n if (localId !== undefined) {\n derivation = { id: localId, truncated: false };\n } else {\n derivation = idFromTitleVerbose(db, workstream, opts.title);\n }\n const id = derivation.id;\n const blockedBy = parseCsvFlag(opts.blockedBy);\n const task = addTask(db, {\n localId: id,\n workstream,\n title: opts.title,\n impact: opts.impact,\n effortDays: opts.effortDays,\n ...(blockedBy.length > 0 ? { blockedBy } : {}),\n });\n const nextSteps: NextStep[] = [\n { intent: \"Show this task\", command: `mu task show ${task.name} -w ${workstream}` },\n {\n // Single-quoted example: shell metachars (`...`, $VAR, $(...))\n // inside a double-quoted string expand in YOUR shell before mu\n // sees the note (mufeedback note #257). Single quotes defer\n // expansion to the agent.\n intent: \"Drop a note (single-quote to defer shell expansion)\",\n command: `mu task note ${task.name} '...' -w ${workstream}`,\n },\n {\n intent: \"Add a blocker\",\n command: `mu task block ${task.name} --by <other-id> -w ${workstream}`,\n },\n {\n intent: \"Claim and start\",\n command: `mu task claim ${task.name} -w ${workstream} --self (or --for <worker>)`,\n },\n ];\n if (opts.json) {\n // JSON callers are scripts: stay machine-readable. The hint is a\n // human-prose convenience and is suppressed under --json (matches\n // every other prose surface in the CLI).\n emitJson({ task: withRoiAll([task])[0], blockers: blockedBy ?? [], nextSteps });\n return;\n }\n // Stderr hint when auto-id derivation truncated the slug. Stderr +\n // exit 0 so scripts that already pipe stdout aren't disturbed; the\n // operator sees the heads-up that the id no longer carries the full\n // title's meaning, with a one-paste fix (pass <id> positional).\n // Suppressed when the operator passed an explicit id (truncated is\n // false in that branch) since they already chose the id by hand.\n if (derivation.truncated) {\n process.stderr.write(\n `hint: id '${task.name}' truncated from a longer slug; pass <id> positional to override (mu task add <id> --title ... ).\\n`,\n );\n }\n const idHint = localId === undefined ? pc.dim(\" (id derived from title)\") : \"\";\n console.log(\n `Added task ${pc.bold(task.name)}${idHint} ${pc.dim(\n `(workstream=${workstream}, impact=${task.impact}, effort=${task.effortDays})`,\n )}`,\n );\n if (blockedBy) console.log(pc.dim(` blocked by: ${blockedBy.join(\", \")}`));\n printNextSteps(nextSteps);\n}\n\nexport async function cmdTaskNote(\n db: Db,\n rawId: string,\n content: string,\n opts: { workstream?: string; json?: boolean; author?: string } = {},\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n // Author resolution: explicit --author wins; otherwise consult\n // MU_AGENT_NAME (env var injected at spawn) > pane title > $USER >\n // 'orchestrator'. Surfaced from mufeedback note #176: notes from\n // spawned agents were appearing as <orchestrator> because the CLI\n // wasn't propagating identity. After this fix, mu-spawned workers'\n // notes are correctly attributed to the agent name.\n const author = opts.author ?? (await resolveActorIdentity());\n const note = addNote(db, localId, unescapeNoteText(content), { author, workstream: ws });\n const nextSteps: NextStep[] = [\n { intent: \"Show all notes on this task\", command: `mu task notes ${localId} -w ${ws}` },\n { intent: \"Show full task state\", command: `mu task show ${localId} -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ taskName: localId, note, nextSteps });\n return;\n }\n console.log(pc.dim(`note appended to ${localId}`));\n printNextSteps(nextSteps);\n}\n\nexport async function cmdTaskShow(\n db: Db,\n rawId: string,\n opts: { json?: boolean; workstream?: string } = {},\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n // v5: tasks.local_id is per-workstream unique. Resolve the\n // operator's workstream up front so the lookup scopes correctly.\n const ws = await resolveWorkstream(opts.workstream);\n const task = getTask(db, localId, ws);\n if (!task) throw new TaskNotFoundError(localId);\n const edges = getTaskEdgesWithStatus(db, localId, task.workstreamName);\n const notes = listNotes(db, localId, task.workstreamName);\n\n // When owner IS NULL but the task is IN_PROGRESS (or recently was),\n // the actor is in agent_logs. Surface it so 'who's working on this'\n // is answerable from `mu task show` alone.\n const lastActor =\n task.ownerName === null && task.status !== \"OPEN\"\n ? lastClaimActor(db, task.workstreamName, task.name)\n : null;\n\n if (opts.json) {\n // JSON shape: blockers/dependents are arrays of {name, status}\n // objects so scripts can filter by status without a second query.\n // (task_show_blocked_by_renders_closed: prior shape was bare\n // string[], which discarded the gating-vs-satisfied distinction.)\n emitJson({\n task: withRoiAll([task])[0],\n blockers: edges.blockers,\n dependents: edges.dependents,\n notes,\n lastClaimActor: lastActor,\n });\n return;\n }\n\n const roi = task.effortDays > 0 ? (task.impact / task.effortDays).toFixed(1) : \"∞\";\n console.log(pc.bold(`${task.name} — ${task.title}`));\n console.log(` workstream : ${task.workstreamName}`);\n console.log(` status : ${task.status}`);\n // owner: registered worker name, or '(self: <actor>)' for an anonymous\n // claim, or '(unowned)' for OPEN tasks.\n const ownerLine =\n task.ownerName !== null\n ? task.ownerName\n : lastActor !== null\n ? pc.dim(`(self: ${lastActor})`)\n : pc.dim(\"(unowned)\");\n console.log(` owner : ${ownerLine}`);\n console.log(` impact : ${task.impact}`);\n console.log(` effort : ${task.effortDays} ${pc.dim(`(ROI ${roi})`)}`);\n console.log(` created : ${pc.dim(task.createdAt)}`);\n console.log(` updated : ${pc.dim(task.updatedAt)}`);\n\n console.log(\"\");\n console.log(pc.bold(\"Edges\"));\n // Group each side of the edge set by status so the operator can\n // tell at a glance which prerequisites still gate the task vs which\n // have already been satisfied. CLOSED is the only status that\n // satisfies a `blocks` edge; REJECTED/DEFERRED still gate downstream\n // work, so they stay in the still-gating bucket alongside\n // OPEN/IN_PROGRESS. (task_show_blocked_by_renders_closed.)\n const { stillGating: gatingBlockers, satisfied: satisfiedBlockers } = partitionEdges(\n edges.blockers,\n );\n const { stillGating: openDependents, satisfied: closedDependents } = partitionEdges(\n edges.dependents,\n );\n console.log(` blocked by : ${formatEdgeList(gatingBlockers, false)}`);\n if (satisfiedBlockers.length > 0) {\n console.log(` satisfied : ${formatEdgeList(satisfiedBlockers, true)}`);\n }\n console.log(` blocks : ${formatEdgeList(openDependents, false)}`);\n if (closedDependents.length > 0) {\n console.log(` no longer : ${formatEdgeList(closedDependents, true)}`);\n }\n\n console.log(\"\");\n console.log(pc.bold(`Notes (${notes.length})`));\n if (notes.length === 0) {\n console.log(pc.dim(\" (no notes)\"));\n } else {\n for (const n of notes) printNote(n);\n }\n}\n\nexport async function cmdTaskNotes(\n db: Db,\n rawId: string,\n opts: { json?: boolean; workstream?: string } = {},\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const task = getTask(db, localId, ws);\n if (!task) throw new TaskNotFoundError(localId);\n const notes = listNotes(db, localId, task.workstreamName);\n if (opts.json) {\n emitJsonCollection(notes);\n return;\n }\n if (notes.length === 0) {\n console.log(pc.dim(`(no notes on ${localId})`));\n return;\n }\n for (const n of notes) printNote(n);\n}\n\nexport async function cmdTaskUpdate(\n db: Db,\n rawId: string,\n opts: {\n title?: string;\n impact?: number;\n effortDays?: number;\n workstream?: string;\n json?: boolean;\n },\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const updateOpts: UpdateTaskOptions = {};\n if (opts.title !== undefined) updateOpts.title = opts.title;\n if (opts.impact !== undefined) updateOpts.impact = opts.impact;\n if (opts.effortDays !== undefined) updateOpts.effortDays = opts.effortDays;\n if (Object.keys(updateOpts).length === 0) {\n throw new UsageError(\n \"nothing to update; pass at least one of --title, --impact, --effort-days\",\n );\n }\n const r = updateTask(db, localId, updateOpts, { workstream: ws });\n const nextSteps: NextStep[] = [\n { intent: \"Show updated task\", command: `mu task show ${localId} -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ taskName: localId, ...r, nextSteps });\n return;\n }\n if (!r.updated) {\n console.log(pc.dim(`${localId}: no fields differ from current (no-op)`));\n return;\n }\n console.log(`Updated ${pc.bold(localId)} ${pc.dim(`(${r.changedFields.join(\", \")})`)}`);\n printNextSteps(nextSteps);\n}\n","// mu — `mu task` ownership + synchronisation verbs (claim / release / wait).\n//\n// claim → CAS-style ownership transfer; dispatch via --for or\n// anonymous --self (owner stays NULL, actor in agent_logs).\n// release → clears owner; auto-flips IN_PROGRESS → OPEN; --reopen\n// forces OPEN from CLOSED/REJECTED/DEFERRED.\n// wait → polls until the listed tasks reach --status (default\n// CLOSED). Exit 0 = met; exit 5 = timeout.\n//\n// Extracted from src/cli/tasks.ts as part of the wire-out follow-up\n// to refactor_split_large_src_files.\n\nimport { refreshAgentTitle } from \"../../agents.js\";\nimport { AgentNotFoundError } from \"../../agents/errors.js\";\nimport {\n UsageError,\n assertTaskInWorkstream,\n emitJson,\n parseQualifiedRef,\n parseStatusOption,\n resolveEntityRef,\n resolveWorkstream,\n} from \"../../cli.js\";\nimport { type Db, WorkstreamNotFoundError, tryResolveWorkstreamId } from \"../../db.js\";\nimport { type NextStep, pc, printNextSteps } from \"../../output.js\";\nimport { reconcile } from \"../../reconcile.js\";\nimport {\n ReaperDetectedDuringWaitError,\n TaskNotFoundError,\n type TaskWaitRef,\n type TaskWaitResult,\n type TaskWaitTaskState,\n claimTask,\n getTask,\n releaseTask,\n waitForTasks,\n} from \"../../tasks.js\";\n\nexport async function cmdTaskRelease(\n db: Db,\n rawId: string,\n opts: { reopen?: boolean; evidence?: string; workstream?: string; json?: boolean },\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const sdkOpts: { reopen: boolean; evidence?: string; workstream: string } = {\n reopen: opts.reopen ?? false,\n workstream: ws,\n };\n if (opts.evidence !== undefined) sdkOpts.evidence = opts.evidence;\n const r = releaseTask(db, localId, sdkOpts);\n // Title push for the agent that just lost the task. Prev-owner could\n // be null (anonymous claim release — nothing to refresh).\n if (r.previousOwnerName) await refreshAgentTitle(db, r.previousOwnerName, ws);\n const nextSteps: NextStep[] = [\n {\n intent: \"Reclaim\",\n command: `mu task claim ${localId} -w ${ws} (--self / --for <worker>)`,\n },\n { intent: \"Show current state\", command: `mu task show ${localId} -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ taskName: localId, ...r, nextSteps });\n return;\n }\n if (!r.changed) {\n console.log(pc.dim(`${localId} already unowned (no-op)`));\n printNextSteps(nextSteps);\n return;\n }\n const ownerBit = r.previousOwnerName ? `was ${pc.bold(r.previousOwnerName)}` : \"was unowned\";\n const statusBit = r.previousStatus !== r.status ? ` (${r.previousStatus} → ${r.status})` : \"\";\n console.log(`Released ${pc.bold(localId)} ${pc.dim(`(${ownerBit})${statusBit}`)}`);\n if (opts.evidence) console.log(pc.dim(` evidence: ${opts.evidence}`));\n printNextSteps(nextSteps);\n}\n\nexport async function cmdClaim(\n db: Db,\n rawId: string,\n opts: {\n for?: string;\n self?: boolean;\n actor?: string;\n evidence?: string;\n workstream?: string;\n json?: boolean;\n },\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n if (opts.self === true && opts.for !== undefined) {\n throw new UsageError(\"--self and --for are mutually exclusive\");\n }\n if (opts.actor !== undefined && opts.self !== true) {\n throw new UsageError(\"--actor only meaningful with --self (it overrides the actor name)\");\n }\n // Parse `--for` for an optional qualified ref: bare `<name>`\n // resolves the agent in the task's workstream (today); qualified\n // `<workstream>/<name>` resolves the agent in its own workstream\n // and dispatches across the workstream boundary\n // (task_claim_for_cross_workstream).\n let forName: string | undefined;\n let forWorkstream: string | undefined;\n if (opts.for !== undefined) {\n const parsed = parseQualifiedRef(opts.for);\n forName = parsed.name;\n if (parsed.workstream !== undefined) {\n forWorkstream = parsed.workstream;\n // Pre-flight: workstream must exist (typed error so the operator\n // sees the canonical exit-3 mapping instead of a bare\n // ClaimerNotRegisteredError pointing at the wrong cause).\n if (tryResolveWorkstreamId(db, forWorkstream) === null) {\n throw new WorkstreamNotFoundError(forWorkstream);\n }\n // Pre-flight: agent must exist in that workstream. Without this\n // the SDK surfaces ClaimerNotRegisteredError (the bare-name\n // shape) which doesn't carry the qualifying workstream context\n // — AgentNotFoundError(name, workstream) is the right shape for\n // the cross-ws path.\n const wsId = tryResolveWorkstreamId(db, forWorkstream);\n if (wsId !== null) {\n const row = db\n .prepare(\"SELECT 1 FROM agents WHERE name = ? AND workstream_id = ?\")\n .get(forName, wsId);\n if (!row) throw new AgentNotFoundError(forName, forWorkstream);\n }\n }\n }\n const sdkOpts: {\n agentName?: string;\n agentWorkstream?: string;\n self?: boolean;\n actor?: string;\n evidence?: string;\n workstream: string;\n } = { workstream: ws };\n if (forName !== undefined) sdkOpts.agentName = forName;\n if (forWorkstream !== undefined) sdkOpts.agentWorkstream = forWorkstream;\n if (opts.self) sdkOpts.self = true;\n if (opts.actor !== undefined) sdkOpts.actor = opts.actor;\n if (opts.evidence !== undefined) sdkOpts.evidence = opts.evidence;\n const result = await claimTask(db, localId, sdkOpts);\n // Title push for the new owner. Anonymous claims (--self) leave\n // owner=null — nothing to refresh. Refresh in the agent's OWN\n // workstream (forWorkstream when the dispatch was cross-ws), not\n // the task's — the agent row only exists in its own workstream.\n if (result.ownerName) {\n await refreshAgentTitle(db, result.ownerName, forWorkstream ?? ws);\n }\n const nextSteps: NextStep[] = [\n {\n // Single-quoted example: shell metachars (`...`, $VAR, $(...))\n // inside a double-quoted string expand in YOUR shell before mu\n // sees the note (mufeedback note #257). Single quotes defer\n // expansion to the agent.\n intent: \"Drop a note (single-quote to defer shell expansion)\",\n command: `mu task note ${localId} 'FILES: ...\\\\nDECISION: ...' -w ${ws}`,\n },\n {\n intent: \"Close with grounding\",\n command: `mu task close ${localId} --evidence \"...\" -w ${ws}`,\n },\n { intent: \"Release if blocked\", command: `mu task release ${localId} -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ ...result, nextSteps });\n return;\n }\n if (result.ownerName === null) {\n console.log(\n `Claimed ${pc.bold(localId)} ${pc.dim(`(--self by ${result.actorName}; ${result.previousStatus} → ${result.status}; owner=NULL)`)}`,\n );\n } else {\n console.log(\n `Claimed ${pc.bold(localId)} for ${pc.bold(result.ownerName)} ${pc.dim(`(${result.previousStatus} → ${result.status})`)}`,\n );\n }\n if (opts.evidence) console.log(pc.dim(` evidence: ${opts.evidence}`));\n printNextSteps(nextSteps);\n}\n\n/** Qualified id `<ws>/<name>` for a watched ref — used in messages,\n * --json output, and stuck-task hints. */\nfunction qualifiedId(ref: { workstreamName: string; name: string }): string {\n return `${ref.workstreamName}/${ref.name}`;\n}\n\n/** Resolve a single `<ws>/<name>` or bare id into a TaskWaitRef.\n * - Qualified refs use their prefix; -w is NOT consulted (so a\n * cross-workstream wait can name two different workstreams).\n * - Bare refs fall back to the standard chain via resolveWorkstream.\n * - When neither qualifier nor -w/MU_SESSION/tmux session resolves a\n * workstream for a bare ref, surface the canonical UsageError from\n * resolveWorkstream so the operator gets the same diagnostic as\n * every other task verb.\n * - Existence is asserted up-front: a typo in either half throws\n * TaskNotFoundError listing the qualified form. (waitForTasks\n * repeats this check; we duplicate here so the error names the\n * raw arg the operator typed, not the normalised internal form.)\n */\nasync function resolveWaitRef(\n db: Db,\n raw: string,\n fallbackWs: string | undefined,\n): Promise<TaskWaitRef> {\n const parsed = parseQualifiedRef(raw);\n let workstreamName: string;\n if (parsed.workstream !== undefined) {\n workstreamName = parsed.workstream;\n } else {\n // Bare ref — use the standard chain (--workstream / $MU_SESSION /\n // tmux session). resolveWorkstream throws UsageError if none of\n // those resolve, which is the same error operators see today on\n // every other task verb.\n workstreamName = await resolveWorkstream(fallbackWs);\n }\n if (getTask(db, parsed.name, workstreamName) === undefined) {\n // Use the raw form (qualified or bare) so the error message\n // matches what the operator typed.\n throw new TaskNotFoundError(parsed.workstream !== undefined ? raw : parsed.name);\n }\n return { workstreamName, name: parsed.name };\n}\n\nexport async function cmdTaskWait(\n db: Db,\n ids: readonly string[],\n opts: {\n status?: string;\n any?: boolean;\n /** --first is a CLI alias for --any with a richer return shape\n * (prints the firing ref's qualified id; --json gains a\n * `firing` field). task_wait_cross_workstream. */\n first?: boolean;\n timeout?: number;\n stuckAfter?: number;\n onStall?: \"warn\" | \"exit\";\n workstream?: string;\n json?: boolean;\n },\n): Promise<void> {\n if (ids.length === 0) {\n throw new UsageError(\"mu task wait: at least one task id is required\");\n }\n // task_wait_stall_action_flag: validate --on-stall up-front so a\n // typo errors loud at the verb boundary instead of being silently\n // ignored by the SDK. Default 'warn' (today's behaviour).\n const onStallRaw = opts.onStall ?? \"warn\";\n if (onStallRaw !== \"warn\" && onStallRaw !== \"exit\") {\n throw new UsageError(`--on-stall: expected 'warn' or 'exit', got '${onStallRaw}'`);\n }\n // Validate status (default CLOSED). Same parser as mu task list --status.\n const statusOpt = opts.status !== undefined ? parseStatusOption(opts.status) : undefined;\n\n // --first is a CLI alias for --any (same exit-condition; differs\n // only in output shape: --first emphasises WHICH ref fired so the\n // operator can pipe the qualified id into the next step).\n const wantAny = opts.any === true || opts.first === true;\n // emphasise WHICH ref fired in stdout / --json (today's --any kept\n // the per-task list as-is).\n const wantFirstShape = opts.first === true || opts.any === true;\n\n // Resolve every ref — cross-workstream-aware. Each ref carries its\n // own workstream so the wait set can span multiple workstreams.\n // Pre-flight existence check is inside resolveWaitRef; failures\n // throw before any waiting begins.\n const refs: TaskWaitRef[] = [];\n for (const id of ids) {\n refs.push(await resolveWaitRef(db, id, opts.workstream));\n }\n // Workstream set: every workstream we'll reconcile per poll. May be\n // 1 (legacy single-ws wait) or N (cross-ws). Used both for the\n // reconcile-each-poll path and for the human output below.\n const workstreamSet = new Set(refs.map((r) => r.workstreamName));\n\n // --timeout in seconds for shell ergonomics; SDK takes ms.\n // 0 in the SDK = wait forever; same convention here.\n const timeoutMs = opts.timeout !== undefined ? opts.timeout * 1000 : 600_000;\n // --stuck-after also in seconds; 0 disables. Default mirrors the SDK.\n const stuckAfterMs = opts.stuckAfter !== undefined ? opts.stuckAfter * 1000 : 300_000;\n\n const sdkOpts: {\n status?: TaskWaitResult[\"tasks\"][number][\"status\"];\n any?: boolean;\n timeoutMs: number;\n stuckAfterMs: number;\n onStall?: \"warn\" | \"exit\";\n beforePoll?: () => Promise<void>;\n } = { timeoutMs, stuckAfterMs };\n if (statusOpt !== undefined) sdkOpts.status = statusOpt;\n if (wantAny) sdkOpts.any = true;\n\n // task_wait_reconcile_dead_panes (extended for cross-workstream by\n // task_wait_cross_workstream): per-poll reconcile + reaper-flip\n // detection. We keep a snapshot of each watched task's prior\n // (status, owner) so that AFTER reconcile (which may have flipped\n // IN_PROGRESS → OPEN for a dead-pane worker) we can spot the\n // transition and abort with exit 6 instead of running out the\n // operator's --timeout.\n //\n // - Reconcile runs on EVERY poll regardless of target, and once\n // per workstream in the wait set (NOT just the resolved -w —\n // cross-ws waits span multiple workstreams). The reaper is\n // exactly what we want to fire, and it only does so during a\n // `\"full\"` reconcile (the prune deletes the agent row which\n // triggers the IN_PROGRESS → OPEN flip).\n // - Exit-6 suppression: only when the wait target is CLOSED.\n // Other targets treat reaper-flip as a legitimate state change\n // (target=OPEN: it's the success; target=IN_PROGRESS: an\n // operator polling for the next worker to claim doesn't want\n // a dead predecessor to abort their wait).\n // - Cross-ws scoping: a reaper-flip in workstream B while we're\n // waiting on A's task does NOT trigger exit 6 — the\n // priorState/check loop runs ONLY over the watched refs. A\n // reconcile of B is harmless to A's wait.\n // - First-iteration coverage: beforePoll runs BEFORE the initial\n // snapshot too (waitForTasks contract), so a worker that died\n // BEFORE the operator typed `mu task wait` still triggers exit\n // 6 on the first tick.\n const target = statusOpt ?? \"CLOSED\";\n const reaperExitEnabled = target === \"CLOSED\";\n // task_wait_stall_action_flag: same target=CLOSED carve-out as\n // exit-6's reaper-flip suppression. With --status OPEN/IN_PROGRESS\n // the worker reaching needs_input might BE the success path —\n // exiting on stall would race the wait-condition check. Operators\n // who pass --on-stall exit + --status OPEN get warn-only behaviour\n // (the SDK still emits the stderr warning + agent_logs event).\n if (onStallRaw === \"exit\" && target === \"CLOSED\") sdkOpts.onStall = \"exit\";\n const priorState = new Map<string, { status: string; owner: string | null }>();\n sdkOpts.beforePoll = async () => {\n // Reconcile each unique workstream in the wait set. Each call is\n // a cheap (~few ms) tmux list-panes + per-survivor capture-pane.\n // \"status-only\" mode would skip the prune — but the prune is\n // exactly what fires the reaper that flips the task. Use\n // \"full\" mode so dead panes get cleaned up AND the reaper runs.\n for (const wsName of workstreamSet) {\n try {\n await reconcile(db, { workstream: wsName, mode: \"full\" });\n } catch {\n // Tmux substrate hiccup mid-wait: don't crash the wait. The\n // next iteration retries; the exit-6 path stays correct\n // because the prior-state map only fires on a real flip we\n // observed.\n }\n }\n if (!reaperExitEnabled) return;\n for (const ref of refs) {\n const key = qualifiedId(ref);\n const row = getTask(db, ref.name, ref.workstreamName);\n const status = row?.status ?? \"OPEN\";\n const owner = row?.ownerName ?? null;\n const prior = priorState.get(key);\n if (prior !== undefined && prior.status === \"IN_PROGRESS\" && status === \"OPEN\") {\n // Reaper-flip detected on a watched task. The prior owner\n // is the agent whose pane just got pruned (the FK\n // CASCADE-SET-NULL on agents.id has already cleared the\n // current row's owner; we use the snapshot from the\n // previous tick).\n throw new ReaperDetectedDuringWaitError(ref.name, prior.owner, ref.workstreamName);\n }\n priorState.set(key, { status, owner });\n }\n };\n\n const result = await waitForTasks(db, refs, sdkOpts);\n\n // ─── WHICH-result shaping ────────────────────────────────────────\n // \"firing\" = the first ref that reached the target on the closing\n // snapshot, for --first / --any. NULL on --all (every ref reached;\n // no \"first\" to single out) and on timeout (nothing reached).\n // The order matches the input refs order; tie-breaks by argv.\n const firingRef: TaskWaitTaskState | null =\n wantFirstShape && !result.timedOut ? (result.tasks.find((t) => t.reachedTarget) ?? null) : null;\n const reachedRefs = result.tasks.filter((t) => t.reachedTarget);\n const unmetRefs = result.tasks.filter((t) => !t.reachedTarget);\n\n // Build nextSteps. The structure differs by exit shape:\n // - --first / --any success: name the firing ref, suggest\n // cherry-pick + verify + free + recreate (the dispatch-pipeline\n // recipe). The cherry-pick command uses the firing ref's owner\n // as the worker name when known.\n // - --all success: list closed refs, suggest verify.\n // - timeout / partial: list unmet refs, suggest mu task show.\n const nextSteps: NextStep[] = [];\n if (!result.timedOut && firingRef !== null) {\n const owner = firingRef.owner;\n if (owner !== null) {\n // Workspace path lookup is per-owner; the cherry-pick command\n // captures HEAD from the worker's workspace and applies it to\n // the orchestrator's repo. `mu workspace path` prints just the\n // path on stdout (no JSON needed), so we wrap it in $(cd $(...)\n // && git log -1) for the HEAD lookup. Operator copy-pastes;\n // the deferred shell expansion is handled by the operator's\n // shell, not ours.\n nextSteps.push({\n intent: `Cherry-pick ${owner}'s HEAD onto your branch`,\n command: `git cherry-pick $(cd $(mu workspace path ${owner} -w ${firingRef.workstreamName}) && git log -1 --format=%H)`,\n });\n }\n nextSteps.push({\n intent: \"Verify the cherry-pick\",\n command: \"npm run typecheck && npm run lint && npm run test && npm run build\",\n });\n if (owner !== null) {\n nextSteps.push({\n intent: `Free + recreate ${owner}'s workspace for the next dispatch`,\n command: `mu workspace free ${owner} -w ${firingRef.workstreamName} && mu workspace create ${owner} -w ${firingRef.workstreamName}`,\n });\n }\n } else if (!result.timedOut && wantFirstShape === false) {\n // --all success path — every ref reached. No single \"firing\"\n // worker to cherry-pick; the operator presumably already\n // picked along the way (or runs a single verify here).\n nextSteps.push({\n intent: \"Verify the merged work\",\n command: \"npm run typecheck && npm run lint && npm run test && npm run build\",\n });\n }\n for (const t of unmetRefs) {\n nextSteps.push({\n intent: `Investigate ${qualifiedId(t)} (status=${t.status})`,\n command: `mu task show ${t.name} -w ${t.workstreamName}`,\n });\n }\n if (!result.timedOut) {\n // Surface the next-ready hint for each workstream we waited on —\n // a cross-ws operator wants both `mu task next` candidates.\n for (const wsName of workstreamSet) {\n nextSteps.push({\n intent: `Pick the next ready task in ${wsName}`,\n command: `mu task next -w ${wsName}`,\n });\n }\n }\n\n if (opts.json) {\n // JSON shape (task_wait_cross_workstream):\n // firing — the first ref to reach target on --first/--any,\n // else null. { workstreamName, name, status }.\n // all — per-ref state for refs that reached target.\n // timedOut — per-ref state for refs that did NOT reach target.\n // On the success path this is [].\n // The legacy `tasks`/`allReached`/`anyReached`/`elapsedMs`/\n // `timedOut` fields stay (back-compat for any caller pinned to\n // the prior shape).\n const firingJson =\n firingRef === null\n ? null\n : {\n workstreamName: firingRef.workstreamName,\n name: firingRef.name,\n qualifiedId: qualifiedId(firingRef),\n status: firingRef.status,\n owner: firingRef.owner,\n };\n // The JSON shape for the cross-ws + WHICH design is:\n // firing — the firing ref on --first/--any success, else null\n // all — refs that REACHED target (on success: every ref for\n // --all; the firing ref for --first/--any. On partial\n // timeout: only the ones that made it.)\n // timedOut — refs that did NOT reach target. ALWAYS [] on a\n // clean exit (every --all ref reached, OR --any saw\n // one). Populated on actual timeout only.\n //\n // The legacy boolean `timedOut` from the SDK spread is intentionally\n // overwritten by an array; downstream callers branch on\n // `firing === null && timedOut.length > 0` for partial-progress.\n const timedOutArray = result.timedOut\n ? unmetRefs.map((t) => ({ ...t, qualifiedId: qualifiedId(t) }))\n : [];\n emitJson({\n ...result,\n firing: firingJson,\n all: reachedRefs.map((t) => ({\n ...t,\n qualifiedId: qualifiedId(t),\n reachedAt: new Date().toISOString(),\n })),\n timedOut: timedOutArray,\n nextSteps,\n });\n if (result.timedOut) process.exit(5);\n return;\n }\n\n // Human output:\n // --first / --any success: print qualified id of firing ref on\n // stdout (so `... | head -1` and `read REF < <(mu task wait\n // ...)` both work), then a dim summary + per-task lines.\n // --all success / timeout: today's summary line + per-task list.\n const targetStatus = statusOpt ?? \"CLOSED\";\n if (firingRef !== null) {\n console.log(qualifiedId(firingRef));\n }\n const summary = result.timedOut\n ? pc.yellow(`Timed out after ${result.elapsedMs}ms`)\n : pc.green(\n `${wantAny ? \"any-of\" : \"all-of\"} ${refs.length} reached ${targetStatus} in ${result.elapsedMs}ms`,\n );\n console.log(summary);\n for (const t of result.tasks) {\n const marker = t.reachedTarget ? pc.green(\"✓\") : pc.dim(\"•\");\n // Cross-ws: show the qualified id so a mixed list is unambiguous.\n // Single-ws (workstreamSet.size === 1) keeps today's bare-name\n // output to avoid noise.\n const label = workstreamSet.size > 1 ? qualifiedId(t) : t.name;\n console.log(` ${marker} ${pc.bold(label)} ${pc.dim(`(${t.status})`)}`);\n }\n printNextSteps(nextSteps);\n if (result.timedOut) process.exit(5);\n}\n","// mu — `mu task` graph-edge verbs (block / unblock / reparent / delete).\n//\n// All four mutate task_edges (block/unblock/reparent) or cascade through\n// it via FK (delete). Each is idempotent and emits typed errors that\n// `handle()` in src/cli.ts maps to exit codes.\n//\n// Extracted from src/cli/tasks.ts as part of the wire-out follow-up\n// to refactor_split_large_src_files.\n\nimport {\n assertTaskInWorkstream,\n emitJson,\n parseCsvFlag,\n resolveEntityRef,\n resolveWorkstream,\n} from \"../../cli.js\";\nimport type { Db } from \"../../db.js\";\nimport { type NextStep, pc, printNextSteps } from \"../../output.js\";\nimport { addBlockEdge, deleteTask, removeBlockEdge, reparentTask } from \"../../tasks.js\";\n\nexport async function cmdTaskBlock(\n db: Db,\n rawBlocked: string,\n opts: { by: string; workstream?: string; json?: boolean },\n): Promise<void> {\n const { name: blocked } = await resolveEntityRef(db, rawBlocked, opts, \"task\");\n assertTaskInWorkstream(db, blocked, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const r = addBlockEdge(db, ws, blocked, opts.by);\n const nextSteps: NextStep[] = [\n { intent: \"Show the dependency tree\", command: `mu task tree ${blocked} -w ${ws}` },\n { intent: \"Remove this edge\", command: `mu task unblock ${blocked} --by ${opts.by} -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ blockedName: blocked, blockerName: opts.by, ...r, nextSteps });\n return;\n }\n if (!r.added) {\n console.log(pc.dim(`${opts.by} → ${blocked}: edge already exists (no-op)`));\n printNextSteps(nextSteps);\n return;\n }\n console.log(`Added edge ${pc.bold(opts.by)} → ${pc.bold(blocked)}`);\n printNextSteps(nextSteps);\n}\n\nexport async function cmdTaskUnblock(\n db: Db,\n rawBlocked: string,\n opts: { by: string; workstream?: string; json?: boolean },\n): Promise<void> {\n const { name: blocked } = await resolveEntityRef(db, rawBlocked, opts, \"task\");\n assertTaskInWorkstream(db, blocked, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const r = removeBlockEdge(db, ws, blocked, opts.by);\n const nextSteps: NextStep[] = [\n { intent: \"Show what now blocks this task\", command: `mu task tree ${blocked} -w ${ws}` },\n { intent: \"Re-add the edge\", command: `mu task block ${blocked} --by ${opts.by} -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ blockedName: blocked, blockerName: opts.by, ...r, nextSteps });\n return;\n }\n if (!r.removed) {\n console.log(pc.dim(`${opts.by} → ${blocked}: no such edge (no-op)`));\n printNextSteps(nextSteps);\n return;\n }\n console.log(`Removed edge ${pc.bold(opts.by)} → ${pc.bold(blocked)}`);\n printNextSteps(nextSteps);\n}\n\nexport async function cmdTaskReparent(\n db: Db,\n rawId: string,\n opts: { blockedBy: string[]; workstream?: string; json?: boolean },\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const blockers = parseCsvFlag(opts.blockedBy);\n const r = reparentTask(db, localId, blockers, { workstream: ws });\n const nextSteps: NextStep[] = [\n { intent: \"Show the new dependency tree\", command: `mu task tree ${localId} -w ${ws}` },\n { intent: \"Show the task\", command: `mu task show ${localId} -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ taskName: localId, blockerNames: blockers, ...r, nextSteps });\n return;\n }\n console.log(\n `Reparented ${pc.bold(localId)} ${pc.dim(`(removed ${r.removedEdges} edges, added ${r.addedEdges})`)}`,\n );\n printNextSteps(nextSteps);\n}\n\nexport async function cmdTaskDelete(\n db: Db,\n rawId: string,\n opts: { workstream?: string; json?: boolean; yes?: boolean } = {},\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n // Two-phase: bare = dry-run preview; --yes commits. Mirrors\n // `mu workstream destroy` / `mu archive delete` / `mu snapshot\n // prune`. Surfaced by feedback ws task fb_task_delete_no_yes\n // (impact=30): the dogfood report typed `mu task delete X --yes`\n // (mirroring workstream destroy) and got 'unknown option --yes'\n // because the verb took no confirmation flag at all; two failed\n // deletes left long-named tasks lingering until noticed.\n const dryRun = opts.yes !== true;\n const r = deleteTask(db, localId, ws, { dryRun });\n const commitNextSteps: NextStep[] = [\n {\n intent: \"Undo (a snapshot was taken before the delete)\",\n command: \"mu undo --yes\",\n },\n {\n intent: \"List remaining tasks\",\n command: `mu task list -w ${ws}`,\n },\n ];\n const dryRunNextSteps: NextStep[] = [\n {\n intent: \"Confirm and actually delete (cascades to edges + notes)\",\n command: `mu task delete ${localId} -w ${ws} --yes`,\n },\n {\n intent: \"After deleting, undo if you regret it (DB only)\",\n command: \"mu undo --yes\",\n },\n {\n intent: \"Inspect the task + edges before deciding\",\n command: `mu task show ${localId} -w ${ws}`,\n },\n ];\n\n // Missing row — idempotent no-op (same outcome whether dry-run or\n // --yes). The `present: false` discriminator keeps this distinct\n // from a dry-run that found an existing task with no edges/notes.\n if (!r.present) {\n if (opts.json) {\n emitJson({ taskName: localId, ...r, nextSteps: commitNextSteps });\n return;\n }\n console.log(pc.dim(`no task named ${localId} (already deleted?)`));\n return;\n }\n\n // Dry-run: print the cascade preview. The task DOES exist (present\n // checked above); zero edges + zero notes is a real cascade-of-one.\n if (r.dryRun) {\n if (opts.json) {\n emitJson({ taskName: localId, ...r, nextSteps: dryRunNextSteps });\n return;\n }\n console.log(\n r.deletedEdges === 0 && r.deletedNotes === 0\n ? `Would delete ${pc.bold(localId)} ${pc.dim(\"(no edges, no notes)\")}`\n : `Would delete ${pc.bold(localId)} ${pc.dim(`(edges: ${r.deletedEdges}, notes: ${r.deletedNotes})`)}`,\n );\n console.log(\"\");\n console.log(pc.dim(\"(dry-run; rerun with --yes to actually delete)\"));\n console.log(\n pc.dim(\"A snapshot will be taken before the delete; `mu undo --yes` reverts it (DB only).\"),\n );\n printNextSteps(dryRunNextSteps);\n return;\n }\n\n // Commit path.\n if (opts.json) {\n emitJson({ taskName: localId, ...r, nextSteps: commitNextSteps });\n return;\n }\n console.log(\n `Deleted ${pc.bold(localId)} ${pc.dim(`(edges: ${r.deletedEdges}, notes: ${r.deletedNotes})`)}`,\n );\n printNextSteps(commitNextSteps);\n}\n","// mu — `mu task` lifecycle verbs (status transitions).\n//\n// close / open / reject / defer + the cascade preview helper. Each\n// snapshots the DB before mutating (via the SDK closeTask / openTask\n// / etc.); --cascade is dry-run by default and requires --yes to\n// commit (the deliberate friction; surfaced live by\n// bug_cascade_reject_too_aggressive).\n//\n// Extracted from src/cli/tasks.ts as part of refactor_split_large_src_files.\n\nimport { refreshAgentTitle } from \"../../agents.js\";\nimport {\n UsageError,\n assertTaskInWorkstream,\n emitJson,\n resolveEntityRef,\n resolveWorkstream,\n} from \"../../cli.js\";\nimport type { Db } from \"../../db.js\";\nimport { type NextStep, pc, printNextSteps } from \"../../output.js\";\nimport { closeTask, deferTask, getTask, openTask, rejectTask } from \"../../tasks.js\";\n\nexport async function cmdTaskClose(\n db: Db,\n rawId: string,\n opts: { evidence?: string; ifReady?: boolean; workstream?: string; json?: boolean } = {},\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const sdkOpts: { evidence?: string; ifReady?: boolean; workstream: string } = { workstream: ws };\n if (opts.evidence !== undefined) sdkOpts.evidence = opts.evidence;\n if (opts.ifReady) sdkOpts.ifReady = true;\n // Capture the owner BEFORE closeTask so we can refresh their title\n // even though closeTask doesn't return owner info. owner won't\n // change as a result of close (FK SET NULL only fires on delete).\n const taskRow = getTask(db, localId, ws);\n const r = closeTask(db, localId, sdkOpts);\n // --if-ready can return a CloseSkippedResult (no mutation). Branch\n // first so the typed `skipped` field stays in scope below.\n if (\"skipped\" in r) {\n const blockingNextSteps: NextStep[] = [\n {\n intent: \"Watch the remaining blockers (returns when one closes)\",\n command: `mu task wait ${r.blockingIds.join(\" \")} -w ${ws} --first --any`,\n },\n { intent: \"Show the umbrella + blockers\", command: `mu task show ${localId} -w ${ws}` },\n {\n intent: \"Close anyway (override --if-ready)\",\n command: `mu task close ${localId} -w ${ws}`,\n },\n ];\n if (opts.json) {\n emitJson({ taskName: localId, ...r, nextSteps: blockingNextSteps });\n return;\n }\n const total = r.blockingIds.length;\n const shown = r.blockingIds.slice(0, 8).join(\", \");\n const tail = total > 8 ? \", \\u2026\" : \"\";\n console.log(\n pc.dim(\n `Skipped ${pc.bold(localId)}: blocked by ${total} task(s) (${shown}${tail}); rerun without --if-ready to close anyway`,\n ),\n );\n printNextSteps(blockingNextSteps);\n return;\n }\n if (r.changed && taskRow?.ownerName) await refreshAgentTitle(db, taskRow.ownerName, ws);\n const nextSteps: NextStep[] = [\n { intent: \"Reopen if needed\", command: `mu task open ${localId} -w ${ws}` },\n { intent: \"Pick the next ready task\", command: `mu task next -w ${ws}` },\n { intent: \"See full state\", command: `mu state -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ taskName: localId, ...r, nextSteps });\n return;\n }\n if (!r.changed) {\n console.log(pc.dim(`${localId} already CLOSED (no-op)`));\n printNextSteps(nextSteps);\n return;\n }\n const ev = opts.evidence ? pc.dim(` evidence: ${opts.evidence}`) : \"\";\n console.log(`Closed ${pc.bold(localId)} ${pc.dim(`(${r.previousStatus} → ${r.status})`)}`);\n if (ev) console.log(ev);\n printNextSteps(nextSteps);\n}\n\nexport async function cmdTaskOpen(\n db: Db,\n rawId: string,\n opts: { evidence?: string; workstream?: string; json?: boolean } = {},\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const sdkOpts: { evidence?: string; workstream: string } = { workstream: ws };\n if (opts.evidence !== undefined) sdkOpts.evidence = opts.evidence;\n const r = openTask(db, localId, sdkOpts);\n const nextSteps: NextStep[] = [\n {\n intent: \"Claim it\",\n command: `mu task claim ${localId} -w ${ws} (--self / --for <worker>)`,\n },\n { intent: \"Close again\", command: `mu task close ${localId} -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ taskName: localId, ...r, nextSteps });\n return;\n }\n if (!r.changed) {\n console.log(pc.dim(`${localId} already OPEN (no-op)`));\n printNextSteps(nextSteps);\n return;\n }\n const ev = opts.evidence ? pc.dim(` evidence: ${opts.evidence}`) : \"\";\n console.log(`Reopened ${pc.bold(localId)} ${pc.dim(`(${r.previousStatus} → ${r.status})`)}`);\n if (ev) console.log(ev);\n printNextSteps(nextSteps);\n}\n\n// ─── reject / defer (terminal-but-blocking transitions) ────────────────\n\ninterface RejectDeferOpts {\n evidence?: string;\n cascade?: boolean;\n yes?: boolean;\n workstream?: string;\n json?: boolean;\n}\n\nexport async function cmdTaskReject(\n db: Db,\n localId: string,\n opts: RejectDeferOpts = {},\n): Promise<void> {\n return cmdTaskRejectOrDefer(db, localId, \"reject\", opts);\n}\n\nexport async function cmdTaskDefer(\n db: Db,\n localId: string,\n opts: RejectDeferOpts = {},\n): Promise<void> {\n return cmdTaskRejectOrDefer(db, localId, \"defer\", opts);\n}\n\nexport async function cmdTaskRejectOrDefer(\n db: Db,\n rawId: string,\n verb: \"reject\" | \"defer\",\n opts: RejectDeferOpts,\n): Promise<void> {\n const { name: localId } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, localId, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n if (opts.yes && !opts.cascade) {\n throw new UsageError(\n `--yes requires --cascade (--yes only meaningful when committing a cascade preview; for single-task ${verb}, --yes is a no-op)`,\n );\n }\n const sdkOpts: { evidence?: string; cascade?: boolean; yes?: boolean; workstream: string } = {\n workstream: ws,\n };\n if (opts.evidence !== undefined) sdkOpts.evidence = opts.evidence;\n if (opts.cascade) sdkOpts.cascade = true;\n if (opts.yes) sdkOpts.yes = true;\n const r = verb === \"reject\" ? rejectTask(db, localId, sdkOpts) : deferTask(db, localId, sdkOpts);\n // Title push for every affected task's owner (the verb compresses\n // potentially-multi-task work; refresh each owner once). Skipped\n // on dry-run since nothing changed.\n if (r.changed) {\n const owners = new Set<string>();\n for (const id of r.changedIds) {\n const t = getTask(db, id, ws);\n if (t?.ownerName) owners.add(t.ownerName);\n }\n for (const owner of owners) await refreshAgentTitle(db, owner, ws);\n }\n const past = verb === \"reject\" ? \"Rejected\" : \"Deferred\";\n const status = verb === \"reject\" ? \"REJECTED\" : \"DEFERRED\";\n\n // Cascade dry-run: render the affected list with each task's\n // current status + title so the operator can spot 'wait, that\n // dependent has independent merit, I want to keep it'. Surfaced\n // in mufeedback bug_cascade_reject_too_aggressive.\n if (r.dryRun) {\n if (opts.json) {\n emitJson({\n taskName: localId,\n ...r,\n nextSteps: [\n {\n intent: \"Commit the cascade after reviewing the list\",\n command: `mu task ${verb} ${localId} --cascade --yes -w ${ws}`,\n },\n {\n intent: \"Address one dependent first, then re-preview\",\n command: `mu task ${verb} <dep> -w ${ws}`,\n },\n ],\n });\n return;\n }\n console.log(\n `${past === \"Rejected\" ? \"Reject\" : \"Defer\"} ${pc.bold(localId)} would sweep ${r.affectedIds.length} task(s) (root + ${r.affectedIds.length - 1} dependent(s)):`,\n );\n for (const id of r.affectedIds) {\n const t = getTask(db, id, ws);\n const title = t ? (t.title.length > 50 ? `${t.title.slice(0, 49)}…` : t.title) : \"?\";\n const marker = id === localId ? pc.bold(\" *\") : \" \";\n console.log(`${marker} ${pc.bold(id)} ${pc.dim(title)}`);\n }\n console.log(\"\");\n console.log(pc.dim(\"(dry-run; rerun with --yes to actually sweep)\"));\n printNextSteps([\n {\n intent: \"Commit the cascade after reviewing the list\",\n command: `mu task ${verb} ${localId} --cascade --yes -w ${ws}`,\n },\n {\n intent: \"Address one dependent first, then re-preview\",\n command: `mu task ${verb} <dep> -w ${ws}`,\n },\n ]);\n return;\n }\n\n const nextSteps: NextStep[] = [\n { intent: \"Reopen if reconsidered\", command: `mu task open ${localId} -w ${ws}` },\n { intent: \"See full state\", command: `mu state -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ taskName: localId, ...r, nextSteps });\n return;\n }\n if (!r.changed) {\n console.log(pc.dim(`${localId} already ${status} (no-op)`));\n printNextSteps(nextSteps);\n return;\n }\n console.log(`${past} ${pc.bold(localId)} ${pc.dim(`(→ ${status})`)}`);\n if (opts.evidence) console.log(pc.dim(` evidence: ${opts.evidence}`));\n if (r.changedIds.length > 1) {\n const cascaded = r.changedIds.slice(1);\n console.log(\n pc.dim(\n ` cascaded to ${cascaded.length} dependent(s): ${cascaded.slice(0, 8).join(\", \")}${cascaded.length > 8 ? \", …\" : \"\"}`,\n ),\n );\n }\n printNextSteps(nextSteps);\n}\n","// mu — `mu task tree` verb (ASCII / JSON dependency rendering).\n//\n// Renders the blocker subtree (default) or dependent subtree (--down)\n// of a task. Diamonds — a node reachable by two parent paths — collapse\n// to one full subtree render plus a one-line \"↻ already shown\" marker\n// at every later occurrence. Schema forbids cycles, so the seen-set\n// only fires on diamonds in practice; double-edged as defence against\n// future bugs.\n//\n// Extracted from src/cli/tasks.ts as part of the wire-out follow-up\n// to refactor_split_large_src_files.\n\nimport { assertTaskInWorkstream, colorStatus, emitJson, resolveEntityRef } from \"../../cli.js\";\nimport type { Db } from \"../../db.js\";\nimport { pc } from \"../../output.js\";\nimport { TaskNotFoundError, type TaskRow, getTask, getTaskEdges } from \"../../tasks.js\";\n\ninterface TreeOpts {\n /** Show dependents (what this task blocks) instead of blockers. */\n down?: boolean;\n json?: boolean;\n workstream?: string;\n}\n\n/** JSON shape: each node carries its full TaskRow plus a recursive\n * `children` array (whose contents depend on direction — blockers if\n * --no-down, dependents if --down). Diamond-recurrent nodes carry\n * `recurrence: true` and an empty `children` (instead of expanding). */\ninterface TreeJsonNode {\n task: TaskRow;\n recurrence?: true;\n children: TreeJsonNode[];\n}\n\nexport async function cmdTaskTree(db: Db, rawId: string, opts: TreeOpts): Promise<void> {\n // resolveEntityRef parses `<workstream>/<name>` if present, else uses\n // opts.workstream. Returns the resolved (name, workstream) pair so we\n // never re-resolve downstream — the workstream is mandatory on every\n // SDK call that resolves an entity by name.\n const { name: rootId, workstream: ws } = await resolveEntityRef(db, rawId, opts, \"task\");\n assertTaskInWorkstream(db, rootId, ws);\n const root = getTask(db, rootId, ws);\n if (!root) throw new TaskNotFoundError(rootId);\n const down = opts.down ?? false;\n const seen = new Set<string>([rootId]);\n\n if (opts.json) {\n const node: TreeJsonNode = {\n task: root,\n children: buildJsonTree(db, ws, rootId, down, seen),\n };\n emitJson({ direction: down ? \"dependents\" : \"blockers\", root: node });\n return;\n }\n\n const direction = down ? \"dependents\" : \"blockers\";\n const swapHint = down ? \"swap to --no-down for blockers\" : \"--down for dependents\";\n console.log(pc.bold(`Tree of ${rootId} ${pc.dim(`(${direction} below; ${swapHint})`)}`));\n console.log(formatTreeNodeLabel(root));\n // Global \"already rendered\" set: a node visited once gets its full\n // subtree drawn; subsequent visits (in a DAG diamond) print a one-line\n // recurrence marker and don't recurse. Schema forbids cycles, so this\n // only fires on diamonds in practice; double-edged as defence against\n // future bugs.\n renderTree(db, ws, rootId, \"\", down, seen);\n}\n\nfunction buildJsonTree(\n db: Db,\n workstream: string,\n taskId: string,\n down: boolean,\n seen: Set<string>,\n): TreeJsonNode[] {\n const edges = getTaskEdges(db, taskId, workstream);\n const childIds = down ? edges.dependents : edges.blockers;\n const out: TreeJsonNode[] = [];\n for (const childId of childIds) {\n const child = getTask(db, childId, workstream);\n if (!child) continue;\n if (seen.has(childId)) {\n out.push({ task: child, recurrence: true, children: [] });\n continue;\n }\n seen.add(childId);\n out.push({ task: child, children: buildJsonTree(db, workstream, childId, down, seen) });\n }\n return out;\n}\n\nfunction renderTree(\n db: Db,\n workstream: string,\n taskId: string,\n prefix: string,\n down: boolean,\n seen: Set<string>,\n): void {\n const edges = getTaskEdges(db, taskId, workstream);\n const children = down ? edges.dependents : edges.blockers;\n if (children.length === 0) return;\n\n for (let i = 0; i < children.length; i++) {\n const childId = children[i];\n if (childId === undefined) continue;\n const isLast = i === children.length - 1;\n const branch = isLast ? \"└── \" : \"├── \";\n const childPrefix = prefix + (isLast ? \" \" : \"│ \");\n\n const child = getTask(db, childId, workstream);\n if (!child) {\n // Defensive: schema FKs prevent this, but the cascade-on-delete\n // could in theory race a sibling read. Render a clear marker.\n console.log(`${prefix}${branch}${pc.red(`${childId} (missing!)`)}`);\n continue;\n }\n\n if (seen.has(childId)) {\n console.log(\n `${prefix}${branch}${formatTreeNodeLabel(child)} ${pc.dim(\"(↻ already shown above)\")}`,\n );\n continue;\n }\n\n console.log(`${prefix}${branch}${formatTreeNodeLabel(child)}`);\n seen.add(childId);\n renderTree(db, workstream, childId, childPrefix, down, seen);\n }\n}\n\nfunction formatTreeNodeLabel(t: TaskRow): string {\n return `${pc.bold(t.name)} ${colorStatus(t.status)} ${pc.dim(t.title)}`;\n}\n","// mu — `mu task` commander wiring.\n//\n// Pure glue: every `task.command(...)` definition lives here, so the\n// per-verb modules in this cluster stay focused on behaviour. Imported\n// by buildProgram() in src/cli.ts via the re-export hub at\n// src/cli/tasks.ts.\n//\n// Extracted from src/cli/tasks.ts as part of the wire-out follow-up\n// to refactor_split_large_src_files.\n\nimport type { Command } from \"commander\";\nimport {\n JSON_OPT,\n TASK_SORT_KEYS,\n WORKSTREAM_OPT,\n handle,\n parseImpact,\n parseLines,\n parsePositiveNumber,\n} from \"../../cli.js\";\nimport { TASK_STATUS_LIST } from \"../../tasks.js\";\nimport { cmdClaim, cmdTaskRelease, cmdTaskWait } from \"./claim.js\";\nimport { cmdTaskBlock, cmdTaskDelete, cmdTaskReparent, cmdTaskUnblock } from \"./edges.js\";\nimport { cmdTaskAdd, cmdTaskNote, cmdTaskNotes, cmdTaskShow, cmdTaskUpdate } from \"./edit.js\";\nimport { cmdTaskClose, cmdTaskDefer, cmdTaskOpen, cmdTaskReject } from \"./lifecycle.js\";\nimport { cmdTaskList, cmdTaskNext, cmdTaskOwnedBy } from \"./queries.js\";\nimport { cmdTaskTree } from \"./tree.js\";\n\nexport function wireTaskCommands(program: Command): void {\n const task = program.command(\"task\").description(\"Task graph commands\");\n\n task\n .command(\"add [id]\")\n .description(\n \"Add a task to the graph. The id positional is optional — if omitted, derived from --title via slugify (collisions get _2, _3, … suffixes). Auto-derived ids are capped at ~40 chars with a word-boundary cut, so long titles drop trailing clauses; pass the <id> positional explicitly to override.\",\n )\n .requiredOption(\"-t, --title <title>\", \"task title\")\n .requiredOption(\"-i, --impact <n>\", \"impact 1..100\", parseImpact)\n .requiredOption(\"-e, --effort-days <days>\", \"effort in days (>0)\", parsePositiveNumber)\n .option(\n \"-b, --blocked-by <ids...>\",\n \"task ids that block this one (repeat or comma-separate; or both)\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (id: string | undefined) {\n const opts = (this as Command).opts() as {\n title: string;\n impact: number;\n effortDays: number;\n blockedBy?: string[];\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskAdd(db, id, opts), this as Command)();\n });\n\n // --sort key list shared across list/next/ready. `id` is the\n // historical default for `mu task list`; `roi` is the default for\n // `next`/`ready` (the \"what should I do\" verbs). The two time-based\n // keys (`recency` = updated_at DESC, `age` = created_at ASC) trigger\n // an extra `updated`/`created` column with relative timestamps so\n // the user sees the dimension they sorted by.\n const SORT_OPT_DESC = `sort key (${TASK_SORT_KEYS.join(\" | \")})`;\n\n task\n .command(\"list\")\n .description(\"List every task in the current workstream (id, status, ROI, owner)\")\n .option(...WORKSTREAM_OPT)\n .option(\n \"--status <status...>\",\n `filter by lifecycle status (${TASK_STATUS_LIST}; case-insensitive; repeat or comma-separate; or both)`,\n )\n .option(\"--sort <key>\", `${SORT_OPT_DESC} (default id)`)\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as {\n workstream?: string;\n json?: boolean;\n status?: string[];\n sort?: string;\n };\n return handle((db) => cmdTaskList(db, opts), this as Command)();\n });\n\n task\n .command(\"next\")\n .description(\n \"Show the next ready task(s) by ROI (impact / effort_days). The 'what should I do?' verb. Pass -n 0 for the unlimited 'what is doable?' shape (merged-in `task ready`).\",\n )\n .option(\n \"-n, --lines <k>\",\n \"how many top-K tasks to return (default 1; 0 = all ready)\",\n parseLines,\n )\n .option(...WORKSTREAM_OPT)\n .option(\"--sort <key>\", `${SORT_OPT_DESC} (default roi)`)\n .option(\n \"--status <status...>\",\n `filter by lifecycle status (${TASK_STATUS_LIST}; case-insensitive; repeat or comma-separate; or both)`,\n )\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as {\n workstream?: string;\n lines?: number;\n json?: boolean;\n sort?: string;\n status?: string[];\n };\n return handle((db) => cmdTaskNext(db, opts), this as Command)();\n });\n\n task\n .command(\"owned-by <agent>\")\n .description(\n \"List tasks owned by an agent. Defaults to the current workstream (v5: agent names are per-workstream unique). Pass --all to surface every workstream's same-named worker. Excludes CLOSED by default — pass --include-closed for the full historical owner list.\",\n )\n .option(\n \"--include-closed\",\n \"include CLOSED tasks (closeTask preserves owner as historical record; default omits them)\",\n )\n .option(\n \"--all\",\n \"surface every workstream's same-named agent (cross-workstream view; default scopes to current workstream)\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (agent: string) {\n const opts = (this as Command).optsWithGlobals() as {\n json?: boolean;\n includeClosed?: boolean;\n all?: boolean;\n workstream?: string;\n };\n return handle((db) => cmdTaskOwnedBy(db, agent, opts), this as Command)();\n });\n\n task\n .command(\"note <id> <text>\")\n .description(\n \"Append a note to a task. Author defaults to $MU_AGENT_NAME (env injected at spawn) > pane title > $USER > 'orchestrator'; pass --author to override. Single-quote the text (or use a quoted heredoc) to defer shell expansion of $VAR / $(...) / `cmd`; double quotes expand them in your shell before mu sees the note.\",\n )\n .option(\"--author <name>\", \"override the auto-detected author label\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (id: string, text: string) {\n const opts = (this as Command).opts() as {\n workstream?: string;\n json?: boolean;\n author?: string;\n };\n return handle((db) => cmdTaskNote(db, id, text, opts), this as Command)();\n });\n\n task\n .command(\"show <id>\")\n .description(\"Show a task: row + edges (blockers/dependents) + notes\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as { json?: boolean; workstream?: string };\n return handle((db) => cmdTaskShow(db, id, opts), this as Command)();\n });\n\n task\n .command(\"tree <id>\")\n .description(\n \"ASCII tree of a task's blockers (default) or dependents (--down). Diamonds collapse to one render with an arrow marker.\",\n )\n .option(\"--down\", \"render dependents (what this task blocks) instead of blockers\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as {\n down?: boolean;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskTree(db, id, opts), this as Command)();\n });\n\n task\n .command(\"notes <id>\")\n .description(\"List the notes attached to a task (oldest first)\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as { json?: boolean; workstream?: string };\n return handle((db) => cmdTaskNotes(db, id, opts), this as Command)();\n });\n\n // --evidence <text> on the four lifecycle verbs records what the\n // caller relied on (test output, command exit, observed file change)\n // in the auto-emitted event payload. The verb still trusts the\n // caller; the audit trail records what they said. First inch of\n // the \"observed vs claimed state\" distinction.\n const EVIDENCE_OPT = [\n \"--evidence <text>\",\n \"record what the caller observed (e.g. 'tests pass: npm test exit 0'); appears verbatim in the event log\",\n ] as const;\n\n task\n .command(\"close <id>\")\n .description(\n \"Mark a task CLOSED (idempotent). --if-ready no-ops unless every blocker is in a terminal status (CLOSED / REJECTED / DEFERRED) — the umbrella-on-wave-done pattern.\",\n )\n .option(\n \"--if-ready\",\n \"only close when every blocker is terminal (CLOSED / REJECTED / DEFERRED); otherwise no-op + list the still-blocking ids\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...EVIDENCE_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as {\n evidence?: string;\n ifReady?: boolean;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskClose(db, id, opts), this as Command)();\n });\n\n task\n .command(\"open <id>\")\n .description(\"Mark a task OPEN — e.g. to reopen something closed by mistake (idempotent)\")\n .option(...WORKSTREAM_OPT)\n .option(...EVIDENCE_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as {\n evidence?: string;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskOpen(db, id, opts), this as Command)();\n });\n\n task\n .command(\"reject <id>\")\n .description(\n \"Mark a task REJECTED — terminal 'won't do' (out of scope, duplicate, wontfix). Refuses if open dependents would be stranded; --cascade previews the sub-tree (dry-run by default), --cascade --yes commits.\",\n )\n .option(\n \"--cascade\",\n \"include every transitive open/in-progress dependent (dry-run; pass --yes to commit)\",\n )\n .option(\"-y, --yes\", \"actually sweep the cascade preview (no-op without --cascade)\")\n .option(...WORKSTREAM_OPT)\n .option(...EVIDENCE_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as {\n cascade?: boolean;\n yes?: boolean;\n evidence?: string;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskReject(db, id, opts), this as Command)();\n });\n\n task\n .command(\"defer <id>\")\n .description(\n \"Mark a task DEFERRED — parked, may revisit. Like reject, doesn't satisfy a blocked-by edge; refuses if open dependents would be stranded; --cascade previews the sub-tree (dry-run by default), --cascade --yes commits.\",\n )\n .option(\n \"--cascade\",\n \"include every transitive open/in-progress dependent (dry-run; pass --yes to commit)\",\n )\n .option(\"-y, --yes\", \"actually sweep the cascade preview (no-op without --cascade)\")\n .option(...WORKSTREAM_OPT)\n .option(...EVIDENCE_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as {\n cascade?: boolean;\n yes?: boolean;\n evidence?: string;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskDefer(db, id, opts), this as Command)();\n });\n\n task\n .command(\"release <id>\")\n .description(\n \"Clear a task's owner. IN_PROGRESS auto-flips to OPEN so the task re-enters the ready set; other statuses preserved. Use --reopen to force OPEN from CLOSED/REJECTED/DEFERRED. Idempotent.\",\n )\n .option(\n \"--reopen\",\n \"force status to OPEN regardless of current status (escape hatch for un-closing a CLOSED owned task in one verb)\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...EVIDENCE_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as {\n reopen?: boolean;\n evidence?: string;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskRelease(db, id, opts), this as Command)();\n });\n\n task\n .command(\"claim <id>\")\n .description(\n \"Claim a task. Default: derive agent from $TMUX_PANE's title (must be a registered worker). \" +\n \"Use --for <worker> to dispatch. Use --self for orchestrator-direct work (anonymous claim, owner=NULL, actor recorded in agent_logs).\",\n )\n .option(\n \"-f, --for <agent>\",\n \"claim on behalf of a registered worker (dispatch); accepts bare 'name' (resolves in the task's workstream) or qualified '<workstream>/<name>' for cross-workstream dispatch (e.g. 'roadmap-v0-3/worker-1')\",\n )\n .option(\n \"--self\",\n \"anonymous claim (orchestrator pattern): owner stays NULL; actor recorded in agent_logs.source. Mutually exclusive with --for.\",\n )\n .option(\n \"--actor <name>\",\n \"override the actor name used for the log (only valid with --self; defaults to pane title or $USER)\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...EVIDENCE_OPT)\n .option(...JSON_OPT)\n .action(function (taskId: string) {\n const opts = (this as Command).opts() as {\n for?: string;\n self?: boolean;\n actor?: string;\n evidence?: string;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdClaim(db, taskId, opts), this as Command)();\n });\n\n task\n .command(\"block <blocked>\")\n .description(\n \"Add a blocking edge: <blocker> --by <id> blocks <blocked>. Validates same-workstream + cycle.\",\n )\n .requiredOption(\"-b, --by <blocker>\", \"the task that should block <blocked>\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (blocked: string) {\n const opts = (this as Command).opts() as { by: string; workstream?: string; json?: boolean };\n return handle((db) => cmdTaskBlock(db, blocked, opts), this as Command)();\n });\n\n task\n .command(\"unblock <blocked>\")\n .description(\"Remove a single blocking edge (idempotent)\")\n .requiredOption(\"-b, --by <blocker>\", \"the task whose blocker edge to remove\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (blocked: string) {\n const opts = (this as Command).opts() as { by: string; workstream?: string; json?: boolean };\n return handle((db) => cmdTaskUnblock(db, blocked, opts), this as Command)();\n });\n\n task\n .command(\"delete <id>\")\n .description(\n \"Delete a task (cascades edges + notes via FK). Two-phase: bare = dry-run preview; --yes commits. Idempotent on missing. Auto-snapshots before the commit; `mu undo --yes` reverts (DB only).\",\n )\n .option(\"-y, --yes\", \"actually delete (without --yes prints a dry-run preview)\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as {\n workstream?: string;\n json?: boolean;\n yes?: boolean;\n };\n return handle((db) => cmdTaskDelete(db, id, opts), this as Command)();\n });\n\n task\n .command(\"update <id>\")\n .description(\n \"Update scalar fields on a task. Pass at least one of --title, --impact, --effort-days. Use close/open/release for status/owner changes.\",\n )\n .option(\"-t, --title <title>\", \"new title\")\n .option(\"-i, --impact <n>\", \"new impact 1..100\", parseImpact)\n .option(\"-e, --effort-days <days>\", \"new effort in days (>0)\", parsePositiveNumber)\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as {\n title?: string;\n impact?: number;\n effortDays?: number;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskUpdate(db, id, opts), this as Command)();\n });\n\n task\n .command(\"reparent <id>\")\n .description(\n \"Atomically replace every incoming edge of <id> with the new --blocked-by list. Pass --blocked-by '' to clear all blockers.\",\n )\n .requiredOption(\n \"-b, --blocked-by <ids...>\",\n \"tasks that block <id> (repeat or comma-separate; or both; pass an empty string to clear)\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (id: string) {\n const opts = (this as Command).opts() as {\n blockedBy: string[];\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskReparent(db, id, opts), this as Command)();\n });\n\n task\n .command(\"wait <ids...>\")\n .description(\n \"Block until the listed tasks reach --status (default CLOSED). Each <id> may be bare (resolves via -w / $MU_SESSION / tmux) or qualified `<workstream>/<name>` (cross-workstream waits don't need -w). Default: every task must reach the target (--all). --any / --first exit on the first one that does; --first additionally prints the firing ref's qualified id to stdout. Exit 0 = condition met; 5 = timeout; 6 = a watched task was reaper-flipped IN_PROGRESS→OPEN (target=CLOSED only).\",\n )\n .option(\n \"--status <status>\",\n `target status (${TASK_STATUS_LIST}, case-insensitive); default CLOSED`,\n )\n .option(\"--any\", \"succeed as soon as ONE listed task reaches the target (default: all must)\")\n .option(\n \"--first\",\n \"alias for --any that ALSO prints the firing ref's qualified id to stdout (--json adds a `firing` field). Use to drive a single-shot dispatch loop: `closed=$(mu task wait a b --first --json | jq -r .firing.qualifiedId)`.\",\n )\n .option(\"--timeout <seconds>\", \"max seconds to wait (0 = forever, default 600)\", parseLines)\n .option(\n \"--stuck-after <seconds>\",\n \"emit a yellow STUCK warning to stderr when an IN_PROGRESS task's owner has been in needs_input for >= N seconds since their last status change (0 = disable, default 300). Surfaces the agent_close_discipline_gap pattern: worker finished + committed but skipped `mu task close <id>`. Wait keeps polling — the warning is observation-only.\",\n parseLines,\n )\n .option(\n \"--on-stall <action>\",\n \"what to do when --stuck-after fires: 'warn' (default; today's behaviour: yellow STUCK warning + corroborating agent_logs event; wait keeps polling) or 'exit' (same emit + persist, then exit 7 = STALL_DETECTED so an unattended orchestrator can branch on the idle-vs-dead distinction). Suppressed when --status is anything other than CLOSED (mirrors exit-6's carve-out). If exit-6 (dead pane) and exit-7 (stall) would fire in the same poll, exit 6 wins (dead pane is unambiguous; stall is ambiguous).\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (ids: string[]) {\n const opts = (this as Command).opts() as {\n status?: string;\n any?: boolean;\n first?: boolean;\n timeout?: number;\n stuckAfter?: number;\n onStall?: \"warn\" | \"exit\";\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdTaskWait(db, ids, opts), this as Command)();\n });\n}\n","// mu — every `mu agent ...` verb + spawn / send / read / list / show /\n// close / free / adopt / attach + the top-level `mu me` agent-self\n// verb (default + `tasks` / `next` subcommands).\n//\n// Extracted from src/cli.ts as part of refactor_split_large_src_files.\n//\n// `whoami` / `my-tasks` / `my-next` were merged into `mu me` /\n// `mu me tasks` / `mu me next` in audit_merge_self_verbs_into_mu_me\n// (audit_cleanups_post_schema_v5_wave). The three old verbs are\n// gone; the three cmd functions (`cmdMe`, `cmdMyTasks`, `cmdMyNext`)\n// stayed (with `cmdMe` formerly `cmdWhoami`).\n\nimport {\n type AdoptAgentOptions,\n type AdoptAgentResult,\n AgentNotFoundError,\n adoptAgent,\n closeAgent,\n freeAgent,\n getAgent,\n listLiveAgents,\n readAgent,\n refreshAgentTitle,\n resolveCliCommand,\n sendToAgent,\n shouldOverwriteAgentStatus,\n spawnAgent,\n updateAgentStatus,\n} from \"../agents.js\";\nimport {\n UsageError,\n assertAgentInWorkstream,\n emitJson,\n formatAgentsTable,\n formatTaskListTable,\n resolveEntityRef,\n resolveSelf,\n resolveWorkstream,\n statusIcon,\n} from \"../cli.js\";\nimport type { Db } from \"../db.js\";\nimport { detectPiStatus } from \"../detect.js\";\nimport { type NextStep, pc, printNextSteps } from \"../output.js\";\nimport { listTasksByOwner } from \"../tasks.js\";\nimport {\n capturePane,\n enableMuPaneBordersForPane,\n listPanesInSession,\n sessionExists,\n} from \"../tmux.js\";\nimport { type VcsBackendName, detectBackend } from \"../vcs.js\";\nimport { getWorkspaceForAgent } from \"../workspace.js\";\n\ninterface SpawnOpts {\n cli?: string;\n command?: string;\n tab?: string;\n role?: string;\n cwd?: string;\n workstream?: string;\n workspace?: boolean;\n workspaceBackend?: VcsBackendName;\n workspaceFrom?: string;\n workspaceProjectRoot?: string;\n json?: boolean;\n}\nexport async function cmdSpawn(db: Db, name: string, opts: SpawnOpts): Promise<void> {\n const workstream = await resolveWorkstream(opts.workstream);\n\n // Preflight: when --workspace is set, resolve+announce the backend\n // and projectRoot BEFORE the long-running git-worktree-add /\n // jj-workspace-add / cp -a, so the operator can ctrl-c if the\n // detected backend is wrong (e.g. cp -a falling through to a\n // 60GB project tree because --workspace-project-root pointed at a\n // non-VCS dir; surfaced by bug_agent_spawn_workspace_aborts_without_status).\n // Skipped on --json so machine consumers get a single structured\n // output, not preflight chatter.\n if (opts.workspace && !opts.json) {\n const projectRoot = opts.workspaceProjectRoot ?? process.cwd();\n const backend: VcsBackendName =\n opts.workspaceBackend ?? (await detectBackend(projectRoot)).name;\n const warn =\n backend === \"none\"\n ? pc.yellow(\n \" — WARNING: 'none' backend will cp -a the entire projectRoot. Verify --workspace-project-root.\",\n )\n : \"\";\n console.log(\n pc.dim(\n `[mu] workspace preflight: backend=${pc.bold(backend)} projectRoot=${pc.bold(projectRoot)}${warn}`,\n ),\n );\n }\n\n const agent = await spawnAgent(db, {\n name,\n workstream,\n ...(opts.cli !== undefined ? { cli: opts.cli } : {}),\n ...(opts.command !== undefined ? { command: opts.command } : {}),\n ...(opts.tab !== undefined ? { tab: opts.tab } : {}),\n ...(opts.role !== undefined ? { role: opts.role } : {}),\n ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),\n ...(opts.workspace !== undefined ? { workspace: opts.workspace } : {}),\n ...(opts.workspaceBackend !== undefined ? { workspaceBackend: opts.workspaceBackend } : {}),\n ...(opts.workspaceFrom !== undefined ? { workspaceFrom: opts.workspaceFrom } : {}),\n ...(opts.workspaceProjectRoot !== undefined\n ? { workspaceProjectRoot: opts.workspaceProjectRoot }\n : {}),\n });\n const workspace = opts.workspace ? getWorkspaceForAgent(db, name, workstream) : undefined;\n // Resolve the actual command that landed in the pane, so the operator\n // can confirm `--command 'pi-meta --no-solo'` (etc.) took effect.\n // Mirrors the resolution chain in spawnAgent: explicit --command >\n // $MU_<UPPER_CLI>_COMMAND > the cli value itself. Surfaced from\n // mufeedback note #159: 'Spawned ... (pi)' was misleading when\n // --command overrode the binary.\n const resolvedCommand = opts.command ?? resolveCliCommand(agent.cli);\n const commandOverridden = resolvedCommand !== agent.cli;\n const nextSteps: NextStep[] = [\n { intent: \"Send work\", command: `mu agent send ${name} \"...\" -w ${workstream}` },\n { intent: \"Read pane\", command: `mu agent read ${name} -w ${workstream}` },\n { intent: \"Watch live events\", command: `mu log -w ${workstream} --tail` },\n {\n intent: \"Close (drops registry row, kills pane)\",\n command: `mu agent close ${name} -w ${workstream}`,\n },\n ];\n if (opts.json) {\n emitJson({\n agent,\n workspace: workspace ?? null,\n resolvedCommand,\n commandOverridden,\n nextSteps,\n });\n return;\n }\n const wsBit = opts.workspace ? pc.dim(\" with auto-workspace\") : \"\";\n // Show 'pi (cmd: pi-meta --no-solo)' when overridden; just '(pi)'\n // when running the default binary for the cli key. Avoids the\n // misleading 'Spawned X (pi)' for pi-meta workers.\n const cliDisplay = commandOverridden\n ? `${agent.cli} ${pc.dim(`(cmd: ${resolvedCommand})`)}`\n : agent.cli;\n console.log(\n `Spawned ${pc.bold(agent.name)} (${cliDisplay}) in window ${pc.bold(agent.tab ?? agent.name)} of ${pc.bold(`mu-${workstream}`)}, pane ${pc.dim(agent.paneId)}${wsBit}`,\n );\n if (workspace) console.log(pc.dim(` workspace: ${workspace.path} (${workspace.backend})`));\n printNextSteps(nextSteps);\n}\n\nexport async function cmdSend(\n db: Db,\n rawName: string,\n text: string,\n opts: { workstream?: string; json?: boolean } = {},\n): Promise<void> {\n const { name } = await resolveEntityRef(db, rawName, opts, \"agent\");\n assertAgentInWorkstream(db, name, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n await sendToAgent(db, name, text, { workstream: ws });\n const nextSteps: NextStep[] = [\n { intent: \"Read response\", command: `mu agent read ${name} -n 50 -w ${ws}` },\n { intent: \"Watch live events\", command: `mu log -w ${ws} --tail` },\n ];\n if (opts.json) {\n emitJson({ agentName: name, sentBytes: text.length, nextSteps });\n return;\n }\n console.log(pc.dim(`sent ${text.length} bytes to ${name}`));\n printNextSteps(nextSteps);\n}\n\nexport async function cmdRead(\n db: Db,\n rawName: string,\n opts: { lines?: number; workstream?: string; json?: boolean },\n): Promise<void> {\n const { name } = await resolveEntityRef(db, rawName, opts, \"agent\");\n assertAgentInWorkstream(db, name, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const text = await readAgent(db, name, {\n workstream: ws,\n ...(opts.lines !== undefined ? { lines: opts.lines } : {}),\n });\n if (opts.json) {\n emitJson({\n agentName: name,\n lines: opts.lines ?? null,\n scrollback: text,\n scrollbackLines: text.split(\"\\n\").length,\n });\n return;\n }\n process.stdout.write(text);\n if (!text.endsWith(\"\\n\")) process.stdout.write(\"\\n\");\n}\n\nexport async function cmdList(\n db: Db,\n opts: { workstream?: string; all?: boolean; json?: boolean },\n): Promise<void> {\n const workstream = await resolveWorkstream(opts.workstream);\n const view = await listLiveAgents(db, { workstream });\n if (opts.json) {\n emitJson({ workstreamName: workstream, agents: view.agents, orphans: view.orphans });\n return;\n }\n console.log(pc.bold(`mu-${workstream}`));\n console.log(formatAgentsTable(view.agents));\n if (view.orphans.length > 0) {\n console.log(\"\");\n console.log(pc.yellow(`Orphan panes (${view.orphans.length})`));\n console.log(pc.dim(\" Panes that look like agents but aren't in the registry.\"));\n console.log(\n pc.dim(\n \" Run `mu adopt <pane-id>` to register one as a managed agent (e.g. `mu adopt %15`).\",\n ),\n );\n for (const orphan of view.orphans) {\n console.log(\n ` ${pc.dim(orphan.paneId)} title=${pc.bold(orphan.title)} cli=${orphan.command}`,\n );\n }\n }\n}\n\nexport async function cmdAgentShow(\n db: Db,\n rawName: string,\n opts: { lines?: number; json?: boolean; workstream?: string },\n): Promise<void> {\n const { name } = await resolveEntityRef(db, rawName, opts, \"agent\");\n assertAgentInWorkstream(db, name, opts.workstream);\n // v5: agents.name is per-workstream unique. Resolve the operator's\n // workstream up front so the lookup scopes correctly.\n const ws = await resolveWorkstream(opts.workstream);\n const agent = getAgent(db, name, ws);\n if (!agent) throw new AgentNotFoundError(name);\n const lines = opts.lines ?? 20;\n let scrollback: string;\n try {\n scrollback = await capturePane(agent.paneId, { lines });\n } catch {\n scrollback = \"\";\n }\n\n // Fresh-status reconciliation. The persisted `agents.status` is\n // whatever the last reconcile pass wrote (typically via\n // `mu agent list` or `mu state`). For `mu agent show <name>` the\n // operator's expectation is \"give me the *current* picture,\" so we\n // re-run the detector against the scrollback we just captured and\n // update the row if status changed. Same shouldOverwrite rules as\n // listLiveAgents (free is sticky until real activity, etc).\n // Real bug found in real use: status was reading stale, especially\n // bad with custom --command wrappers where the orchestrator never noticed needs_input.\n let displayed = agent;\n if (scrollback.trim() !== \"\") {\n const detected = detectPiStatus(scrollback);\n if (detected !== agent.status && shouldOverwriteAgentStatus(agent.status, detected)) {\n updateAgentStatus(db, agent.name, detected, agent.workstreamName);\n const refreshed = getAgent(db, name, agent.workstreamName);\n if (refreshed) displayed = refreshed;\n }\n }\n\n if (opts.json) {\n emitJson({ agent: displayed, scrollback, scrollbackLines: lines });\n return;\n }\n\n console.log(pc.bold(`${displayed.name} ${statusIcon(displayed.status)} ${displayed.status}`));\n console.log(` workstream : ${agent.workstreamName}`);\n console.log(` cli : ${agent.cli}`);\n console.log(` pane : ${pc.dim(agent.paneId)}`);\n console.log(` window : ${agent.tab ?? agent.name}`);\n console.log(` role : ${agent.role}`);\n console.log(` created : ${pc.dim(agent.createdAt)}`);\n console.log(` updated : ${pc.dim(agent.updatedAt)}`);\n\n console.log(\"\");\n console.log(pc.bold(`Recent scrollback (last ${lines} lines)`));\n if (scrollback.trim() === \"\") {\n console.log(pc.dim(\" (pane gone or empty)\"));\n return;\n }\n for (const line of scrollback.replace(/\\n+$/, \"\").split(\"\\n\")) {\n console.log(` ${line}`);\n }\n}\n\nexport async function cmdMe(\n db: Db,\n opts: { json?: boolean; includeClosed?: boolean } = {},\n): Promise<void> {\n const self = resolveSelf(db);\n const owned = listTasksByOwner(db, self.workstreamName, self.name, {\n includeClosed: opts.includeClosed ?? false,\n });\n\n if (opts.json) {\n emitJson({ agent: self, ownedTasks: owned });\n return;\n }\n\n console.log(pc.bold(`${self.name} ${statusIcon(self.status)} ${self.status}`));\n console.log(` workstream : ${self.workstreamName}`);\n console.log(` cli : ${self.cli}`);\n console.log(` pane : ${pc.dim(self.paneId)}`);\n console.log(` role : ${self.role}`);\n console.log(\"\");\n if (owned.length === 0) {\n console.log(pc.dim(\"Currently owns no tasks. Try `mu me next` for a recommendation.\"));\n return;\n }\n console.log(pc.bold(`Currently owns ${owned.length} task${owned.length === 1 ? \"\" : \"s\"}`));\n console.log(formatTaskListTable(owned));\n}\n\nexport async function cmdClose(\n db: Db,\n rawName: string,\n opts: { workstream?: string; json?: boolean; discardWorkspace?: boolean } = {},\n): Promise<void> {\n const { name } = await resolveEntityRef(db, rawName, opts, \"agent\");\n assertAgentInWorkstream(db, name, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const result = await closeAgent(db, name, {\n workstream: ws,\n ...(opts.discardWorkspace === true ? { discardWorkspace: true } : {}),\n });\n const next: NextStep[] = [];\n if (result.workspaceFreed) {\n next.push({\n intent: \"Workspace was freed alongside the agent (--discard-workspace)\",\n command: \"cd / # the workspace dir is gone\",\n });\n }\n next.push({\n intent: \"Re-spawn under the same name\",\n command: `mu agent spawn ${name} -w <workstream>`,\n });\n if (opts.json) {\n emitJson({ agentName: name, ...result, nextSteps: next });\n return;\n }\n if (!result.killedPane && !result.deletedRow) {\n console.log(pc.dim(`no agent named ${name} (already closed?)`));\n printNextSteps(next);\n return;\n }\n const wsBit = result.workspaceFreed ? pc.dim(\" (workspace discarded)\") : \"\";\n console.log(`Closed ${pc.bold(name)}${wsBit}`);\n printNextSteps(next);\n}\n\ninterface AdoptCliOpts {\n workstream?: string;\n name?: string;\n cli?: string;\n role?: string;\n json?: boolean;\n}\n\nexport async function cmdAdopt(db: Db, paneOrTitle: string, opts: AdoptCliOpts): Promise<void> {\n const ws = await resolveWorkstream(opts.workstream);\n\n // Allow `mu adopt <pane-id>` (literal '%15') OR `mu adopt <pane-title>`\n // (a string that looks like an agent name; we look it up in the\n // workstream's tmux session). Pane-id form is preferred for scripting;\n // pane-title form is the ergonomic form for interactive use.\n let paneId: string;\n if (paneOrTitle.startsWith(\"%\")) {\n paneId = paneOrTitle;\n } else {\n const session = `mu-${ws}`;\n const panes = await listPanesInSession(session);\n const match = panes.find((p) => p.title === paneOrTitle);\n if (!match) {\n throw new UsageError(\n `no pane with title '${paneOrTitle}' in tmux session ${session} (try \\`mu agent list -w ${ws}\\` and pass the pane id)`,\n );\n }\n paneId = match.paneId;\n }\n\n const adoptOpts: AdoptAgentOptions = {\n paneId,\n workstream: ws,\n name: opts.name,\n cli: opts.cli,\n role: opts.role,\n };\n const result: AdoptAgentResult = await adoptAgent(db, adoptOpts);\n // Refresh title with composed state. Adopt may have set it to just\n // the agent name; this re-renders with status emoji etc.\n await refreshAgentTitle(db, result.agent.name, ws);\n\n // Window-scoped border for the adopted pane's window. (cmdInit set\n // it on _mu but adopted panes can be in any window.) Self-checks\n // MU_BANNER_QUIET; best-effort.\n await enableMuPaneBordersForPane(paneId);\n\n const nextSteps: NextStep[] = [\n { intent: \"Send work\", command: `mu agent send ${result.agent.name} \"...\" -w ${ws}` },\n { intent: \"Read pane\", command: `mu agent read ${result.agent.name} -w ${ws}` },\n { intent: \"Verify in agent list\", command: `mu agent list -w ${ws}` },\n ];\n\n if (opts.json) {\n emitJson({\n adopted: !result.alreadyAdopted,\n alreadyAdopted: result.alreadyAdopted,\n agent: result.agent,\n previousTitle: result.previousTitle,\n paneTitleSetTo: result.paneTitleSetTo,\n nextSteps,\n });\n return;\n }\n\n if (result.alreadyAdopted) {\n console.log(pc.dim(`already adopted: ${result.agent.name} (pane ${result.agent.paneId})`));\n printNextSteps(nextSteps);\n return;\n }\n console.log(\n `Adopted ${pc.bold(result.agent.name)} ${pc.dim(`(pane ${result.agent.paneId}, workstream ${result.agent.workstreamName})`)}`,\n );\n if (result.previousTitle !== null && result.previousTitle !== result.paneTitleSetTo) {\n console.log(pc.dim(` pane title: '${result.previousTitle}' -> '${result.paneTitleSetTo}'`));\n }\n printNextSteps(nextSteps);\n}\n\nexport async function cmdFree(\n db: Db,\n rawName: string,\n opts: { workstream?: string; json?: boolean } = {},\n): Promise<void> {\n const { name } = await resolveEntityRef(db, rawName, opts, \"agent\");\n assertAgentInWorkstream(db, name, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const r = freeAgent(db, name, ws);\n if (r.changed) await refreshAgentTitle(db, name, ws);\n const nextSteps: NextStep[] = [\n { intent: \"Send work to the freed agent\", command: `mu agent send ${name} '...' -w ${ws}` },\n { intent: \"Close the agent\", command: `mu agent close ${name} -w ${ws}` },\n { intent: \"See workstream state\", command: `mu state -w ${ws}` },\n ];\n if (opts.json) {\n emitJson({ agentName: name, ...r, nextSteps });\n return;\n }\n if (!r.changed) {\n console.log(pc.dim(`${name} already free (no-op)`));\n printNextSteps(nextSteps);\n return;\n }\n console.log(`Freed ${pc.bold(name)} ${pc.dim(`(${r.previousStatus} → ${r.status})`)}`);\n printNextSteps(nextSteps);\n}\n\nexport async function cmdAttach(\n db: Db,\n rawName: string,\n opts: { workstream?: string },\n): Promise<void> {\n const { name } = await resolveEntityRef(db, rawName, opts, \"agent\");\n const workstream = await resolveWorkstream(opts.workstream);\n const sessionName = `mu-${workstream}`;\n if (!(await sessionExists(sessionName))) {\n throw new UsageError(`workstream \"${workstream}\" has no tmux session yet`);\n }\n // mu agent attach prints scrollback + an attach hint; it has no\n // business pruning the registry. status-only so the operator still\n // sees fresh agent status when they run it interactively.\n const view = await listLiveAgents(db, { workstream, mode: \"status-only\" });\n const agent = view.agents.find((a) => a.name === name);\n if (!agent) {\n throw new AgentNotFoundError(name);\n }\n // Capture and print its scrollback.\n const text = await capturePane(agent.paneId);\n process.stdout.write(text);\n console.log(\"\");\n console.log(\n pc.dim(\n `Attach with: tmux a -t ${sessionName} && tmux select-window -t ${agent.tab ?? agent.name}`,\n ),\n );\n}\n\n// ─── commander wiring ────────────────────────────────────────────────\n//\n// wireAgentCommands is called by buildProgram() in src/cli.ts. Wired here so\n// every per-namespace builder lives next to its cmd functions.\n\nimport type { Command } from \"commander\";\nimport { JSON_OPT, WORKSTREAM_OPT, handle, parseLines } from \"../cli.js\";\n// wireSelfCommands needs cmdMyTasks / cmdMyNext which live in cli/tasks.ts\n// (they're task queries scoped to the resolved-self agent). Lateral\n// cluster→cluster import documented as the single intentional edge.\nimport { cmdMyNext, cmdMyTasks } from \"./tasks.js\";\n\nexport function wireAgentCommands(program: Command): void {\n const agent = program.command(\"agent\").description(\"Agent-level commands\");\n\n agent\n .command(\"spawn <name>\")\n .description(\"Spawn a new agent in a tmux pane\")\n .option(\n \"--cli <cli>\",\n \"agent CLI key (default: pi); also used as the lookup key for $MU_<UPPER_CLI>_COMMAND, e.g. --cli pi_big resolves $MU_PI_BIG_COMMAND\",\n \"pi\",\n )\n .option(\n \"--command <cmd>\",\n \"executable to run in the pane (defaults to $MU_<CLI>_COMMAND or the cli value)\",\n )\n .option(\"--tab <tab>\", \"tmux window name to group under (defaults to agent name)\")\n .option(\"--role <role>\", \"full-access | read-only\", \"full-access\")\n .option(\"--cwd <cwd>\", \"initial working directory (ignored when --workspace is set)\")\n .option(\"--workspace\", \"auto-create a VCS workspace for this agent and use its path as cwd\")\n .option(\n \"--workspace-backend <name>\",\n \"force a specific VCS backend for --workspace (jj | sl | git | none)\",\n )\n .option(\n \"--workspace-from <ref>\",\n \"base the workspace on a specific commit / branch / changeset\",\n )\n .option(\n \"--workspace-project-root <path>\",\n \"override the project root the workspace branches from (default: cwd)\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (name: string) {\n const opts = (this as Command).opts() as {\n cli?: string;\n command?: string;\n tab?: string;\n role?: string;\n cwd?: string;\n workstream?: string;\n workspace?: boolean;\n workspaceBackend?: VcsBackendName;\n workspaceFrom?: string;\n workspaceProjectRoot?: string;\n json?: boolean;\n };\n return handle((db) => cmdSpawn(db, name, opts), this as Command)();\n });\n\n agent\n .command(\"send <name> <text>\")\n .description(\"Send text to an agent's pane (bracketed-paste protocol)\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (name: string, text: string) {\n const opts = (this as Command).opts() as { workstream?: string; json?: boolean };\n return handle((db) => cmdSend(db, name, text, opts), this as Command)();\n });\n\n agent\n .command(\"read <name>\")\n .description(\"Read an agent's pane scrollback\")\n .option(\"-n, --lines <n>\", \"show last N lines (default: full scrollback)\", parseLines)\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (name: string) {\n const opts = (this as Command).opts() as {\n lines?: number;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdRead(db, name, opts), this as Command)();\n });\n\n agent\n .command(\"list\")\n .description(\"List agents in the current workstream (reconciled with tmux)\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as {\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdList(db, opts), this as Command)();\n });\n\n agent\n .command(\"show <name>\")\n .description(\"Show an agent: registry row + recent scrollback (last N lines, default 20)\")\n .option(\"-n, --lines <n>\", \"how many scrollback lines to show (default 20)\", parseLines)\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (name: string) {\n const opts = (this as Command).opts() as {\n lines?: number;\n json?: boolean;\n workstream?: string;\n };\n return handle((db) => cmdAgentShow(db, name, opts), this as Command)();\n });\n\n agent\n .command(\"close <name>\")\n .description(\n \"Kill an agent's pane and remove its registry row. If the agent has a workspace, refuses by default (would orphan the on-disk dir); pass --discard-workspace to free both, or run `mu workspace free <agent>` first.\",\n )\n .option(\n \"--discard-workspace\",\n \"free the agent's workspace alongside close (lossy: pending changes are gone)\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (name: string) {\n const opts = (this as Command).opts() as {\n workstream?: string;\n json?: boolean;\n discardWorkspace?: boolean;\n };\n return handle((db) => cmdClose(db, name, opts), this as Command)();\n });\n\n agent\n .command(\"free <name>\")\n .description(\n \"Mark an agent's status as 'free' (idempotent). Pane untouched; reconcile flips back to busy on real activity.\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (name: string) {\n const opts = (this as Command).opts() as { workstream?: string; json?: boolean };\n return handle((db) => cmdFree(db, name, opts), this as Command)();\n });\n\n agent\n .command(\"attach <name>\")\n .description(\"Print an agent's full scrollback and the tmux command to attach\")\n .option(...WORKSTREAM_OPT)\n .action(function (name: string) {\n const opts = (this as Command).opts() as { workstream?: string };\n // Routed through handle() like every other verb — errorNextSteps\n // fire on typed errors, exit codes classify uniformly\n // (review_code_attach_bypasses_handle).\n return handle((db) => cmdAttach(db, name, opts), this as Command)();\n });\n\n // `mu adopt` is intentionally TOP-LEVEL, not nested under `mu agent`,\n // matching the original e20af89 design and every doc/skill/orphan-hint\n // reference (`mu adopt %15`, not `mu agent adopt %15`). It was dropped\n // on the floor in the f42e86d wireXxxCommands split — bug_adopt_verb_unwired.\n program\n .command(\"adopt <pane-or-title>\")\n .description(\n \"Register an existing tmux pane as a managed mu agent (the inverse of `mu agent list`'s 'orphan' state). Pane id form '%15' or pane title form 'worker-2'.\",\n )\n .option(\"--name <name>\", \"agent name (defaults to the pane's current title)\")\n .option(\"--cli <cli>\", \"agent CLI key (default: pi)\")\n .option(\"--role <role>\", \"full-access | read-only\", \"full-access\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (paneOrTitle: string) {\n // optsWithGlobals so the top-level -w / --json flags propagate.\n const opts = (this as Command).optsWithGlobals() as AdoptCliOpts;\n return handle((db) => cmdAdopt(db, paneOrTitle, opts), this as Command)();\n });\n\n // ─── task ───────────────────────────────────────────────────────────\n}\n\nexport function wireSelfCommands(program: Command): void {\n // `mu me` is the merged self-identity verb (audit_merge_self_verbs_into_mu_me).\n // Default action: identity block + owned tasks (was `mu whoami`).\n // Subcommands:\n // mu me tasks — just the owned-tasks table (was `mu my-tasks`)\n // mu me next [-n K] — top-K ready in self.workstream (was `mu my-next`)\n const me = program\n .command(\"me\")\n .description(\n \"Identify the agent running this process (via $TMUX_PANE) plus its currently-owned tasks (excludes CLOSED by default). Subcommands: `mu me tasks` for just the owned-tasks table; `mu me next [-n K]` for the top-K ready tasks in this agent's workstream.\",\n )\n .option(\"--include-closed\", \"include CLOSED tasks in the owned-tasks list\")\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as { json?: boolean; includeClosed?: boolean };\n return handle((db) => cmdMe(db, opts), this as Command)();\n });\n\n me.command(\"tasks\")\n .description(\n \"List tasks owned by the current agent (the second half of `mu me`'s output). Excludes CLOSED by default.\",\n )\n .option(\"--include-closed\", \"include CLOSED tasks in the list\")\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as { json?: boolean; includeClosed?: boolean };\n return handle((db) => cmdMyTasks(db, opts), this as Command)();\n });\n\n me.command(\"next\")\n .description(\n \"Top-K ready tasks in the current agent's workstream (alias for `task next -w <self.workstream>`)\",\n )\n .option(\n \"-n, --lines <k>\",\n \"how many top-K tasks to return (default 1; 0 = all ready)\",\n parseLines,\n )\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as { lines?: number; json?: boolean };\n return handle((db) => cmdMyNext(db, opts), this as Command)();\n });\n}\n","// mu — `mu archive` verbs (create / list / show / add / remove / delete).\n//\n// Phase 2 of the v0.3 archive feature (workstream_archive_verb).\n// Phase 1 landed the schema (v6) + SDK (src/archives.ts); this file\n// is the thin commander glue that surfaces them.\n//\n// Six verbs:\n//\n// mu archive create <label> [--description \"...\"]\n// mu archive list\n// mu archive show <label>\n// mu archive add <label> -w <workstream> [--destroy]\n// mu archive remove <label> -w <workstream>\n// mu archive delete <label> [--yes]\n//\n// Mirrors src/cli/workspace.ts's wiring shape (one-file-per-verb-\n// namespace, with wireArchiveCommands at the bottom).\n\nimport {\n type AddToArchiveResult,\n type Archive,\n ArchiveAlreadyExistsError,\n ArchiveLabelInvalidError,\n ArchiveNotFoundError,\n type ArchiveSearchHit,\n type ArchiveSummary,\n addToArchive,\n createArchive,\n deleteArchive,\n getArchive,\n listArchives,\n removeFromArchive,\n searchArchives,\n} from \"../archives.js\";\nimport {\n UsageError,\n emitJson,\n emitJsonCollection,\n relTime,\n resolveWorkstream,\n truncate,\n} from \"../cli.js\";\nimport type { Db } from \"../db.js\";\nimport { exportArchive } from \"../exporting.js\";\nimport { type NextStep, muTable, pc, printNextSteps } from \"../output.js\";\nimport { captureSnapshot } from \"../snapshots.js\";\nimport { destroyWorkstream } from \"../workstream.js\";\n\n// ─── helpers ─────────────────────────────────────────────────────────\n\n/** Render an ISO timestamp as \"<rel> ago\" relative to now. Empty string\n * for an unset value. Matches the relative-time idiom used by\n * `mu task list --sort recency`. */\nfunction ago(iso: string | null): string {\n if (!iso) return \"\";\n const ms = Date.now() - new Date(iso).getTime();\n if (Number.isNaN(ms)) return \"\";\n return `${relTime(ms)} ago`;\n}\n\n// ─── create ──────────────────────────────────────────────────────────\n\nexport async function cmdArchiveCreate(\n db: Db,\n label: string,\n opts: { description?: string; json?: boolean } = {},\n): Promise<void> {\n const archive: Archive = createArchive(db, label, opts.description);\n const nextSteps: NextStep[] = [\n {\n intent: \"Add a workstream's task graph to this archive\",\n command: `mu archive add ${label} -w <workstream>`,\n },\n { intent: \"List every archive on this machine\", command: \"mu archive list\" },\n ];\n if (opts.json) {\n emitJson({ archive, nextSteps });\n return;\n }\n console.log(\n `Created archive ${pc.bold(label)}${\n archive.description ? pc.dim(` — ${archive.description}`) : \"\"\n }`,\n );\n printNextSteps(nextSteps);\n}\n\n// ─── list ────────────────────────────────────────────────────────────\n\nexport async function cmdArchiveList(db: Db, opts: { json?: boolean } = {}): Promise<void> {\n const rows = listArchives(db);\n if (opts.json) {\n emitJsonCollection(rows);\n return;\n }\n if (rows.length === 0) {\n console.log(pc.dim(\"(no archives)\"));\n printNextSteps([\n {\n intent: \"Create one (operator-named bucket; outlives any workstream)\",\n command: 'mu archive create <label> --description \"...\"',\n },\n ]);\n return;\n }\n // Cap label + sources columns; tasks/created/last_added are short\n // fixed-shape values.\n const LABEL_BUDGET = 32;\n const SOURCES_BUDGET = 40;\n const table = muTable({\n head: [\"label\", \"tasks\", \"sources\", \"created\", \"last_added\"].map((h) => pc.bold(h)),\n colWidths: [LABEL_BUDGET, null, SOURCES_BUDGET, null, null],\n });\n for (const r of rows) {\n const sources =\n r.sourceWorkstreams.length === 0\n ? pc.dim(\"—\")\n : r.sourceWorkstreams.map((s) => s.name).join(\", \");\n table.push([\n r.label,\n String(r.totalTasks),\n sources,\n pc.dim(ago(r.createdAt) || r.createdAt),\n pc.dim(ago(r.lastAddedAt) || r.lastAddedAt),\n ]);\n }\n console.log(table.toString());\n printNextSteps([\n { intent: \"Inspect one archive's contents\", command: \"mu archive show <label>\" },\n {\n intent: \"Append a workstream's task graph (idempotent on re-run)\",\n command: \"mu archive add <label> -w <workstream>\",\n },\n ]);\n}\n\n// ─── show ────────────────────────────────────────────────────────────\n\nexport async function cmdArchiveShow(\n db: Db,\n label: string,\n opts: { json?: boolean } = {},\n): Promise<void> {\n const summary: ArchiveSummary = getArchive(db, label);\n if (opts.json) {\n emitJson(summary);\n return;\n }\n console.log(pc.bold(`archive ${summary.label}`));\n if (summary.description) console.log(` description : ${summary.description}`);\n console.log(` created_at : ${summary.createdAt} ${pc.dim(`(${ago(summary.createdAt)})`)}`);\n console.log(\n ` last_added_at : ${summary.lastAddedAt} ${pc.dim(`(${ago(summary.lastAddedAt)})`)}`,\n );\n console.log(` total_tasks : ${summary.totalTasks}`);\n console.log(` sources : ${summary.sourceWorkstreams.length}`);\n if (summary.sourceWorkstreams.length === 0) {\n console.log(pc.dim(\" (no source workstreams yet — call `mu archive add` to populate)\"));\n } else {\n const table = muTable({\n head: [\"source workstream\", \"tasks\", \"added_at\"].map((h) => pc.bold(h)),\n colWidths: [40, null, null],\n });\n for (const s of summary.sourceWorkstreams) {\n table.push([s.name, String(s.taskCount), pc.dim(`${s.addedAt} (${ago(s.addedAt)})`)]);\n }\n console.log(table.toString());\n }\n printNextSteps([\n {\n intent: \"Append another workstream's task graph (additive accumulation)\",\n command: `mu archive add ${label} -w <workstream>`,\n },\n {\n intent: \"Surgically un-archive one source workstream's contribution\",\n command: `mu archive remove ${label} -w <workstream>`,\n },\n {\n intent: \"Query the underlying rows directly\",\n command: `mu sql \"SELECT * FROM archived_tasks WHERE archive_id=(SELECT id FROM archives WHERE label='${label}')\"`,\n },\n ]);\n}\n\n// ─── add ─────────────────────────────────────────────────────────────\n\nexport async function cmdArchiveAdd(\n db: Db,\n label: string,\n opts: { workstream?: string; destroy?: boolean; json?: boolean },\n): Promise<void> {\n const workstream = await resolveWorkstream(opts.workstream);\n\n // Refuse early if the archive doesn't exist. Phase-2 anti-feature\n // pledge from the task design note: DO NOT auto-create on add.\n // Calling addToArchive throws ArchiveNotFoundError; we still pre-\n // check so the --destroy short-circuit also gets the same loud\n // error before we touch any tmux/workstream state.\n // (addToArchive itself also throws WorkstreamNotFoundError if the\n // source workstream is gone — i.e. you must archive BEFORE\n // destroy.)\n // Note: getArchive throws ArchiveNotFoundError on miss — exactly\n // the precheck we want.\n getArchive(db, label);\n\n const result: AddToArchiveResult = addToArchive(db, label, workstream);\n\n // --destroy cascade. Run AFTER the archive add succeeds so a\n // failed add never destroys the source workstream by mistake.\n // We mirror `mu workstream destroy --yes`'s semantics directly via\n // destroyWorkstream(); we deliberately skip the usual auto-export\n // (the archive already preserves the structured state — exporting\n // a second copy to disk would just be noise).\n let destroyed: Awaited<ReturnType<typeof destroyWorkstream>> | undefined;\n if (opts.destroy) {\n destroyed = await destroyWorkstream(db, { workstream });\n }\n\n if (opts.json) {\n emitJson({\n archiveLabel: label,\n sourceWorkstream: workstream,\n ...result,\n destroyed: opts.destroy ? { ranDestroy: true, ...(destroyed ?? {}) } : { ranDestroy: false },\n });\n return;\n }\n\n console.log(\n `Added ${pc.bold(workstream)} to archive ${pc.bold(label)} ${pc.dim(\n `(tasks=${result.addedTasks}, edges=${result.addedEdges}, notes=${result.addedNotes}, events=${result.addedEvents}, skipped_existing=${result.skippedTasks})`,\n )}`,\n );\n if (destroyed) {\n console.log(\n `Destroyed source workstream ${pc.bold(workstream)} ${pc.dim(\n `(killed tmux=${destroyed.killedTmux}, agents=${destroyed.deletedAgents}, tasks=${destroyed.deletedTasks}, workspaces=${destroyed.freedWorkspaces})`,\n )}`,\n );\n if (destroyed.failedWorkspaces.length > 0) {\n console.log(\n pc.yellow(\n `WARNING: ${destroyed.failedWorkspaces.length} workspace(s) could not be freed cleanly; see \\`mu workspace orphans\\` for cleanup.`,\n ),\n );\n }\n }\n printNextSteps([\n { intent: \"Inspect the archive\", command: `mu archive show ${label}` },\n {\n intent: \"Re-running on the same workstream is a no-op (idempotent)\",\n command: `mu archive add ${label} -w ${workstream}`,\n },\n {\n intent: opts.destroy\n ? \"Undo the destroy (DB only; tmux NOT rolled back)\"\n : \"Destroy the source workstream now that its memory is preserved\",\n command: opts.destroy\n ? \"mu undo --yes\"\n : `mu archive add ${label} -w ${workstream} --destroy`,\n },\n ]);\n}\n\n// ─── remove ──────────────────────────────────────────────────────────\n\nexport async function cmdArchiveRemove(\n db: Db,\n label: string,\n opts: { workstream?: string; json?: boolean },\n): Promise<void> {\n const workstream = await resolveWorkstream(opts.workstream);\n const result = removeFromArchive(db, label, workstream);\n if (opts.json) {\n emitJson({ archiveLabel: label, sourceWorkstream: workstream, ...result });\n return;\n }\n console.log(\n `Removed ${pc.bold(workstream)} from archive ${pc.bold(label)} ${pc.dim(\n `(tasks=${result.removedTasks}, edges=${result.removedEdges}, notes=${result.removedNotes}, events=${result.removedEvents})`,\n )}`,\n );\n if (result.removedTasks === 0 && result.removedEvents === 0) {\n console.log(pc.dim(`(${workstream} did not contribute to ${label}; no rows removed)`));\n }\n printNextSteps([\n { intent: \"Inspect what's left\", command: `mu archive show ${label}` },\n {\n intent:\n \"Re-add the workstream (rows are gone from the live DB; this requires it still exists)\",\n command: `mu archive add ${label} -w ${workstream}`,\n },\n ]);\n}\n\n// ─── delete ──────────────────────────────────────────────────────────\n\nexport async function cmdArchiveDelete(\n db: Db,\n label: string,\n opts: { yes?: boolean; json?: boolean } = {},\n): Promise<void> {\n // Always confirm the archive exists FIRST so dry-run + real-run\n // both raise the same typed error on miss.\n const summary = getArchive(db, label);\n\n if (!opts.yes) {\n if (opts.json) {\n emitJson({\n archiveLabel: label,\n deleted: false,\n dryRun: true,\n summary,\n nextSteps: [\n {\n intent: \"Confirm and actually delete (a snapshot is taken first)\",\n command: `mu archive delete ${label} --yes`,\n },\n ],\n });\n return;\n }\n console.log(pc.bold(`About to delete archive ${label}`));\n console.log(` total_tasks : ${summary.totalTasks}`);\n console.log(` sources : ${summary.sourceWorkstreams.length}`);\n for (const s of summary.sourceWorkstreams) {\n console.log(` - ${s.name}: ${s.taskCount} task(s) (added ${s.addedAt})`);\n }\n console.log(\"\");\n console.log(pc.dim(\"(dry-run; rerun with --yes to actually delete)\"));\n console.log(\n pc.dim(\n \"A snapshot will be taken before the delete; `mu undo --yes` reverts the DB (archives + every other table).\",\n ),\n );\n printNextSteps([\n {\n intent: \"Confirm and actually delete\",\n command: `mu archive delete ${label} --yes`,\n },\n {\n intent: \"Surgically remove a single source workstream instead\",\n command: `mu archive remove ${label} -w <workstream>`,\n },\n ]);\n return;\n }\n\n // Pre-delete snapshot. Mirror of destroyWorkstream's pre-mutation\n // captureSnapshot — gives `mu undo --yes` as the recovery path.\n // workstream=null because archives are machine-wide (not per-ws).\n captureSnapshot(db, `archive delete ${label}`, null);\n\n deleteArchive(db, label);\n\n if (opts.json) {\n emitJson({\n archiveLabel: label,\n deleted: true,\n removedSources: summary.sourceWorkstreams.length,\n removedTasks: summary.totalTasks,\n nextSteps: [\n {\n intent: \"Undo (a snapshot was taken before the delete)\",\n command: \"mu undo --yes\",\n },\n ],\n });\n return;\n }\n console.log(\n `Deleted archive ${pc.bold(label)} ${pc.dim(\n `(removed ${summary.totalTasks} task(s) across ${summary.sourceWorkstreams.length} source workstream(s))`,\n )}`,\n );\n printNextSteps([\n {\n intent: \"Undo (a snapshot was taken before the delete)\",\n command: \"mu undo --yes\",\n },\n ]);\n}\n\n// ─── search ───────────────────────────────────────────────────────\n//\n// `mu archive search <pattern>` — LIKE-search across archived task\n// titles AND archived note content. The SDK (searchArchives) does\n// the heavy lifting; this is a thin formatter. Empty patterns are\n// rejected at the CLI boundary with UsageError so the SDK doesn't\n// have to embed CLI-flavoured error messages.\n\nexport async function cmdArchiveSearch(\n db: Db,\n pattern: string,\n opts: { label?: string; limit?: string; json?: boolean } = {},\n): Promise<void> {\n if (pattern.trim().length === 0) {\n throw new UsageError(\"search pattern is required\");\n }\n const limit = opts.limit !== undefined ? Number(opts.limit) : undefined;\n if (limit !== undefined && (Number.isNaN(limit) || limit < 1)) {\n throw new UsageError(`--limit must be a positive integer (got ${JSON.stringify(opts.limit)})`);\n }\n const hits: ArchiveSearchHit[] = searchArchives(db, {\n pattern,\n label: opts.label,\n limit,\n });\n\n if (opts.json) {\n emitJsonCollection(hits);\n return;\n }\n\n const nextSteps: NextStep[] = [\n {\n intent: \"Inspect a specific archive by label\",\n command: \"mu archive show <label>\",\n },\n {\n intent: \"Pull raw archived rows directly\",\n command:\n \"mu sql \\\"SELECT * FROM archived_tasks t JOIN archives a ON a.id=t.archive_id WHERE LOWER(t.title) LIKE '%PATTERN%'\\\"\",\n },\n ];\n\n if (hits.length === 0) {\n console.log(pc.dim(\"(no matches)\"));\n printNextSteps(nextSteps);\n return;\n }\n\n // Snippet column gets the bulk of the table; archive label / id\n // are short fixed-shape values. The snippet itself is already\n // capped at ~120 chars by the SDK; truncate further to keep the\n // table readable on standard 80–120 col terminals.\n const SNIPPET_BUDGET = 60;\n const TITLE_BUDGET = 32;\n const table = muTable({\n head: [\"archive\", \"source-ws\", \"id\", \"kind\", \"title\", \"snippet\"].map((h) => pc.bold(h)),\n colWidths: [20, 20, 16, 8, TITLE_BUDGET, SNIPPET_BUDGET],\n });\n for (const h of hits) {\n table.push([\n h.archiveLabel,\n h.sourceWorkstream,\n h.originalLocalId,\n h.matchKind === \"title\" ? pc.cyan(\"title\") : pc.dim(\"note\"),\n truncate(h.title, TITLE_BUDGET - 2),\n pc.dim(truncate(h.matchSnippet, SNIPPET_BUDGET - 2)),\n ]);\n }\n console.log(table.toString());\n console.log(pc.dim(`(${hits.length} hit(s))`));\n printNextSteps(nextSteps);\n}\n\n// ─── export ───────────────────────────────────────────────────────\n//\n// `mu archive export <label> --out <bucket>` — render every source\n// workstream in an archive to a bucket directory using the unified\n// renderer in src/exporting.ts. Same disk shape as `mu workstream\n// export`, just with N source-ws subdirs (one per archived source).\n\nexport async function cmdArchiveExport(\n db: Db,\n label: string,\n opts: { out?: string; json?: boolean } = {},\n): Promise<void> {\n if (!opts.out || opts.out.trim().length === 0) {\n throw new UsageError(\"--out <dir> is required for `mu archive export`\");\n }\n // exportArchive throws ArchiveNotFoundError (via listArchivedTasks)\n // before any disk I/O — classifyError maps it the same way for\n // both the JSON and prose error paths.\n const result = exportArchive(db, { label, outDir: opts.out });\n const totalTasks = Object.values(result.manifest.sources).reduce(\n (acc, s) => acc + s.tasks.length,\n 0,\n );\n const nextSteps: NextStep[] = [\n { intent: \"Browse the bucket\", command: `ls ${result.outDir}` },\n {\n intent: \"Re-export to refresh (additive; existing source-ws subdirs untouched)\",\n command: `mu archive export ${label} --out ${result.outDir}`,\n },\n {\n intent: \"Track in git\",\n command: `(cd ${result.outDir} && git init && git add . && git commit -m '${label} export')`,\n },\n ];\n if (opts.json) {\n emitJson({\n archiveLabel: label,\n outDir: result.outDir,\n sourceCount: result.sourceCount,\n totalTasks,\n written: result.written,\n unchanged: result.unchanged,\n preserved: result.preserved,\n manifestPath: result.manifestPath,\n manifest: result.manifest,\n nextSteps,\n });\n return;\n }\n console.log(\n `Exported archive ${pc.bold(label)} → ${pc.bold(result.outDir)} ${pc.dim(\n `(sources=${result.sourceCount}, tasks=${totalTasks}, written=${result.written}, unchanged=${result.unchanged}, preserved=${result.preserved})`,\n )}`,\n );\n printNextSteps(nextSteps);\n}\n\n// ─── commander wiring ────────────────────────────────────────────────\n//\n// wireArchiveCommands is called by buildProgram() in src/cli.ts. Wired\n// here so every per-namespace builder lives next to its cmd functions.\n\nimport type { Command } from \"commander\";\nimport { JSON_OPT, WORKSTREAM_OPT, handle } from \"../cli.js\";\n\n// The classifyError() switch in src/cli.ts is the single source of\n// truth for typed-error → exit-code mapping; the new Archive*Error\n// classes are wired there. This module just throws them via the SDK.\n// Re-exports here let test files (and downstream skills) import from\n// one consistent place.\nexport { ArchiveAlreadyExistsError, ArchiveLabelInvalidError, ArchiveNotFoundError };\n\nexport function wireArchiveCommands(program: Command): void {\n const archive = program\n .command(\"archive\")\n .description(\n \"Cross-workstream preservation of task graphs. An archive is an operator-named bucket that outlives every source workstream and accumulates additively.\",\n );\n\n archive\n .command(\"create <label>\")\n .description(\n \"Create a new (empty) archive bucket. Labels are globally unique on this machine; populate via `mu archive add`.\",\n )\n .option(\"--description <text>\", \"optional one-liner describing what the archive is for\")\n .option(...JSON_OPT)\n .action(function (label: string) {\n const opts = (this as Command).opts() as {\n description?: string;\n json?: boolean;\n };\n return handle((db) => cmdArchiveCreate(db, label, opts), this as Command)();\n });\n\n archive\n .command(\"list\")\n .description(\"List every archive on this machine, with per-source-workstream summary.\")\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as { json?: boolean };\n return handle((db) => cmdArchiveList(db, opts), this as Command)();\n });\n\n archive\n .command(\"show <label>\")\n .description(\n \"Detail card for one archive: description, timestamps, total task count, per-source-workstream breakdown.\",\n )\n .option(...JSON_OPT)\n .action(function (label: string) {\n const opts = (this as Command).opts() as { json?: boolean };\n return handle((db) => cmdArchiveShow(db, label, opts), this as Command)();\n });\n\n archive\n .command(\"add <label>\")\n .description(\n \"Snapshot a workstream's task graph (tasks + edges + notes + events) into an existing archive. Idempotent at (archive, source_workstream) granularity. With --destroy, cascades to `mu workstream destroy --yes` after the archive succeeds.\",\n )\n .option(...WORKSTREAM_OPT)\n .option(\n \"--destroy\",\n \"After a successful archive, also destroy the source workstream (kills tmux + frees workspaces + cascade-deletes DB rows).\",\n )\n .option(...JSON_OPT)\n .action(function (label: string) {\n // optsWithGlobals so a top-level `-w` (e.g. `mu -w foo archive\n // add bar`) propagates here. opts() alone would only see\n // `-w` when it appears AFTER the subcommand. Mirrors\n // wireSelfCommands' adopt verb in src/cli/agents.ts.\n const opts = (this as Command).optsWithGlobals() as {\n workstream?: string;\n destroy?: boolean;\n json?: boolean;\n };\n return handle((db) => cmdArchiveAdd(db, label, opts), this as Command)();\n });\n\n archive\n .command(\"remove <label>\")\n .description(\n \"Surgically remove a single source workstream's contribution from an archive (rare; recovery). Other source workstreams' rows are untouched.\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (label: string) {\n // optsWithGlobals: see archive add for the rationale.\n const opts = (this as Command).optsWithGlobals() as {\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdArchiveRemove(db, label, opts), this as Command)();\n });\n\n archive\n .command(\"search <pattern>\")\n .description(\n \"LIKE-search archived task titles AND archived note content across every archive (or a single one with --label). Pattern is bound as a SQL parameter; SQL-injection-safe.\",\n )\n .option(\"--label <label>\", \"restrict to one archive (throws ArchiveNotFoundError on miss)\")\n .option(\"--limit <n>\", \"cap on results (default 50)\")\n .option(...JSON_OPT)\n .action(function (pattern: string) {\n const opts = (this as Command).opts() as {\n label?: string;\n limit?: string;\n json?: boolean;\n };\n return handle((db) => cmdArchiveSearch(db, pattern, opts), this as Command)();\n });\n\n archive\n .command(\"export <label>\")\n .description(\n \"Render every source workstream in an archive to a bucket directory of markdown (one subdir per source-ws + bucket-level README/INDEX/manifest). Idempotent + additive: re-running refreshes only changed task files.\",\n )\n .option(\"--out <dir>\", \"output directory (the bucket); required\")\n .option(...JSON_OPT)\n .action(function (label: string) {\n const opts = (this as Command).opts() as { out?: string; json?: boolean };\n return handle((db) => cmdArchiveExport(db, label, opts), this as Command)();\n });\n\n archive\n .command(\"delete <label>\")\n .description(\n \"Delete an entire archive and every row that references it. Two-phase: bare invocation prints a dry-run summary; --yes captures a snapshot first then deletes.\",\n )\n .option(\"-y, --yes\", \"actually delete (without this flag, prints a dry-run summary)\")\n .option(...JSON_OPT)\n .action(function (label: string) {\n const opts = (this as Command).opts() as { yes?: boolean; json?: boolean };\n return handle((db) => cmdArchiveDelete(db, label, opts), this as Command)();\n });\n}\n","// mu — `mu doctor` diagnostic verb (human + --json forms).\n//\n// Reports:\n// - environment: tmux, $TMUX, $TMUX_PANE, $MU_SESSION\n// - db: schema integrity, schema_version, journal_mode, foreign_keys\n// - workstream: auto-detected current workstream\n// - state: per-workstream agent / task / log counts + reconcile drift\n//\n// Read-only (the reconcile pass uses mode: \"report-only\" so polling doesn't\n// race in-flight spawns; see bug_agent_spawn_workspace_fk_failure).\n//\n// Extracted from src/cli.ts as part of refactor_split_large_src_files.\n\nimport { listLiveAgents } from \"../agents.js\";\nimport { emitJson, resolveWorkstream } from \"../cli.js\";\nimport { CURRENT_SCHEMA_VERSION, type Db, EXPECTED_TABLES, defaultDbPath } from \"../db.js\";\nimport { pc } from \"../output.js\";\nimport { tmux } from \"../tmux.js\";\nimport { summarizeWorkstream } from \"../workstream.js\";\n\nexport async function cmdDoctor(db: Db, opts: { json?: boolean } = {}): Promise<void> {\n if (opts.json) {\n return cmdDoctorJson(db);\n }\n console.log(pc.bold(\"mu doctor\"));\n\n // ─ Environment\n console.log(pc.bold(\"\\nenvironment\"));\n try {\n const version = (await tmux([\"-V\"])).trim();\n console.log(` tmux : ${pc.green(\"ok\")} (${version})`);\n } catch {\n console.log(` tmux : ${pc.red(\"NOT FOUND\")} — install tmux ≥ 3.0`);\n }\n console.log(` $TMUX : ${process.env.TMUX ? pc.green(\"set\") : pc.yellow(\"not set\")}`);\n console.log(\n ` $TMUX_PANE : ${process.env.TMUX_PANE ? pc.green(process.env.TMUX_PANE) : pc.dim(\"not set\")}`,\n );\n console.log(\n ` $MU_SESSION : ${process.env.MU_SESSION ? pc.green(process.env.MU_SESSION) : pc.dim(\"not set\")}`,\n );\n\n // ─ DB + schema\n console.log(pc.bold(\"\\ndb\"));\n console.log(` path : ${pc.dim(defaultDbPath())}`);\n try {\n const tables = (\n db\n .prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name\",\n )\n .all() as { name: string }[]\n ).map((r) => r.name);\n const missing = EXPECTED_TABLES.filter((t) => !tables.includes(t));\n if (missing.length === 0) {\n console.log(` schema : ${pc.green(\"ok\")} (${EXPECTED_TABLES.length} tables)`);\n } else {\n console.log(` schema : ${pc.red(\"missing\")} — ${missing.join(\", \")}`);\n }\n // Schema version: should match CURRENT_SCHEMA_VERSION after openDb\n // (which runs migrations). Mismatch means either a downgrade\n // attempt or a bug in the migration runner — either way, surface it.\n try {\n const row = db.prepare(\"SELECT version FROM schema_version WHERE id = 1\").get() as\n | { version: number }\n | undefined;\n const v = row?.version;\n if (v === undefined) {\n console.log(\n ` schema_version : ${pc.red(\"missing row\")} (expected ${CURRENT_SCHEMA_VERSION})`,\n );\n } else if (v === CURRENT_SCHEMA_VERSION) {\n console.log(` schema_version : ${pc.green(String(v))}`);\n } else if (v < CURRENT_SCHEMA_VERSION) {\n console.log(\n ` schema_version : ${pc.yellow(String(v))} (code expects ${CURRENT_SCHEMA_VERSION}; openDb should have migrated)`,\n );\n } else {\n console.log(\n ` schema_version : ${pc.red(String(v))} (code expects ${CURRENT_SCHEMA_VERSION}; possible downgrade or future-version DB)`,\n );\n }\n } catch {\n console.log(\n ` schema_version : ${pc.red(\"unreadable\")} (schema_version table missing or wrong shape)`,\n );\n }\n const journal = db.pragma(\"journal_mode\", { simple: true });\n console.log(\n ` journal_mode : ${journal === \"wal\" ? pc.green(String(journal)) : pc.yellow(String(journal))}`,\n );\n const fk = db.pragma(\"foreign_keys\", { simple: true });\n console.log(` foreign_keys : ${fk === 1 ? pc.green(\"on\") : pc.red(`off (${fk})`)}`);\n } catch (err) {\n console.log(\n ` schema : ${pc.red(\"FAIL\")} — ${err instanceof Error ? err.message : err}`,\n );\n }\n\n // ─ Workstream auto-detect\n console.log(pc.bold(\"\\nworkstream\"));\n let currentWorkstream: string | null = null;\n try {\n currentWorkstream = await resolveWorkstream();\n console.log(` current : ${pc.green(currentWorkstream)}`);\n } catch {\n console.log(\n ` current : ${pc.yellow(\"none\")} (set $MU_SESSION, cd into an mu-<name> tmux session, or pass -w to a subcommand)`,\n );\n }\n\n // ─ Per-workstream stats (current only; --all stretch)\n if (currentWorkstream) {\n const ws = currentWorkstream;\n console.log(pc.bold(`\\nstate (workstream=${ws})`));\n const summary = await summarizeWorkstream(db, { workstream: ws });\n const counts = {\n agents: summary.agentCount,\n tasks: summary.taskCount,\n ready: countReady(db, ws),\n blocked: countBlocked(db, ws),\n inProgress: countInProgressByWorkstream(db, ws),\n logs: countLogsByWorkstream(db, ws),\n };\n console.log(` agents : ${counts.agents}`);\n console.log(\n ` tasks : ${counts.tasks} (ready ${counts.ready}, blocked ${counts.blocked}, in-progress ${counts.inProgress})`,\n );\n console.log(` agent_logs rows : ${counts.logs}`);\n\n // Reconciliation: ghost detection (DB rows with dead panes) + orphans.\n // mu doctor is diagnostic — mode: \"report-only\" so it never\n // deletes rows AND never writes to the DB / tmux titles just for\n // being polled (would race in-flight spawns; see\n // bug_agent_spawn_workspace_fk_failure).\n try {\n const view = await listLiveAgents(db, { workstream: ws, mode: \"report-only\" });\n const ghostNote =\n view.report.prunedGhosts > 0\n ? pc.yellow(`pruned ${view.report.prunedGhosts} during this check`)\n : pc.green(\"none\");\n console.log(` ghosts : ${ghostNote}`);\n const orphanColor = view.orphans.length > 0 ? pc.yellow : pc.green;\n console.log(\n ` orphan panes : ${orphanColor(String(view.orphans.length))}${view.orphans.length > 0 ? pc.dim(\" (run `mu agent list` to see them)\") : \"\"}`,\n );\n } catch (err) {\n console.log(\n ` reconcile : ${pc.dim(\"skipped\")} (${err instanceof Error ? err.message : err})`,\n );\n }\n }\n}\n\n/**\n * JSON form of `mu doctor`. Same checks the human form runs, collected\n * into a single structured record for piping. Surfaces 'ok' / 'warn' /\n * 'fail' for each subsystem so callers can match on a single field.\n */\nexport async function cmdDoctorJson(db: Db): Promise<void> {\n // environment\n let tmuxVersion: string | null = null;\n let tmuxOk = false;\n try {\n tmuxVersion = (await tmux([\"-V\"])).trim();\n tmuxOk = true;\n } catch {\n tmuxOk = false;\n }\n const env = {\n tmux: { ok: tmuxOk, version: tmuxVersion },\n TMUX: process.env.TMUX ?? null,\n TMUX_PANE: process.env.TMUX_PANE ?? null,\n MU_SESSION: process.env.MU_SESSION ?? null,\n };\n\n // db / schema\n let dbReport: Record<string, unknown>;\n try {\n const tables = (\n db\n .prepare(\n \"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name\",\n )\n .all() as { name: string }[]\n ).map((r) => r.name);\n const missing = EXPECTED_TABLES.filter((t) => !tables.includes(t));\n let schemaVersion: number | null = null;\n let schemaVersionStatus: \"ok\" | \"missing\" | \"stale\" | \"future\" | \"unreadable\";\n try {\n const row = db.prepare(\"SELECT version FROM schema_version WHERE id = 1\").get() as\n | { version: number }\n | undefined;\n const v = row?.version;\n if (v === undefined) schemaVersionStatus = \"missing\";\n else {\n schemaVersion = v;\n if (v === CURRENT_SCHEMA_VERSION) schemaVersionStatus = \"ok\";\n else if (v < CURRENT_SCHEMA_VERSION) schemaVersionStatus = \"stale\";\n else schemaVersionStatus = \"future\";\n }\n } catch {\n schemaVersionStatus = \"unreadable\";\n }\n const journal = db.pragma(\"journal_mode\", { simple: true });\n const fk = db.pragma(\"foreign_keys\", { simple: true });\n dbReport = {\n path: defaultDbPath(),\n schema: { ok: missing.length === 0, expected: EXPECTED_TABLES, missing, present: tables },\n schemaVersion: {\n value: schemaVersion,\n expected: CURRENT_SCHEMA_VERSION,\n status: schemaVersionStatus,\n },\n journalMode: journal,\n foreignKeys: fk === 1,\n };\n } catch (err) {\n dbReport = { error: err instanceof Error ? err.message : String(err) };\n }\n\n // workstream\n let currentWorkstream: string | null = null;\n try {\n currentWorkstream = await resolveWorkstream();\n } catch {\n currentWorkstream = null;\n }\n\n // per-workstream stats (only when resolvable)\n let workstreamStats: Record<string, unknown> | null = null;\n if (currentWorkstream) {\n const ws = currentWorkstream;\n const summary = await summarizeWorkstream(db, { workstream: ws });\n const counts = {\n agents: summary.agentCount,\n tasks: summary.taskCount,\n ready: countReady(db, ws),\n blocked: countBlocked(db, ws),\n inProgress: countInProgressByWorkstream(db, ws),\n logs: countLogsByWorkstream(db, ws),\n };\n let reconcile: Record<string, unknown> | null = null;\n try {\n // mu doctor --json: report-only for the same reason as the human path.\n const view = await listLiveAgents(db, { workstream: ws, mode: \"report-only\" });\n reconcile = {\n prunedGhosts: view.report.prunedGhosts,\n orphanCount: view.orphans.length,\n };\n } catch (err) {\n reconcile = { skipped: true, reason: err instanceof Error ? err.message : String(err) };\n }\n workstreamStats = { workstreamName: ws, ...counts, reconcile };\n }\n\n emitJson({\n environment: env,\n db: dbReport,\n workstream: { currentName: currentWorkstream },\n state: workstreamStats,\n });\n}\n\n// agents/tasks counts come from summarizeWorkstream() (src/workstream.ts) —\n// it already runs the same JOIN-on-workstreams SELECTs for its summary, so\n// we don't keep a second copy of those queries here. The four helpers below\n// (in-progress / logs / ready / blocked) are doctor-only views that\n// summarizeWorkstream doesn't expose.\nfunction countInProgressByWorkstream(db: Db, workstream: string): number {\n return (\n db\n .prepare(\n `SELECT COUNT(*) AS n FROM tasks t\n JOIN workstreams ws ON ws.id = t.workstream_id\n WHERE ws.name = ? AND t.status = 'IN_PROGRESS'`,\n )\n .get(workstream) as { n: number }\n ).n;\n}\nfunction countLogsByWorkstream(db: Db, workstream: string): number {\n return (\n db\n .prepare(\n `SELECT COUNT(*) AS n FROM agent_logs l\n LEFT JOIN workstreams ws ON ws.id = l.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number }\n ).n;\n}\nfunction countReady(db: Db, workstream: string): number {\n return (\n db\n .prepare(\n `SELECT COUNT(*) AS n FROM ready v\n JOIN workstreams ws ON ws.id = v.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number }\n ).n;\n}\nfunction countBlocked(db: Db, workstream: string): number {\n return (\n db\n .prepare(\n `SELECT COUNT(*) AS n FROM blocked v\n JOIN workstreams ws ON ws.id = v.workstream_id\n WHERE ws.name = ?`,\n )\n .get(workstream) as { n: number }\n ).n;\n}\n\n// ─── commander wiring ────────────────────────────────────────────────\n//\n// wireDoctorCommand is called by buildProgram() in src/cli.ts. Wired here so\n// every per-namespace builder lives next to its cmd functions.\n\nimport type { Command } from \"commander\";\nimport { JSON_OPT, handle } from \"../cli.js\";\n\nexport function wireDoctorCommand(program: Command): void {\n program\n .command(\"doctor\")\n .description(\"Environment + state health check\")\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as { json?: boolean };\n return handle((db) => cmdDoctor(db, opts), this as Command)();\n });\n}\n","// mu — typed-error → exit-code mapping + CLI handler wrapper.\n//\n// Extracted from src/cli.ts (review_cli_ts_past_refactor_signal):\n// classifyError + emitError + handle were ~150 LOC in the middle of\n// cli.ts. They live here so cli.ts is just argument parsing /\n// workstream resolution / program wiring; the typed-error catalogue\n// belongs next to its own helper. cli.ts re-exports `classifyError`\n// and `handle` for back-compat with existing tests + cli/* importers.\n//\n// Exit codes (from VOCABULARY.md / ARCHITECTURE.md):\n// 0 = success\n// 1 = generic error\n// 2 = usage error (validation / missing-flag / type / mutex /\n// unknown subcommand) — commander-detected mistakes AND\n// handler-thrown UsageError. Both surfaces print the failing\n// subcommand's --help (human path) or include a structured\n// `usage` field (--json path). See `audit_cli_validation_uniformity`.\n// 3 = not found (no such agent / task / pane)\n// 4 = conflict (name collision, double-claim, cycle, etc.)\n// 5 = substrate unavailable (tmux not running, DB locked)\n// 6 = REAPER_DETECTED — `mu task wait` aborted because the\n// per-poll reconciler flipped a watched task IN_PROGRESS →\n// OPEN (the owning pane was dead). Only fires when the wait\n// target is CLOSED.\n// 7 = STALL_DETECTED — `mu task wait --on-stall exit` aborted\n// because the existing --stuck-after predicate fired on a\n// watched task. Same target=CLOSED carve-out as exit 6; if\n// both fire in the same poll iteration, exit 6 wins.\n\nimport { type Command, CommanderError } from \"commander\";\nimport {\n AgentDiedOnSpawnError,\n AgentExistsError,\n AgentNotFoundError,\n AgentNotInWorkstreamError,\n WorkspacePreservedError,\n} from \"../agents.js\";\nimport {\n ArchiveAlreadyExistsError,\n ArchiveLabelInvalidError,\n ArchiveNotFoundError,\n} from \"../archives.js\";\nimport { type Db, SchemaTooOldError, WorkstreamNotFoundError, openDb } from \"../db.js\";\nimport { LegacyExportLayoutError } from \"../exporting.js\";\nimport {\n ImportBucketInvalidError,\n ImportEdgeRefMissingError,\n ImportFrontmatterParseError,\n ImportLegacyLayoutError,\n ImportSourceNotInBucketError,\n WorkstreamAlreadyExistsError,\n} from \"../importing.js\";\nimport {\n type NextStep,\n type UsageJson,\n hasNextSteps,\n isJsonMode,\n pc,\n printNextStepsTo,\n printUsageHuman,\n renderUsageJson,\n} from \"../output.js\";\nimport {\n PruneOptionsInvalidError,\n SnapshotFileMissingError,\n SnapshotNotFoundError,\n SnapshotVersionMismatchError,\n} from \"../snapshots.js\";\nimport {\n ClaimerNotRegisteredError,\n CrossWorkstreamEdgeError,\n CycleError,\n ReaperDetectedDuringWaitError,\n StallDetectedDuringWaitError,\n TaskAlreadyOwnedError,\n TaskExistsError,\n TaskHasOpenDependentsError,\n TaskIdInvalidError,\n TaskNotFoundError,\n TaskNotInWorkstreamError,\n} from \"../tasks.js\";\nimport { PaneNotFoundError, TmuxError } from \"../tmux.js\";\nimport { WorkspaceConflictError, WorkspaceDirtyError, WorkspaceVcsRequiredError } from \"../vcs.js\";\nimport {\n HomeDirAsProjectRootError,\n WorkspaceExistsError,\n WorkspaceNotFoundError,\n WorkspacePathNotEmptyError,\n} from \"../workspace.js\";\nimport { WorkstreamNameInvalidError } from \"../workstream.js\";\n\nexport class UsageError extends Error {\n override readonly name = \"UsageError\";\n}\n\n// ─── Active-command tracking for the validation-error contract ──────\n//\n// `setActiveCommand` is called by `handle()` at the entry of every\n// .action() body. `emitError()` reads it to render the failing\n// subcommand's --help on usage errors. We thread it via module-local\n// state (mirror of `isJsonMode` reading process.argv) instead of\n// constructor-arg-ing every UsageError throw site — there are 27 of\n// them, several inside helpers (resolveWorkstream, parseStatusFilter)\n// that don't know which subcommand called them.\n//\n// Cleared in handle()'s finally so a sequence of test calls (which\n// re-use the process) doesn't bleed state across cases.\n\nlet activeCommand: Command | undefined;\n\n/** Test-only: introspect the current active command. */\nexport function getActiveCommandForTest(): Command | undefined {\n return activeCommand;\n}\n\n/** Test-only: clear stale state between vitest cases. */\nexport function clearActiveCommandForTest(): void {\n activeCommand = undefined;\n}\n\n/** Map a commander.CommanderError to (exitCode, label). The codes that\n * represent successful early-exits (--help, --version) are mapped to\n * exitCode 0 and a sentinel label so emitError can short-circuit\n * without printing anything. All other codes are uniform exit 2 —\n * the documented \"usage error\" lane that handler-thrown UsageError\n * also occupies. */\nfunction classifyCommanderError(err: CommanderError): { label: string; exitCode: number } {\n if (\n err.code === \"commander.helpDisplayed\" ||\n err.code === \"commander.help\" ||\n err.code === \"commander.version\"\n ) {\n return { label: \"help\", exitCode: 0 };\n }\n // Everything else is a usage mistake (missing required option,\n // unknown option, unknown command, missing argument, type-coercion\n // via InvalidArgumentError). Renumber commander's default exit 1\n // to mu's documented exit 2 so the surface is uniform with the\n // handler-thrown UsageError lane.\n return { label: \"error\", exitCode: 2 };\n}\n\n/** Predicate: is this error a usage-class one for which we should\n * render the failing subcommand's --help? Three families:\n * 1. CommanderError (missing required option, unknown option,\n * unknown command, missing argument, type-coercion failure).\n * 2. UsageError (handler-thrown mutex / arity / range checks).\n * 3. Typed *Invalid* domain errors that fault on a value the\n * operator typed at the CLI (workstream name, archive label,\n * task id, prune-flag combination). The verb's --help would\n * have explained the constraint; show it.\n *\n * Deliberately excluded: ImportBucketInvalidError /\n * ImportLegacyLayoutError / LegacyExportLayoutError. Those\n * fault on the contents of a directory the operator pointed at;\n * --help wouldn't have prevented them, and their typed\n * nextSteps already point at the fix.\n */\nfunction isUsageClassError(err: unknown): boolean {\n if (err instanceof CommanderError) return true;\n if (err instanceof UsageError) return true;\n if (\n err instanceof WorkstreamNameInvalidError ||\n err instanceof ArchiveLabelInvalidError ||\n err instanceof PruneOptionsInvalidError ||\n err instanceof TaskIdInvalidError\n ) {\n return true;\n }\n return false;\n}\n\n/**\n * Raised when a bare entity name is used at the CLI but the resolution\n * context (--workstream / $MU_SESSION / current tmux session) is empty\n * AND the same name lives in two or more workstreams. Exit 4 (conflict)\n * via classifyError. The errorNextSteps lists every candidate as a\n * runnable qualified-form invocation — a one-paste fix.\n *\n * See verb_arg_qualified_workstream_name for the design.\n */\nexport class NameAmbiguousError extends Error {\n override readonly name = \"NameAmbiguousError\";\n constructor(\n public readonly entityName: string,\n public readonly candidates: readonly string[],\n public readonly kind: \"task\" | \"agent\" | \"workspace\",\n ) {\n super(\n `${kind} name \"${entityName}\" exists in ${candidates.length} workstreams (${candidates.join(\", \")}); pass -w <workstream> or use the qualified form <workstream>/${entityName}`,\n );\n }\n errorNextSteps(): NextStep[] {\n return this.candidates.map((ws) => ({\n intent: `Target the ${ws} ${this.kind}`,\n command: `${ws}/${this.entityName}`,\n }));\n }\n}\n\n/**\n * Map a typed error to (label, exitCode). The label is the prefix\n * before the message in human output (e.g. \"conflict\", \"not found\");\n * the exit code is what the process exits with.\n *\n * Order matters: more-specific classes first. The fallthrough at the\n * end is the generic exit-1 catch-all.\n */\nexport function classifyError(err: unknown): { label: string; exitCode: number } {\n if (\n err instanceof UsageError ||\n err instanceof WorkstreamNameInvalidError ||\n err instanceof ArchiveLabelInvalidError ||\n err instanceof LegacyExportLayoutError ||\n err instanceof ImportBucketInvalidError ||\n err instanceof ImportLegacyLayoutError ||\n err instanceof ImportFrontmatterParseError ||\n err instanceof ImportEdgeRefMissingError ||\n err instanceof PruneOptionsInvalidError\n ) {\n return { label: \"error\", exitCode: 2 };\n }\n if (\n err instanceof AgentNotFoundError ||\n err instanceof TaskNotFoundError ||\n err instanceof WorkstreamNotFoundError ||\n err instanceof WorkspaceNotFoundError ||\n err instanceof SnapshotNotFoundError ||\n err instanceof ArchiveNotFoundError\n ) {\n // WorkstreamNotFoundError originates inside resolveWorkstreamId\n // (src/db.ts) — it's the canonical resolve-time miss for the\n // first leg of the SDK boundary (operator-name → surrogate id).\n // Without this entry it fell through to generic exit 1, robbing\n // operators of the same -> exit-3 mapping that AgentNotFoundError /\n // TaskNotFoundError get. (schema_v5_cli_boundary)\n return { label: \"not found\", exitCode: 3 };\n }\n if (\n err instanceof NameAmbiguousError ||\n err instanceof AgentExistsError ||\n err instanceof TaskExistsError ||\n err instanceof TaskAlreadyOwnedError ||\n err instanceof TaskNotInWorkstreamError ||\n err instanceof AgentNotInWorkstreamError ||\n err instanceof CycleError ||\n err instanceof TaskHasOpenDependentsError ||\n err instanceof CrossWorkstreamEdgeError ||\n err instanceof WorkspaceExistsError ||\n err instanceof WorkspacePathNotEmptyError ||\n err instanceof WorkspacePreservedError ||\n err instanceof HomeDirAsProjectRootError ||\n err instanceof WorkspaceVcsRequiredError ||\n err instanceof WorkspaceDirtyError ||\n err instanceof ClaimerNotRegisteredError ||\n err instanceof SnapshotVersionMismatchError ||\n err instanceof SchemaTooOldError ||\n err instanceof TaskIdInvalidError ||\n err instanceof ArchiveAlreadyExistsError ||\n err instanceof ImportSourceNotInBucketError ||\n err instanceof WorkstreamAlreadyExistsError\n ) {\n return { label: \"conflict\", exitCode: 4 };\n }\n if (err instanceof AgentDiedOnSpawnError) {\n // Substrate-level failure (CLI exited at spawn). The message is\n // already rich (includes captured scrollback). Generic exit 1.\n return { label: \"spawn failed\", exitCode: 1 };\n }\n if (err instanceof TmuxError || err instanceof PaneNotFoundError) {\n return { label: \"tmux\", exitCode: 5 };\n }\n if (err instanceof WorkspaceConflictError) {\n // Rebase produced conflicts — the operator must `cd` and resolve.\n // Distinct from the typed-conflict-of-state family (exit 4) which\n // we refused before any side effect; here the side effect happened\n // and the workspace is half-rebased. Same exit-code lane as\n // TmuxError (substrate-level: action requires manual recovery).\n return { label: \"workspace conflict\", exitCode: 5 };\n }\n if (err instanceof ReaperDetectedDuringWaitError) {\n // task_wait_reconcile_dead_panes: distinct exit code (6) so\n // operator scripts can branch on \"worker died, not a generic\n // failure\" vs the timeout (5) and the typed conflict family (4).\n return { label: \"reaper\", exitCode: 6 };\n }\n if (err instanceof StallDetectedDuringWaitError) {\n // task_wait_stall_action_flag: distinct exit code (7) so\n // operator scripts can branch on \"worker idle, ambiguous\" vs\n // the unambiguous dead-pane (6). Same precedence rule lives at\n // the wait call site: if both fire in one poll, the\n // ReaperDetectedDuringWaitError throw runs first, so exit 6 wins.\n return { label: \"stall\", exitCode: 7 };\n }\n if (err instanceof SnapshotFileMissingError) {\n // Substrate-level: the .db file is gone but the row still says it\n // should be there. Same flavour as `tmux` errors above.\n return { label: \"snapshot file missing\", exitCode: 5 };\n }\n return { label: \"error\", exitCode: 1 };\n}\n\n/** Render error + nextSteps to stderr and return the resolved exit\n * code. Returning the exitCode lets `handle` reuse it instead of\n * re-classifying the same error twice (review_code_classify_error_called_twice).\n *\n * audit_cli_validation_uniformity: usage-class errors (commander +\n * UsageError + typed *Invalid*) ALSO emit the failing subcommand's\n * --help (human path) or a structured `usage` field (JSON path). */\nfunction emitError(err: unknown): number {\n // Commander's --help / --version success-exits arrive as CommanderError\n // with exitCode 0. helpInformation() has already been written by\n // commander itself; nothing more to do.\n if (err instanceof CommanderError) {\n const { exitCode } = classifyCommanderError(err);\n if (exitCode === 0) return 0;\n }\n\n const message = err instanceof Error ? err.message : String(err);\n const { label, exitCode } =\n err instanceof CommanderError ? classifyCommanderError(err) : classifyError(err);\n const errClass = err instanceof Error ? err.name : \"Error\";\n const steps: NextStep[] = hasNextSteps(err) ? err.errorNextSteps() : [];\n // Strip commander's own \"error: \" prefix — we re-add our own \"error: \"\n // (red, in the human path) and don't want \"error: error: ...\".\n const cleanMessage =\n err instanceof CommanderError && message.startsWith(\"error: \")\n ? message.slice(\"error: \".length)\n : message;\n const usageCmd = isUsageClassError(err) ? activeCommand : undefined;\n const usage: UsageJson | undefined = usageCmd ? renderUsageJson(usageCmd) : undefined;\n\n if (isJsonMode()) {\n const envelope: Record<string, unknown> = {\n error: errClass,\n message: cleanMessage,\n nextSteps: steps,\n exitCode,\n };\n if (usage) envelope.usage = usage;\n process.stderr.write(`${JSON.stringify(envelope)}\\n`);\n return exitCode;\n }\n\n console.error(pc.red(`${label}: ${cleanMessage}`));\n if (steps.length > 0) {\n printNextStepsTo(steps, \"stderr\");\n }\n if (usageCmd) {\n printUsageHuman(usageCmd);\n }\n return exitCode;\n}\n\n/** Wrap an async handler so typed errors become specific exit codes.\n *\n * The optional `command` arg is the failing subcommand's `Command`\n * (commander's `this` in a `.action(function () { ... })` body).\n * When supplied, usage-class errors thrown inside `fn` will render\n * that subcommand's --help (human) or `usage` JSON (--json). */\nexport function handle(fn: (db: Db) => Promise<void>, command?: Command): () => Promise<void> {\n return async () => {\n let db: Db | undefined;\n activeCommand = command;\n try {\n db = openDb();\n await fn(db);\n } catch (err) {\n const exitCode = emitError(err);\n process.exit(exitCode);\n } finally {\n activeCommand = undefined;\n try {\n db?.close();\n } catch {\n // best effort\n }\n }\n };\n}\n\n/** Translate a commander parse-time error into mu's wire format,\n * then return the exit code. Used by the top-level `parseAsync` catch\n * in cli.ts so commander mistakes go through the same emitError\n * pipeline that handler-thrown errors use. */\nexport function emitParseError(err: unknown, failingCommand: Command | undefined): number {\n activeCommand = failingCommand;\n try {\n return emitError(err);\n } finally {\n activeCommand = undefined;\n }\n}\n\n/** Walk argv tokens against the program tree to find the deepest\n * matching subcommand. Used by parseAsync's catch to identify which\n * subcommand commander was processing when it threw. Stops at the\n * first `-` (option) or unknown token. */\nexport function findCommandForArgv(root: Command, argv: readonly string[]): Command {\n let cur: Command = root;\n for (const t of argv) {\n if (t.startsWith(\"-\")) break;\n const next: Command | undefined = cur.commands.find(\n (c) => c.name() === t || (c.aliases().includes(t) ?? false),\n );\n if (!next) break;\n cur = next;\n }\n return cur;\n}\n","// mu — inverse of src/exporting.ts: parse a v0.3 bucket directory\n// (markdown + manifest.json) and rebuild every source workstream as\n// live DB rows.\n//\n// IMPORTS MARKDOWN ONLY. We do not read any .db file. The export\n// shape is the import format: README + INDEX + manifest.json at the\n// bucket root, then one subdir per source workstream containing\n// README + INDEX + tasks/<id>.md. .db cross-machine = snapshot/undo;\n// markdown cross-machine = this module. See task notes for\n// workstream_import_from_markdown for the full rationale.\n//\n// What survives the round trip:\n// - per-task: id (local_id) + title + status + impact + effort_days +\n// created_at + updated_at + blocked_by + blocks + every note.\n// - per-source-ws: a workstream row of the same name (or the\n// --workstream override when the bucket has exactly one source).\n//\n// What does NOT survive (anti-features per the design note):\n// - owner_id (agents aren't exported; we keep the original owner\n// name in the markdown and set owner_id to NULL in the DB).\n// - agents / workspaces / agent_logs (out of scope).\n// - archive labels (separate follow-up if needed).\n//\n// Per source-ws transactionality: every task + edge + note for one\n// source-ws goes into ONE SQLite transaction. If that source-ws\n// fails (bad frontmatter, edge ref to a missing task, ...), only\n// THAT source-ws is rolled back; siblings still import.\n\nimport { existsSync, readFileSync, readdirSync, statSync } from \"node:fs\";\nimport { basename, dirname, join } from \"node:path\";\nimport type { Db } from \"./db.js\";\nimport { resolveWorkstreamId } from \"./db.js\";\nimport { DELETED_BANNER_PREFIX, type ExportManifest, readManifest } from \"./exporting.js\";\nimport { emitEvent } from \"./logs.js\";\nimport type { HasNextSteps, NextStep } from \"./output.js\";\nimport { type TaskStatus, isTaskStatus } from \"./tasks/status.js\";\nimport { ensureWorkstream } from \"./workstream.js\";\n\n// ─── Typed errors ────────────────────────────────────────────────────\n\nexport class ImportBucketInvalidError extends Error implements HasNextSteps {\n override readonly name = \"ImportBucketInvalidError\";\n constructor(\n public readonly bucketDir: string,\n public readonly reason: string,\n ) {\n super(`not a valid mu bucket export at ${bucketDir}: ${reason}`);\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List the directory's contents\", command: `ls ${this.bucketDir}` },\n {\n intent: \"Inspect the manifest (must be bucketVersion 2)\",\n command: `cat ${this.bucketDir}/manifest.json`,\n },\n ];\n }\n}\n\n/** Raised when `--source-ws X[,Y]` lists a name that isn't a key in\n * the bucket manifest's `sources` map. Exit code 4 (conflict) per\n * classifyError. */\nexport class ImportSourceNotInBucketError extends Error implements HasNextSteps {\n override readonly name = \"ImportSourceNotInBucketError\";\n constructor(\n public readonly bucketDir: string,\n public readonly badName: string,\n public readonly validNames: string[],\n ) {\n super(\n `--source-ws \"${badName}\" is not a source-ws in bucket ${bucketDir}; valid: ${\n validNames.length === 0 ? \"<none>\" : validNames.join(\", \")\n }`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n { intent: \"List the bucket's source-ws subdirs\", command: `ls ${this.bucketDir}` },\n {\n intent: \"Inspect the bucket manifest's sources map\",\n command: `cat ${this.bucketDir}/manifest.json | head -40`,\n },\n ];\n }\n}\n\nexport class ImportLegacyLayoutError extends Error implements HasNextSteps {\n override readonly name = \"ImportLegacyLayoutError\";\n constructor(public readonly bucketDir: string) {\n super(\n `${bucketDir} is a pre-0.3 (single-workstream) export; mu workstream import requires bucketVersion 2. Re-export with mu ≥ 0.3, or run mu workstream import on a freshly-rendered bucket.`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Re-export the source workstream into a new bucket\",\n command: \"mu workstream export -w <ws> --out <new-bucket-dir>\",\n },\n ];\n }\n}\n\nexport class WorkstreamAlreadyExistsError extends Error implements HasNextSteps {\n override readonly name = \"WorkstreamAlreadyExistsError\";\n constructor(public readonly workstream: string) {\n super(\n `workstream \"${workstream}\" already exists in the DB; mu workstream import refuses to merge silently. Pass --workstream <new-name> to import under a different name (single-source buckets only), or destroy the existing workstream first.`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Import under a new name (single-source bucket only)\",\n command: \"mu workstream import <bucket> --workstream <new-name>\",\n },\n {\n intent: \"Or destroy the existing workstream first\",\n command: `mu workstream destroy -w ${this.workstream} --yes`,\n },\n ];\n }\n}\n\nexport class ImportFrontmatterParseError extends Error implements HasNextSteps {\n override readonly name = \"ImportFrontmatterParseError\";\n constructor(\n public readonly path: string,\n public readonly line: number,\n public readonly raw: string,\n ) {\n super(`failed to parse frontmatter at ${path}:${line}: ${raw}`);\n }\n errorNextSteps(): NextStep[] {\n return [{ intent: \"Inspect the offending file\", command: `sed -n 1,30p ${this.path}` }];\n }\n}\n\nexport class ImportEdgeRefMissingError extends Error implements HasNextSteps {\n override readonly name = \"ImportEdgeRefMissingError\";\n constructor(\n public readonly fromTask: string,\n public readonly toTask: string,\n public readonly direction: \"blocked_by\" | \"blocks\",\n ) {\n super(\n `task \"${fromTask}\" references \"${toTask}\" via ${direction}, but no task with that id was found in the import`,\n );\n }\n errorNextSteps(): NextStep[] {\n return [\n {\n intent: \"Inspect the offending task file in the bucket\",\n command: `grep -l 'id: \"${this.fromTask}\"' <bucket>/*/tasks/`,\n },\n ];\n }\n}\n\n// ─── Parser primitives (inverses of src/exporting.ts helpers) ─────────\n\n/** Inverse of `yamlScalar`: strip the surrounding double quotes and\n * unescape `\\\"` → `\"` and `\\\\` → `\\`. Newlines inside scalars are\n * not supported (the renderer flattens them to spaces). */\nfunction unquote(raw: string): string | null {\n const trimmed = raw.trim();\n if (trimmed === \"null\") return null;\n if (trimmed.length < 2 || trimmed[0] !== '\"' || trimmed[trimmed.length - 1] !== '\"') {\n return raw.trim();\n }\n const inner = trimmed.slice(1, -1);\n let out = \"\";\n for (let i = 0; i < inner.length; i++) {\n const ch = inner[i];\n if (ch === \"\\\\\" && i + 1 < inner.length) {\n const next = inner[i + 1];\n if (next === '\"' || next === \"\\\\\") {\n out += next;\n i += 1;\n continue;\n }\n }\n if (ch !== undefined) out += ch;\n }\n return out;\n}\n\n/** Parse `[ \"a\", \"b\", \"c\" ]` → [\"a\",\"b\",\"c\"]. Empty `[]` → []. */\nfunction parseStringArray(raw: string): string[] {\n const trimmed = raw.trim();\n if (!trimmed.startsWith(\"[\") || !trimmed.endsWith(\"]\")) {\n throw new Error(`expected [..] array, got ${JSON.stringify(raw)}`);\n }\n const inner = trimmed.slice(1, -1).trim();\n if (inner === \"\") return [];\n const out: string[] = [];\n // Walk the inner content respecting `\\\"` escapes.\n let i = 0;\n while (i < inner.length) {\n while (i < inner.length && (inner[i] === \" \" || inner[i] === \",\")) i += 1;\n if (i >= inner.length) break;\n if (inner[i] !== '\"') {\n throw new Error(`expected quoted string in array, got ${JSON.stringify(inner.slice(i))}`);\n }\n let j = i + 1;\n while (j < inner.length) {\n if (inner[j] === \"\\\\\" && j + 1 < inner.length) {\n j += 2;\n continue;\n }\n if (inner[j] === '\"') break;\n j += 1;\n }\n if (j >= inner.length) {\n throw new Error(\"unterminated quoted string in array\");\n }\n const scalar = unquote(inner.slice(i, j + 1));\n if (scalar === null) {\n throw new Error(\"null is not a legal array element\");\n }\n out.push(scalar);\n i = j + 1;\n }\n return out;\n}\n\ninterface ParsedFrontmatter {\n id: string;\n workstream: string;\n status: TaskStatus;\n impact: number;\n effortDays: number;\n ownerName: string | null;\n createdAt: string;\n updatedAt: string;\n blockedBy: string[];\n blocks: string[];\n title: string;\n notes: { author: string | null; createdAt: string; content: string }[];\n}\n\n/** Parse a single rendered tasks/<id>.md file. Mirrors\n * renderTaskMarkdown in src/exporting.ts. */\nfunction parseTaskMarkdown(path: string): ParsedFrontmatter {\n const raw = readFileSync(path, \"utf8\");\n const lines = raw.split(\"\\n\");\n\n if (lines[0] !== \"---\") {\n throw new ImportFrontmatterParseError(path, 1, lines[0] ?? \"\");\n }\n // Find the closing '---' for the frontmatter block.\n let end = -1;\n for (let i = 1; i < lines.length; i++) {\n if (lines[i] === \"---\") {\n end = i;\n break;\n }\n }\n if (end === -1) {\n throw new ImportFrontmatterParseError(path, 1, \"missing closing '---' for frontmatter\");\n }\n\n const fields: Record<string, string> = {};\n for (let i = 1; i < end; i++) {\n const line = lines[i] ?? \"\";\n const colon = line.indexOf(\":\");\n if (colon === -1) {\n throw new ImportFrontmatterParseError(path, i + 1, line);\n }\n const key = line.slice(0, colon).trim();\n const value = line.slice(colon + 1).trim();\n fields[key] = value;\n }\n\n function require(key: string): string {\n const v = fields[key];\n if (v === undefined) {\n throw new ImportFrontmatterParseError(path, 1, `missing frontmatter key: ${key}`);\n }\n return v;\n }\n\n const id = unquote(require(\"id\"));\n if (id === null || id === \"\") {\n throw new ImportFrontmatterParseError(path, 1, \"id must be a non-empty string\");\n }\n const workstream = unquote(require(\"workstream\"));\n if (workstream === null || workstream === \"\") {\n throw new ImportFrontmatterParseError(path, 1, \"workstream must be a non-empty string\");\n }\n const statusRaw = require(\"status\");\n if (!isTaskStatus(statusRaw)) {\n throw new ImportFrontmatterParseError(path, 1, `unknown status: ${statusRaw}`);\n }\n const impact = Number(require(\"impact\"));\n const effortDays = Number(require(\"effort_days\"));\n if (!Number.isFinite(impact) || !Number.isFinite(effortDays)) {\n throw new ImportFrontmatterParseError(path, 1, \"impact / effort_days must be numeric\");\n }\n const ownerName = unquote(require(\"owner\"));\n const createdAt = unquote(require(\"created_at\"));\n const updatedAt = unquote(require(\"updated_at\"));\n if (createdAt === null || updatedAt === null) {\n throw new ImportFrontmatterParseError(path, 1, \"created_at / updated_at cannot be null\");\n }\n let blockedBy: string[];\n let blocks: string[];\n try {\n blockedBy = parseStringArray(require(\"blocked_by\"));\n blocks = parseStringArray(require(\"blocks\"));\n } catch (err) {\n throw new ImportFrontmatterParseError(\n path,\n 1,\n err instanceof Error ? err.message : String(err),\n );\n }\n\n // Body. Find the title line (first '# ').\n let bodyIdx = end + 1;\n while (bodyIdx < lines.length && lines[bodyIdx] === \"\") bodyIdx += 1;\n const titleLine = lines[bodyIdx] ?? \"\";\n if (!titleLine.startsWith(\"# \")) {\n throw new ImportFrontmatterParseError(\n path,\n bodyIdx + 1,\n \"expected '# <title>' after frontmatter\",\n );\n }\n const title = titleLine.slice(2).trim();\n\n // Notes: scan for '## Notes (' header. Everything else is layout\n // sugar from the renderer; we don't roundtrip the title H1.\n const notes: ParsedFrontmatter[\"notes\"] = [];\n let i = bodyIdx + 1;\n while (i < lines.length && !(lines[i] ?? \"\").startsWith(\"## Notes (\")) i += 1;\n if (i < lines.length) {\n i += 1; // consume the '## Notes (N)' line.\n while (i < lines.length) {\n const line = lines[i] ?? \"\";\n if (!line.startsWith(\"### #\")) {\n i += 1;\n continue;\n }\n // '### #N by AUTHOR, TIMESTAMP'\n const headerRest = line.slice(line.indexOf(\" by \") + 4);\n const lastComma = headerRest.lastIndexOf(\", \");\n if (lastComma === -1) {\n throw new ImportFrontmatterParseError(path, i + 1, line);\n }\n const authorRaw = headerRest.slice(0, lastComma);\n const author = authorRaw === \"system\" ? null : authorRaw;\n const createdAtNote = headerRest.slice(lastComma + 2);\n i += 1;\n // Skip any blank line.\n while (i < lines.length && lines[i] === \"\") i += 1;\n // Opening fence (run of backticks).\n const openFence = lines[i] ?? \"\";\n if (!/^`{3,}$/.test(openFence)) {\n throw new ImportFrontmatterParseError(path, i + 1, openFence);\n }\n const fence = openFence;\n i += 1;\n const contentLines: string[] = [];\n while (i < lines.length && lines[i] !== fence) {\n contentLines.push(lines[i] ?? \"\");\n i += 1;\n }\n if (i >= lines.length) {\n throw new ImportFrontmatterParseError(path, i + 1, `unterminated note fence ${fence}`);\n }\n i += 1; // consume closing fence\n notes.push({ author, createdAt: createdAtNote, content: contentLines.join(\"\\n\") });\n }\n }\n\n return {\n id,\n workstream,\n status: statusRaw,\n impact,\n effortDays,\n ownerName,\n createdAt,\n updatedAt,\n blockedBy,\n blocks,\n title,\n notes,\n };\n}\n\n// ─── Public types ────────────────────────────────────────────────────\n\nexport interface ImportBucketOptions {\n bucketDir: string;\n /** Rename the (single) source workstream on import. Only valid when\n * the bucket has exactly one source-ws subdir (after applying any\n * `sourceWs` filter); otherwise rejected with an\n * ImportBucketInvalidError. */\n workstreamOverride?: string;\n /** Restrict the import to a subset of source-ws subdirs (by name).\n * Each name must be a key in the bucket manifest's `sources` map;\n * otherwise ImportSourceNotInBucketError is raised. Mutually\n * exclusive with the per-source-ws-subdir invocation form (Form 1):\n * passing this flag against a Form 1 path raises\n * ImportBucketInvalidError. Empty array is treated as \"no filter\";\n * the CLI rejects an explicitly-empty `--source-ws ,,`. */\n sourceWs?: string[];\n /** Walk + parse but write nothing to the DB. */\n dryRun?: boolean;\n}\n\nexport interface ImportSourceResult {\n workstreamName: string;\n tasksImported: number;\n edgesImported: number;\n notesImported: number;\n tombstonesSkipped: number;\n /** Per-source-ws errors that did NOT roll back this source. Empty\n * on success. (Sibling failures live in their own entry.) */\n errors: string[];\n}\n\nexport interface ImportBucketResult {\n bucketLabel: string | null;\n bucketVersion: number;\n sources: ImportSourceResult[];\n}\n\n// ─── Bucket walker ────────────────────────────────────────────────────\n\ninterface BucketSource {\n /** Name of the on-disk subdirectory (== source workstream name). */\n diskName: string;\n /** Absolute paths of every tasks/*.md under the source. */\n taskFiles: string[];\n}\n\n/** Detected on-disk shape. Auto-detected by walkBucket; the CLI\n * uses this to enforce the anti-feature guard against\n * `--source-ws` on a Form 1 (per-source-ws subdir) invocation. */\nexport type BucketShape =\n /** `<dir>/manifest.json` (bucketVersion: 2) present. The classic\n * shape; `--source-ws` can filter the per-source-ws subdirs. */\n | \"bucket\"\n /** `<dir>` is itself a per-source-ws subdir; the parent dir\n * contains the bucket manifest and resolves the source name. */\n | \"sourceWsSubdir\";\n\n/** Probe a directory and tell us whether it's a bucket root, a\n * per-source-ws subdir of a parent bucket, or neither. Pure;\n * does no DB I/O. Exposed for callers (the CLI) that need to\n * distinguish Form 1 vs Form 2 to enforce the anti-feature guard. */\nexport function detectBucketShape(bucketDir: string): BucketShape {\n if (!existsSync(bucketDir) || !statSync(bucketDir).isDirectory()) return \"bucket\";\n const manifestPath = join(bucketDir, \"manifest.json\");\n const probe = readManifest(manifestPath);\n if (probe.kind === \"v2\") return \"bucket\";\n // The source-ws subdir signature: README.md + INDEX.md + tasks/\n // (matches what renderBucketSourceFiles writes per source-ws).\n if (\n existsSync(join(bucketDir, \"README.md\")) &&\n existsSync(join(bucketDir, \"INDEX.md\")) &&\n existsSync(join(bucketDir, \"tasks\")) &&\n statSync(join(bucketDir, \"tasks\")).isDirectory()\n ) {\n return \"sourceWsSubdir\";\n }\n return \"bucket\";\n}\n\nfunction walkBucket(bucketDir: string): {\n /** The bucket manifest. For source-ws-subdir mode, this is the\n * parent bucket's manifest; bucketLabel still flows through. */\n manifest: ExportManifest;\n /** Every source-ws subdir under the bucket. For source-ws-subdir\n * mode, this list has exactly one entry (the dir we were given). */\n sources: BucketSource[];\n /** Detected on-disk shape; the CLI uses this to refuse\n * `--source-ws` on a Form 1 invocation. */\n shape: BucketShape;\n} {\n if (!existsSync(bucketDir)) {\n throw new ImportBucketInvalidError(bucketDir, \"directory does not exist\");\n }\n if (!statSync(bucketDir).isDirectory()) {\n throw new ImportBucketInvalidError(bucketDir, \"not a directory\");\n }\n const manifestPath = join(bucketDir, \"manifest.json\");\n const probe = readManifest(manifestPath);\n\n // ── Form 2 (the classic): <dir>/manifest.json present ──\n if (probe.kind === \"v2\") {\n const manifest = probe.manifest;\n const sources: BucketSource[] = [];\n for (const entry of readdirSync(bucketDir, { withFileTypes: true })) {\n if (!entry.isDirectory()) continue;\n const sourceDir = join(bucketDir, entry.name);\n const tasksDir = join(sourceDir, \"tasks\");\n if (!existsSync(tasksDir) || !statSync(tasksDir).isDirectory()) continue;\n const taskFiles: string[] = [];\n for (const f of readdirSync(tasksDir, { withFileTypes: true })) {\n if (f.isFile() && f.name.endsWith(\".md\")) {\n taskFiles.push(join(tasksDir, f.name));\n }\n }\n taskFiles.sort();\n sources.push({ diskName: entry.name, taskFiles });\n }\n sources.sort((a, b) => a.diskName.localeCompare(b.diskName));\n return { manifest, sources, shape: \"bucket\" };\n }\n if (probe.kind === \"corrupt\") {\n throw new ImportBucketInvalidError(bucketDir, \"manifest.json is unreadable / malformed\");\n }\n if (probe.kind === \"legacy\") {\n throw new ImportLegacyLayoutError(bucketDir);\n }\n\n // ── Form 1 (per-source-ws subdir): manifest.json absent at <dir>;\n // expect README.md + INDEX.md + tasks/ and a parent bucket manifest. ──\n const tasksDir = join(bucketDir, \"tasks\");\n const looksLikeSourceWs =\n existsSync(join(bucketDir, \"README.md\")) &&\n existsSync(join(bucketDir, \"INDEX.md\")) &&\n existsSync(tasksDir) &&\n statSync(tasksDir).isDirectory();\n if (!looksLikeSourceWs) {\n throw new ImportBucketInvalidError(bucketDir, \"manifest.json missing\");\n }\n\n // Walk one level up; require a v2 bucket manifest with our basename\n // listed under sources. Refuse \"orphan\" source-ws subdirs (an\n // intermediate that someone copied out of a bucket loses the\n // bucketLabel + cross-source provenance).\n const parentDir = dirname(bucketDir);\n const baseName = basename(bucketDir);\n const parentProbe = readManifest(join(parentDir, \"manifest.json\"));\n if (parentProbe.kind !== \"v2\") {\n throw new ImportBucketInvalidError(\n bucketDir,\n `${bucketDir} looks like a per-source-ws subdir (README.md + INDEX.md + tasks/), but ${parentDir}/manifest.json is missing or not a bucketVersion 2 manifest. Pass the parent bucket directory instead, or re-export.`,\n );\n }\n const parentManifest = parentProbe.manifest;\n if (!Object.prototype.hasOwnProperty.call(parentManifest.sources ?? {}, baseName)) {\n throw new ImportBucketInvalidError(\n bucketDir,\n `${bucketDir} looks like a per-source-ws subdir but \"${baseName}\" is not in the parent bucket manifest's sources (${parentDir}/manifest.json). Re-export to refresh the bucket manifest.`,\n );\n }\n\n // Synthesize a single-source BucketSource list. Reuse the parent\n // manifest verbatim so bucketLabel + bucketVersion flow through.\n const taskFiles: string[] = [];\n for (const f of readdirSync(tasksDir, { withFileTypes: true })) {\n if (f.isFile() && f.name.endsWith(\".md\")) {\n taskFiles.push(join(tasksDir, f.name));\n }\n }\n taskFiles.sort();\n return {\n manifest: parentManifest,\n sources: [{ diskName: baseName, taskFiles }],\n shape: \"sourceWsSubdir\",\n };\n}\n\n// ─── Per-source-ws DB writer ─────────────────────────────────────────\n\n/** Insert one source's tasks + edges + notes inside one transaction.\n * Throws to roll the whole source back; caller catches per-source. */\nfunction importOneSource(\n db: Db,\n targetWorkstream: string,\n parsed: ParsedFrontmatter[],\n options: { dryRun: boolean },\n): { tasksImported: number; edgesImported: number; notesImported: number } {\n // Build the id-set early so we can validate edge endpoints before\n // any INSERT (cheap-fail path; the transaction below can also catch\n // a missed ref but pre-validating gives a clearer error message and\n // skips the rollback bookkeeping).\n const idSet = new Set(parsed.map((p) => p.id));\n for (const t of parsed) {\n for (const blocker of t.blockedBy) {\n if (!idSet.has(blocker)) {\n throw new ImportEdgeRefMissingError(t.id, blocker, \"blocked_by\");\n }\n }\n for (const dep of t.blocks) {\n if (!idSet.has(dep)) {\n throw new ImportEdgeRefMissingError(t.id, dep, \"blocks\");\n }\n }\n }\n\n if (options.dryRun) {\n // Edges: count canonical (blocker → blocked) edges; the renderer\n // emits both blocked_by AND blocks, but they're the same set.\n // We dedupe on (from, to) pairs.\n const edgePairs = new Set<string>();\n for (const t of parsed) {\n for (const blocker of t.blockedBy) edgePairs.add(`${blocker}\\0${t.id}`);\n for (const dep of t.blocks) edgePairs.add(`${t.id}\\0${dep}`);\n }\n const noteCount = parsed.reduce((acc, p) => acc + p.notes.length, 0);\n return {\n tasksImported: parsed.length,\n edgesImported: edgePairs.size,\n notesImported: noteCount,\n };\n }\n\n return db.transaction(() => {\n ensureWorkstream(db, targetWorkstream);\n const wsId = resolveWorkstreamId(db, targetWorkstream);\n\n // INSERT tasks. Preserve created_at / updated_at, force owner to\n // NULL (agents aren't restored). Status is preserved verbatim;\n // schema CHECK keeps us honest.\n const insertTask = db.prepare(\n `INSERT INTO tasks\n (workstream_id, local_id, title, status, impact, effort_days, owner_id, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?)`,\n );\n const taskIdByLocalId = new Map<string, number>();\n for (const t of parsed) {\n const r = insertTask.run(\n wsId,\n t.id,\n t.title,\n t.status,\n t.impact,\n t.effortDays,\n t.createdAt,\n t.updatedAt,\n );\n taskIdByLocalId.set(t.id, Number(r.lastInsertRowid));\n }\n\n // Edges DEFERRED until every task is inserted (forward refs).\n // Dedupe (from, to) so blocked_by + blocks (which are mutual)\n // don't double-insert. Same pair from two task files is a no-op.\n const insertEdge = db.prepare(\n \"INSERT OR IGNORE INTO task_edges (from_task_id, to_task_id, created_at) VALUES (?, ?, ?)\",\n );\n const seenEdges = new Set<string>();\n let edgesImported = 0;\n const now = new Date().toISOString();\n const recordEdge = (fromLocal: string, toLocal: string): void => {\n const key = `${fromLocal}\\0${toLocal}`;\n if (seenEdges.has(key)) return;\n seenEdges.add(key);\n const fromId = taskIdByLocalId.get(fromLocal);\n const toId = taskIdByLocalId.get(toLocal);\n if (fromId === undefined || toId === undefined) return; // pre-validated\n const r = insertEdge.run(fromId, toId, now);\n if (r.changes > 0) edgesImported += 1;\n };\n for (const t of parsed) {\n for (const blocker of t.blockedBy) recordEdge(blocker, t.id);\n for (const dep of t.blocks) recordEdge(t.id, dep);\n }\n\n // Notes — preserve author + content + createdAt verbatim.\n const insertNote = db.prepare(\n \"INSERT INTO task_notes (task_id, author, content, created_at) VALUES (?, ?, ?, ?)\",\n );\n let notesImported = 0;\n for (const t of parsed) {\n const taskId = taskIdByLocalId.get(t.id);\n if (taskId === undefined) continue;\n for (const note of t.notes) {\n insertNote.run(taskId, note.author, note.content, note.createdAt);\n notesImported += 1;\n }\n }\n\n emitEvent(\n db,\n targetWorkstream,\n `workstream import ${targetWorkstream} (tasks=${parsed.length}, edges=${edgesImported}, notes=${notesImported})`,\n );\n return {\n tasksImported: parsed.length,\n edgesImported,\n notesImported,\n };\n })();\n}\n\n// ─── Public verb ─────────────────────────────────────────────────────\n\n/**\n * Import a v0.3 bucket directory back into the DB. One source-ws\n * subdirectory becomes one workstream + N tasks + M edges + K notes.\n * Per source-ws transactional: a failure in source A rolls back A\n * but leaves source B's import committed.\n *\n * Throws on unrecoverable bucket-level errors (no manifest, legacy\n * layout, --workstream override against multi-source). Per-source\n * errors (frontmatter parse, edge ref, target name collision) leave\n * the failing source's `errors` array populated and that source's\n * counts at zero; siblings still attempt their own import.\n */\nexport function importBucket(db: Db, opts: ImportBucketOptions): ImportBucketResult {\n const { manifest, sources, shape } = walkBucket(opts.bucketDir);\n\n // Anti-feature guard: --source-ws against a per-source-ws subdir\n // is meaningless (the subdir already implies a single source).\n // Refuse loudly so a confused operator drops the flag rather\n // than silently importing something they didn't expect.\n if (shape === \"sourceWsSubdir\" && opts.sourceWs !== undefined && opts.sourceWs.length > 0) {\n throw new ImportBucketInvalidError(\n opts.bucketDir,\n `cannot pass --source-ws when ${opts.bucketDir} is itself a source-ws subdir; drop the flag`,\n );\n }\n\n // Apply the --source-ws filter (Form 2 only). Validate every name\n // is a key in the bucket manifest BEFORE the walker's source list,\n // so a typo surfaces an ImportSourceNotInBucketError listing the\n // valid names rather than a silent \"nothing to import\".\n let filteredSources = sources;\n if (opts.sourceWs !== undefined && opts.sourceWs.length > 0) {\n const validNames = Object.keys(manifest.sources ?? {}).sort();\n const validSet = new Set(validNames);\n for (const wanted of opts.sourceWs) {\n if (!validSet.has(wanted)) {\n throw new ImportSourceNotInBucketError(opts.bucketDir, wanted, validNames);\n }\n }\n const wantedSet = new Set(opts.sourceWs);\n filteredSources = sources.filter((s) => wantedSet.has(s.diskName));\n }\n\n if (opts.workstreamOverride !== undefined && filteredSources.length !== 1) {\n throw new ImportBucketInvalidError(\n opts.bucketDir,\n `--workstream override requires a single source-ws subdir; this bucket has ${filteredSources.length}`,\n );\n }\n\n const dryRun = opts.dryRun === true;\n const results: ImportSourceResult[] = [];\n\n for (const source of filteredSources) {\n const targetName = opts.workstreamOverride ?? source.diskName;\n const result: ImportSourceResult = {\n workstreamName: targetName,\n tasksImported: 0,\n edgesImported: 0,\n notesImported: 0,\n tombstonesSkipped: 0,\n errors: [],\n };\n\n // Walk the tombstones first; counted but never imported.\n const liveFiles: string[] = [];\n for (const file of source.taskFiles) {\n const head = readFileSync(file, \"utf8\").slice(0, DELETED_BANNER_PREFIX.length);\n if (head.startsWith(DELETED_BANNER_PREFIX)) {\n result.tombstonesSkipped += 1;\n continue;\n }\n liveFiles.push(file);\n }\n\n // Pre-flight: refuse merge into an existing workstream of the\n // same target name. Done BEFORE parsing so a heavy bucket fails\n // fast on the cheap check.\n if (!dryRun) {\n const existing = db\n .prepare(\n `SELECT 1 AS x FROM workstreams ws WHERE ws.name = ? AND EXISTS (\n SELECT 1 FROM tasks t WHERE t.workstream_id = ws.id\n )`,\n )\n .get(targetName) as { x: number } | undefined;\n if (existing !== undefined) {\n const err = new WorkstreamAlreadyExistsError(targetName);\n result.errors.push(err.message);\n results.push(result);\n // Bubble the typed error up so the CLI maps it to exit 4.\n // We've already populated result.errors for the JSON view;\n // the throw below preempts subsequent siblings, which is\n // intentional for the conflict case.\n throw err;\n }\n }\n\n let parsed: ParsedFrontmatter[];\n try {\n parsed = liveFiles.map(parseTaskMarkdown);\n } catch (err) {\n // Frontmatter parse errors abort the source AND propagate so\n // the CLI maps them to exit 2; we still emit a per-source\n // result so the JSON view names the failing workstream.\n result.errors.push(err instanceof Error ? err.message : String(err));\n results.push(result);\n throw err;\n }\n\n try {\n const counts = importOneSource(db, targetName, parsed, { dryRun });\n result.tasksImported = counts.tasksImported;\n result.edgesImported = counts.edgesImported;\n result.notesImported = counts.notesImported;\n } catch (err) {\n result.errors.push(err instanceof Error ? err.message : String(err));\n results.push(result);\n throw err;\n }\n results.push(result);\n }\n\n return {\n bucketLabel: manifest.bucketLabel,\n bucketVersion: manifest.bucketVersion,\n sources: results,\n };\n}\n","// mu — `mu log` verb (write + read + tail).\n//\n// One verb, three modes:\n// mu log \"text\" → write\n// mu log → read latest 50\n// mu log --tail → blocking subscription (poll every 1s)\n// mu log --since <seq> → cursor read (everything after seq)\n//\n// Extracted from src/cli.ts as part of refactor_split_large_src_files.\n\nimport { getAgentByPane } from \"../agents.js\";\nimport { emitJson, emitJsonCollection, printLogRow, resolveOptionalWorkstream } from \"../cli.js\";\nimport type { Db } from \"../db.js\";\nimport { type ListLogsOptions, appendLog, latestSeq, listLogs } from \"../logs.js\";\nimport { pc } from \"../output.js\";\n\nexport interface LogReadOpts {\n workstream?: string;\n allWorkstreams?: boolean;\n since?: number;\n lines?: number;\n source?: string;\n kind?: string;\n json?: boolean;\n tail?: boolean;\n}\n\nexport interface LogWriteOpts {\n workstream?: string;\n as?: string;\n kind?: string;\n}\n\n/**\n * The `mu log` verb is overloaded: with a positional <text>, write\n * an entry; without, read the log (optionally tailing).\n */\nexport async function cmdLog(\n db: Db,\n text: string | undefined,\n opts: LogReadOpts & LogWriteOpts,\n): Promise<void> {\n if (text !== undefined && text.length > 0) {\n await cmdLogWrite(db, text, opts);\n return;\n }\n await cmdLogRead(db, opts);\n}\n\n/**\n * Resolve who/where this log entry belongs to:\n * --as <name> explicit override; workstream still resolved below\n * $TMUX_PANE agent name + workstream from the agent row\n * else source = 'user', workstream from -w / $MU_SESSION /\n * tmux session, or null if none of those resolve\n */\nasync function resolveLogContext(\n db: Db,\n opts: { as?: string; workstream?: string },\n): Promise<{ source: string; workstream: string | null }> {\n if (opts.as) {\n const workstream = opts.workstream ?? (await resolveOptionalWorkstream());\n return { source: opts.as, workstream };\n }\n const paneId = process.env.TMUX_PANE;\n if (paneId) {\n const agent = getAgentByPane(db, paneId);\n if (agent) {\n // Pane branch is intentionally asymmetric: when no -w is given, the\n // agent's own workstream wins over $MU_SESSION / tmux session, since\n // the agent row is the more authoritative binding. Don't \"fix\" this\n // to call resolveOptionalWorkstream() like the other branches.\n return {\n source: agent.name,\n workstream: opts.workstream ?? agent.workstreamName,\n };\n }\n }\n const workstream = opts.workstream ?? (await resolveOptionalWorkstream());\n return { source: \"user\", workstream };\n}\n\nasync function cmdLogWrite(db: Db, text: string, opts: LogWriteOpts): Promise<void> {\n const ctx = await resolveLogContext(db, opts);\n const row = appendLog(db, {\n workstream: ctx.workstream,\n source: ctx.source,\n kind: opts.kind ?? \"message\",\n payload: text,\n });\n console.log(\n pc.dim(\n `seq ${row.seq} workstream=${row.workstreamName ?? \"—\"} source=${row.source} kind=${row.kind}`,\n ),\n );\n}\n\nasync function cmdLogRead(db: Db, opts: LogReadOpts): Promise<void> {\n const workstream = await logReadWorkstream(opts);\n const listOpts: ListLogsOptions = {};\n if (workstream !== undefined) listOpts.workstream = workstream;\n if (opts.source !== undefined) listOpts.source = opts.source;\n if (opts.kind !== undefined) listOpts.kind = opts.kind;\n\n if (opts.tail) {\n await cmdLogTail(db, listOpts, opts);\n return;\n }\n\n if (opts.since !== undefined) listOpts.since = opts.since;\n if (opts.lines !== undefined) listOpts.limit = opts.lines;\n // Default cap: latest 50 entries when no `since` and no `--lines`.\n if (opts.since === undefined && opts.lines === undefined) listOpts.limit = 50;\n\n const rows = listLogs(db, listOpts);\n if (opts.json) {\n emitJsonCollection(rows);\n return;\n }\n if (rows.length === 0) {\n console.log(pc.dim(\"(no log entries)\"));\n return;\n }\n for (const row of rows) printLogRow(row);\n}\n\n/**\n * Resolve the `--workstream` filter for log reads:\n * --all → undefined (every workstream + machine-wide)\n * --workstream X → X\n * $MU_SESSION etc. → the current workstream (default behaviour)\n * none → undefined (be permissive in read mode)\n */\nasync function logReadWorkstream(opts: LogReadOpts): Promise<string | undefined> {\n if (opts.allWorkstreams) return undefined;\n if (opts.workstream) return opts.workstream;\n const ws = await resolveOptionalWorkstream();\n return ws ?? undefined;\n}\n\nasync function cmdLogTail(db: Db, baseOpts: ListLogsOptions, cliOpts: LogReadOpts): Promise<void> {\n // If --since wasn't given, start at \"now\" so the subscriber only sees\n // NEW entries. Pass `--since 0` to replay from the beginning.\n let cursor = cliOpts.since ?? latestSeq(db);\n if (!cliOpts.json) {\n console.log(\n pc.dim(\n `(tailing log; cursor=${cursor}; ${baseOpts.workstream ? `workstream=${baseOpts.workstream}` : \"all workstreams\"}; ctrl-c to exit)`,\n ),\n );\n }\n const intervalMs = defaultLogTailIntervalMs();\n for (;;) {\n const rows = listLogs(db, { ...baseOpts, since: cursor });\n for (const row of rows) {\n if (cliOpts.json) emitJson(row);\n else printLogRow(row);\n cursor = row.seq;\n }\n await new Promise((r) => setTimeout(r, intervalMs));\n }\n}\n\n/** Tail interval default + safety floor. Mirrors defaultSpawnLivenessMs\n * (src/agents.ts) and defaultSendDelayMs (src/tmux.ts). Naive\n * `Number(env)` was a DOS-by-typo: an env value of '' parses as 0\n * and any non-numeric value as NaN, both of which Node's setTimeout\n * treats as 1ms — hammering SQLite at ~500 Hz on the next mu log\n * --tail. Unparseable env → fall back to the no-env default of 1000ms;\n * parseable but too-low → clamp up to the 50ms floor (so a deliberate\n * `MU_LOG_TAIL_INTERVAL_MS=10` becomes 50, not 1000). */\nconst LOG_TAIL_INTERVAL_FLOOR_MS = 50;\nconst LOG_TAIL_INTERVAL_DEFAULT_MS = 1000;\nfunction defaultLogTailIntervalMs(): number {\n const raw = process.env.MU_LOG_TAIL_INTERVAL_MS;\n if (raw === undefined) return LOG_TAIL_INTERVAL_DEFAULT_MS;\n const parsed = Number.parseInt(raw, 10);\n if (Number.isNaN(parsed)) return LOG_TAIL_INTERVAL_DEFAULT_MS;\n return Math.max(LOG_TAIL_INTERVAL_FLOOR_MS, parsed);\n}\n\n// ─── commander wiring ────────────────────────────────────────────────\n//\n// wireLogCommand is called by buildProgram() in src/cli.ts. Wired here so\n// every per-namespace builder lives next to its cmd functions.\n\nimport type { Command } from \"commander\";\nimport { JSON_OPT, WORKSTREAM_OPT, handle, parseLines, parseNonNegativeInt } from \"../cli.js\";\n\nexport function wireLogCommand(program: Command): void {\n // mu log — overloaded:\n // mu log \"text\" → write\n // mu log → read latest 50\n // mu log --tail → blocking subscription (poll every 1s)\n // mu log --since <seq> → cursor read (everything after seq)\n program\n .command(\"log [text]\")\n .description(\n \"Write a log entry (with text) or read the log (without). --tail blocks and prints new entries as they land.\",\n )\n .option(\"--as <name>\", \"override the source name (default: agent via $TMUX_PANE, else 'user')\")\n .option(\"--kind <kind>\", \"kind tag (default: 'message' on write)\")\n .option(\"--tail\", \"block and print entries as they're appended\")\n .option(\n \"--since <seq>\",\n \"return entries with seq strictly greater than this (use 0 to replay everything)\",\n parseNonNegativeInt,\n )\n .option(\n \"-n, --lines <n>\",\n \"cap to the latest N entries (default 50, no cap with --since)\",\n parseLines,\n )\n .option(\"--source <name>\", \"filter by source\")\n .option(\"--all\", \"read across every workstream (and machine-wide entries)\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (text: string | undefined) {\n const raw = (this as Command).opts() as {\n as?: string;\n kind?: string;\n tail?: boolean;\n since?: number;\n lines?: number;\n source?: string;\n all?: boolean;\n workstream?: string;\n json?: boolean;\n };\n const opts: LogReadOpts & LogWriteOpts = {};\n if (raw.as !== undefined) opts.as = raw.as;\n if (raw.kind !== undefined) opts.kind = raw.kind;\n if (raw.tail !== undefined) opts.tail = raw.tail;\n if (raw.since !== undefined) opts.since = raw.since;\n if (raw.lines !== undefined) opts.lines = raw.lines;\n if (raw.source !== undefined) opts.source = raw.source;\n if (raw.all !== undefined) opts.allWorkstreams = raw.all;\n if (raw.workstream !== undefined) opts.workstream = raw.workstream;\n if (raw.json !== undefined) opts.json = raw.json;\n return handle((db) => cmdLog(db, text, opts), this as Command)();\n });\n}\n","// mu — `mu undo` / `mu snapshot list` / `mu snapshot show`.\n//\n// Snapshots are taken automatically by every destructive verb\n// (snap_schema commit ab82a11). These three CLI verbs surface them:\n//\n// mu undo [--yes] [--to <id>] restore the latest (or a specific)\n// mu snapshot list [-n N] [--json] list newest-first, with size\n// mu snapshot show <id> [--json] one snapshot's full metadata\n//\n// `mu redo` was deliberately NOT shipped: snap_design (note #293)\n// rejected it because mu verbs have side effects (tmux pane kills,\n// `git worktree remove`, etc.) that aren't replayable. \"Undo of undo\"\n// works for free — every restore takes a pre-restore snapshot first,\n// so re-running `mu undo` after `mu undo` rolls forward to that\n// snapshot.\n//\n// Extracted from src/cli.ts as part of refactor_split_large_src_files.\n\nimport { UsageError, emitJson, emitJsonCollection, truncate } from \"../cli.js\";\nimport { type Db, openDb } from \"../db.js\";\nimport { muTable, pc, printNextSteps } from \"../output.js\";\nimport { reconcile } from \"../reconcile.js\";\nimport {\n type PruneMode,\n type PruneResult,\n SnapshotNotFoundError,\n type SnapshotRow,\n deleteSnapshot,\n isStaleVersion,\n listSnapshots,\n pruneSnapshots,\n restoreSnapshot,\n snapshotFileSize,\n} from \"../snapshots.js\";\nimport { listWorkstreams } from \"../workstream.js\";\n\n/** Format a byte count for human-readable display in `mu snapshot list`.\n * Four levels: bytes / KB / MB / GB. We added a GB level after\n * snapshot_gc_caps_too_lax_no_cleanup_verb — the bug report's\n * evidence was a 731MB snapshots dir, well within the bytes → MB\n * domain, but a `mu snapshot prune --all` summary may legitimately\n * hit GB on neglected installs. */\nexport function formatBytes(n: number | null): string {\n if (n === null) return pc.red(\"missing\");\n if (n < 1024) return `${n} B`;\n if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;\n if (n < 1024 * 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;\n return `${(n / (1024 * 1024 * 1024)).toFixed(2)} GB`;\n}\n\n/** Render a schema_version cell. Stale rows render dimmed in tables\n * via the caller (see cmdSnapshotList); this helper just shapes the\n * string. Always `v<N>` for a non-zero version; `v?` for the\n * defensive 0 case (shouldn't happen — every row is stamped at\n * capture time). Exported for tests. */\nexport function formatSchemaVersion(v: number): string {\n if (!Number.isFinite(v) || v <= 0) return \"v?\";\n return `v${v}`;\n}\n\n/** Parse the value of `mu snapshot prune --older-than`. Accepts a\n * bare integer (treated as days) or `<N>d` (e.g. `7d`, `30d`).\n * Throws commander's `InvalidArgumentError` on bad input so the CLI\n * exits with the same usage-error code as other parsers.\n * Exported for tests. */\nexport function parseOlderThanDays(value: string): number {\n const trimmed = value.trim();\n const match = trimmed.match(/^(\\d+(?:\\.\\d+)?)d?$/);\n if (!match || match[1] === undefined) {\n throw new InvalidArgumentError(\n `expected a number of days (e.g. '7' or '7d'), got ${JSON.stringify(value)}`,\n );\n }\n const n = Number.parseFloat(match[1]);\n if (!Number.isFinite(n) || n < 0) {\n throw new InvalidArgumentError(\n `expected a non-negative number of days, got ${JSON.stringify(value)}`,\n );\n }\n return n;\n}\n\nexport async function cmdUndo(\n db: Db,\n opts: { to?: number; yes?: boolean; json?: boolean } = {},\n): Promise<void> {\n // Resolve target snapshot. Default = latest by id; --to picks one.\n let target: SnapshotRow | undefined;\n if (opts.to !== undefined) {\n const all = listSnapshots(db);\n target = all.find((r) => r.id === opts.to);\n if (!target) throw new SnapshotNotFoundError(opts.to);\n } else {\n const latest = listSnapshots(db, { limit: 1 });\n target = latest[0];\n }\n\n if (!target) {\n // Empty snapshots table: nothing to undo. Friendly because this\n // is the common state right after install.\n if (opts.json) {\n emitJson({\n restored: false,\n reason: \"no snapshots\",\n nextSteps: [\n {\n intent: \"Take an action that snapshots first\",\n command: \"mu task close <id> (any destructive verb auto-snapshots)\",\n },\n ],\n });\n return;\n }\n console.log(pc.dim(\"no snapshots to undo\"));\n printNextSteps([\n {\n intent: \"Snapshots are taken by destructive verbs\",\n command: \"mu task close <id> / mu agent close <name> / mu workstream destroy ...\",\n },\n { intent: \"List snapshots once they exist\", command: \"mu snapshot list\" },\n ]);\n return;\n }\n\n // Dry-run mode: print what would happen, exit clean.\n if (!opts.yes) {\n if (opts.json) {\n emitJson({\n restored: false,\n dryRun: true,\n snapshot: target,\n sizeBytes: snapshotFileSize(target),\n nextSteps: [\n {\n intent: \"Confirm and actually restore\",\n command: opts.to !== undefined ? `mu undo --to ${opts.to} --yes` : \"mu undo --yes\",\n },\n ],\n });\n return;\n }\n const wsLabel = target.workstreamName ?? pc.dim(\"<whole-DB>\");\n console.log(pc.bold(`About to restore snapshot #${target.id}`));\n console.log(` label : ${target.label}`);\n console.log(` workstream : ${wsLabel}`);\n console.log(` taken at : ${target.createdAt}`);\n console.log(` size : ${formatBytes(snapshotFileSize(target))}`);\n console.log(\"\");\n console.log(\n pc.yellow(\n \"This will REPLACE the live mu.db with the snapshot. tmux state\\nwill NOT be rolled back: agents in DB whose panes are gone will\\nbe pruned by reconcile; tmux panes whose DB rows are gone will\\nsurface as orphans on the next `mu agent list`.\",\n ),\n );\n console.log(\"\");\n console.log(pc.dim(\"(dry-run; rerun with --yes to actually restore)\"));\n printNextSteps([\n {\n intent: \"Confirm and actually restore\",\n command: opts.to !== undefined ? `mu undo --to ${opts.to} --yes` : \"mu undo --yes\",\n },\n { intent: \"Inspect the snapshot first\", command: `mu snapshot show ${target.id}` },\n ]);\n return;\n }\n\n // Capture identity BEFORE restore — restoreSnapshot closes the\n // live handle, so reading off `target` afterwards is fine but\n // reading off `db` is not.\n const restored = restoreSnapshot(db, target.id);\n\n // Re-open and reconcile every workstream so the post-restore output\n // can honestly say how much DB-vs-tmux drift exists. We open a fresh\n // handle here; handle()'s finally `db?.close()` will silently fail on\n // the old (now-closed) handle, which is the documented behaviour.\n //\n // **mode: \"report-only\"** is the load-bearing flag. Without it,\n // the reconcile pass would prune any agent row whose pane is no\n // longer in tmux — which is EVERY agent row in a snapshot taken\n // before `mu workstream destroy --yes`, because the destroy\n // killed the panes. The contract `mu undo` advertises is \"the\n // restore brings back the snapshot's rows verbatim\"; a mutating\n // post-restore reconcile silently breaks that. report-only\n // reports drift but doesn't delete (the user can still do a\n // real reconcile later via `mu agent list` once they've decided\n // which would-be-pruned rows to re-spawn vs let go).\n // (snap_undo_reconcile_destroys_recovered_agents.)\n const fresh = openDb({ path: restored.restoredTo });\n let totalGhostsWouldBePruned = 0;\n let totalOrphans = 0;\n const reconcilePerWorkstream: Array<{\n workstream: string;\n wouldBePrunedGhosts: number;\n orphans: number;\n }> = [];\n try {\n const workstreams = await listWorkstreams(fresh);\n for (const ws of workstreams) {\n // reconcile() shells out to tmux and may throw on substrate\n // failures (tmux not running). Per-workstream try/catch so a\n // single bad workstream doesn't poison the post-restore\n // summary.\n try {\n const report = await reconcile(fresh, {\n workstream: ws.name,\n mode: \"report-only\",\n });\n totalGhostsWouldBePruned += report.prunedGhosts;\n totalOrphans += report.orphans.length;\n reconcilePerWorkstream.push({\n workstream: ws.name,\n wouldBePrunedGhosts: report.prunedGhosts,\n orphans: report.orphans.length,\n });\n } catch {\n // Best-effort; the restore itself succeeded.\n }\n }\n } finally {\n try {\n fresh.close();\n } catch {\n // best effort\n }\n }\n\n if (opts.json) {\n emitJson({\n restored: true,\n snapshot: target,\n restoredTo: restored.restoredTo,\n schemaVersion: restored.schemaVersion,\n reconcile: {\n // Renamed from `ghostsPruned` because we no longer prune\n // during restore. The shape is informational — callers\n // wanting to actually prune should run `mu agent list`\n // (which still mutates) after deciding which rows to keep.\n wouldBePrunedGhosts: totalGhostsWouldBePruned,\n orphansSurfaced: totalOrphans,\n // Reconcile mode: \"report-only\" preserves the snapshot's\n // restored rows verbatim. (Was `dryRun: true` before the\n // status-only/report-only split — BREAKING for SDK consumers\n // reading this field; see CHANGELOG.)\n mode: \"report-only\",\n perWorkstream: reconcilePerWorkstream,\n },\n nextSteps: [\n {\n intent: \"See orphan panes (DB doesn't know about them)\",\n command: \"mu agent list -w '*' --json | jq '.[].orphans'\",\n },\n {\n intent: \"Confirm + actually prune dead-pane rows you don't want to re-spawn\",\n command: \"mu agent list -w <ws> (a normal list reconciles + prunes)\",\n },\n { intent: \"Roll forward (undo the undo)\", command: \"mu undo --yes\" },\n ],\n });\n return;\n }\n console.log(\n `Restored snapshot ${pc.bold(`#${target.id}`)} (${target.label}, taken ${target.createdAt})`,\n );\n console.log(\"\");\n console.log(pc.bold(\"Reconcile (tmux NOT rolled back; rows NOT pruned):\"));\n console.log(\n ` would-be-pruned (DB row → dead pane) : ${pc.yellow(String(totalGhostsWouldBePruned))} ${pc.dim(\"(suppressed: rows preserved as restored)\")}`,\n );\n console.log(` orphan panes surfaced : ${pc.yellow(String(totalOrphans))}`);\n printNextSteps([\n {\n intent: \"See orphan panes (DB doesn't know about them)\",\n command: \"mu agent list -w '*' --json | jq '.[].orphans'\",\n },\n {\n intent: \"Confirm + actually prune dead-pane rows you don't want to re-spawn\",\n command: \"mu agent list -w <ws> (a normal list reconciles + prunes)\",\n },\n { intent: \"Re-spawn an agent the DB now lacks\", command: \"mu agent spawn <name> -w <ws>\" },\n { intent: \"Roll forward (undo the undo)\", command: \"mu undo --yes\" },\n ]);\n}\n\nexport async function cmdSnapshotList(\n db: Db,\n opts: { lines?: number; json?: boolean } = {},\n): Promise<void> {\n const limit = opts.lines ?? 20;\n const rows = listSnapshots(db, { limit });\n if (opts.json) {\n emitJsonCollection(\n rows.map((r) => ({\n ...r,\n sizeBytes: snapshotFileSize(r),\n })),\n );\n return;\n }\n if (rows.length === 0) {\n console.log(pc.dim(\"no snapshots\"));\n printNextSteps([\n {\n intent: \"Snapshots are taken automatically by destructive verbs\",\n command: \"mu task close <id> / mu agent close <name> / mu workstream destroy ...\",\n },\n ]);\n return;\n }\n // Snapshot labels are free-text (e.g. \"task close <id> evidence=...\"\n // can run dozens of chars). Cap the label column so a long label\n // can't push id/created_at off-screen\n // (tables_truncate_long_cols_audit).\n const LABEL_BUDGET = 50;\n // \"ver\" column added in snapshot_gc_caps_too_lax_no_cleanup_verb so\n // operators can see at a glance which snapshots are stale (schema\n // bumped past their stamp — unrestorable; pure dead weight on disk).\n // Stale rows render dimmed via pc.dim, mirroring the satisfied-blockers\n // bucket in `mu task show` (task_show_blocked_by_renders_closed).\n const table = muTable({\n head: [\n pc.bold(\"id\"),\n pc.bold(\"ver\"),\n pc.bold(\"label\"),\n pc.bold(\"workstream\"),\n pc.bold(\"created_at\"),\n pc.bold(\"size\"),\n ],\n colWidths: [null, null, LABEL_BUDGET, null, null, null],\n style: { head: [] },\n });\n let staleCount = 0;\n for (const r of rows) {\n const stale = isStaleVersion(r);\n if (stale) staleCount += 1;\n const ver = formatSchemaVersion(r.schemaVersion);\n const cells = [\n String(r.id),\n ver,\n truncate(r.label, LABEL_BUDGET - 2),\n r.workstreamName ?? \"<whole-DB>\",\n r.createdAt,\n formatBytes(snapshotFileSize(r)),\n ];\n // Dim every cell on a stale row so the row reads as a single\n // \"don't bother trying to restore this\" hint. Match the\n // task_show satisfied-bucket pattern: pc.dim each piece.\n if (stale) {\n table.push(cells.map((c) => pc.dim(c)));\n } else {\n // Apply the dim to <whole-DB> on non-stale rows for the\n // historical look-and-feel.\n cells[3] = r.workstreamName ?? pc.dim(\"<whole-DB>\");\n table.push(cells);\n }\n }\n console.log(table.toString());\n const nextSteps = [\n { intent: \"Show one snapshot's full metadata\", command: \"mu snapshot show <id>\" },\n { intent: \"Restore the latest snapshot\", command: \"mu undo --yes\" },\n { intent: \"Restore a specific snapshot\", command: \"mu undo --to <id> --yes\" },\n ];\n if (staleCount > 0) {\n nextSteps.push({\n intent: `Drop ${staleCount} stale-version row${staleCount === 1 ? \"\" : \"s\"} (unrestorable; pure disk weight)`,\n command: \"mu snapshot prune --stale-version --yes\",\n });\n }\n printNextSteps(nextSteps);\n}\n\n// ─── mu snapshot prune ──────────────────────────────────────────────\n//\n// Two-phase: dry-run by default, --yes to commit. Mode is determined\n// by the supplied flag (mutually exclusive); exactly one is required\n// EXCEPT the bare `mu snapshot prune` form which runs the GC policy.\n// snapshot_gc_caps_too_lax_no_cleanup_verb introduces this verb.\n\nexport interface PruneCliOptions {\n /** mode='keep-last' selector */\n keepLast?: number;\n /** mode='older-than' selector (already-parsed days) */\n olderThanDays?: number;\n /** mode='stale-version' selector */\n staleVersion?: boolean;\n /** mode='all' selector */\n all?: boolean;\n yes?: boolean;\n json?: boolean;\n}\n\nexport async function cmdSnapshotPrune(db: Db, opts: PruneCliOptions = {}): Promise<void> {\n // Determine mode + report flag conflicts.\n const flags: Array<{ name: string; on: boolean }> = [\n { name: \"--keep-last\", on: opts.keepLast !== undefined },\n { name: \"--older-than\", on: opts.olderThanDays !== undefined },\n { name: \"--stale-version\", on: opts.staleVersion === true },\n { name: \"--all\", on: opts.all === true },\n ];\n const on = flags.filter((f) => f.on);\n if (on.length > 1) {\n const names = on.map((f) => f.name).join(\", \");\n throw new UsageError(`prune flags are mutually exclusive; got ${names}`);\n }\n let mode: PruneMode = \"gc\";\n if (opts.keepLast !== undefined) mode = \"keep-last\";\n else if (opts.olderThanDays !== undefined) mode = \"older-than\";\n else if (opts.staleVersion === true) mode = \"stale-version\";\n else if (opts.all === true) mode = \"all\";\n\n const dryRun = opts.yes !== true;\n\n // Compute the would-delete set first (dryRun=true). Even on the\n // commit path we want the same victim shape for the summary; so we\n // do a dry-run, print the summary if applicable, then re-run for\n // real if --yes is set. Single round-trip is fine — victim sets are\n // small (bounded by row count).\n const dry = pruneSnapshots(db, {\n mode,\n keepLast: opts.keepLast,\n olderThanDays: opts.olderThanDays,\n dryRun: true,\n });\n\n if (dryRun) {\n if (opts.json) {\n emitJson({\n dryRun: true,\n mode,\n wouldDeleteRows: dry.victims.length,\n wouldDeleteFiles: dry.victims.filter((v) => snapshotFileSize(v) !== null).length,\n wouldFreeBytes: dry.freedBytes,\n victims: dry.victims.map((v) => ({\n ...v,\n sizeBytes: snapshotFileSize(v),\n stale: isStaleVersion(v),\n })),\n nextSteps: [\n {\n intent: \"Confirm and actually prune\",\n command: pruneConfirmCommand(mode, opts),\n },\n ],\n });\n return;\n }\n printPruneSummary(mode, dry, /*dryRun*/ true);\n printNextSteps([\n { intent: \"Confirm and actually prune\", command: pruneConfirmCommand(mode, opts) },\n { intent: \"List snapshots\", command: \"mu snapshot list\" },\n ]);\n return;\n }\n\n // Commit.\n const result = pruneSnapshots(db, {\n mode,\n keepLast: opts.keepLast,\n olderThanDays: opts.olderThanDays,\n dryRun: false,\n });\n\n if (opts.json) {\n emitJson({\n dryRun: false,\n mode,\n deletedRows: result.deletedRows,\n deletedFiles: result.deletedFiles,\n freedBytes: result.freedBytes,\n ...(result.safetyNetSnapshotId !== undefined\n ? { safetyNetSnapshotId: result.safetyNetSnapshotId }\n : {}),\n nextSteps: [\n { intent: \"List remaining snapshots\", command: \"mu snapshot list\" },\n ...(result.safetyNetSnapshotId !== undefined\n ? [\n {\n intent: \"Undo the prune (a safety-net snapshot was captured)\",\n command: `mu undo --to ${result.safetyNetSnapshotId} --yes`,\n },\n ]\n : []),\n ],\n });\n return;\n }\n printPruneSummary(mode, result, /*dryRun*/ false);\n if (result.safetyNetSnapshotId !== undefined) {\n console.log(\n pc.dim(\n `Safety-net snapshot #${result.safetyNetSnapshotId} captured before the wipe; restore via mu undo --to ${result.safetyNetSnapshotId} --yes.`,\n ),\n );\n }\n const next: Array<{ intent: string; command: string }> = [\n { intent: \"List remaining snapshots\", command: \"mu snapshot list\" },\n ];\n if (result.safetyNetSnapshotId !== undefined) {\n next.push({\n intent: \"Undo the prune (a safety-net snapshot was captured)\",\n command: `mu undo --to ${result.safetyNetSnapshotId} --yes`,\n });\n }\n printNextSteps(next);\n}\n\nfunction printPruneSummary(mode: PruneMode, r: PruneResult, dryRun: boolean): void {\n const verb = dryRun ? \"Would delete\" : \"Deleted\";\n const rowCount = dryRun ? r.victims.length : r.deletedRows;\n const fileCount = dryRun\n ? r.victims.filter((v) => snapshotFileSize(v) !== null).length\n : r.deletedFiles;\n console.log(\n `${pc.bold(`${verb} ${rowCount} snapshot row${rowCount === 1 ? \"\" : \"s\"}`)} ${pc.dim(`(${fileCount} on-disk .db file${fileCount === 1 ? \"\" : \"s\"}, ${formatBytes(r.freedBytes)})`)}`,\n );\n console.log(` mode : ${mode}`);\n if (dryRun) console.log(pc.dim(\"(dry-run; rerun with --yes to actually prune)\"));\n}\n\nfunction pruneConfirmCommand(mode: PruneMode, opts: PruneCliOptions): string {\n switch (mode) {\n case \"gc\":\n return \"mu snapshot prune --yes\";\n case \"keep-last\":\n return `mu snapshot prune --keep-last ${opts.keepLast} --yes`;\n case \"older-than\":\n return `mu snapshot prune --older-than ${opts.olderThanDays}d --yes`;\n case \"stale-version\":\n return \"mu snapshot prune --stale-version --yes\";\n case \"all\":\n return \"mu snapshot prune --all --yes\";\n }\n}\n\n// ─── mu snapshot delete <id> ───────────────────────────────────────\n//\n// Surgical removal mirroring `mu task delete`. Single row + the\n// on-disk .db. Errors with SnapshotNotFoundError on miss.\n// snapshot_gc_caps_too_lax_no_cleanup_verb.\n\nexport async function cmdSnapshotDelete(\n db: Db,\n id: number,\n opts: { json?: boolean } = {},\n): Promise<void> {\n const r = deleteSnapshot(db, id);\n if (opts.json) {\n emitJson({\n snapshotId: id,\n deleted: r.deleted,\n deletedFiles: r.deletedFiles,\n freedBytes: r.freedBytes,\n nextSteps: [{ intent: \"List snapshots\", command: \"mu snapshot list\" }],\n });\n return;\n }\n console.log(\n `Deleted snapshot ${pc.bold(`#${id}`)} ${pc.dim(`(${r.deletedFiles === 1 ? \"file unlinked\" : \"file already gone\"}, ${formatBytes(r.freedBytes)})`)}`,\n );\n // Note: deleteSnapshot does NOT auto-snapshot first — the point\n // is to delete one snapshot, and the auto-snapshot would defeat\n // that. Other snapshots are unaffected.\n printNextSteps([{ intent: \"List remaining snapshots\", command: \"mu snapshot list\" }]);\n}\n\nexport async function cmdSnapshotShow(\n db: Db,\n id: number,\n opts: { json?: boolean } = {},\n): Promise<void> {\n const all = listSnapshots(db);\n const row = all.find((r) => r.id === id);\n if (!row) throw new SnapshotNotFoundError(id);\n const sizeBytes = snapshotFileSize(row);\n if (opts.json) {\n emitJson({ ...row, sizeBytes });\n return;\n }\n console.log(pc.bold(`snapshot #${row.id}`));\n console.log(` label : ${row.label}`);\n console.log(` workstream : ${row.workstreamName ?? pc.dim(\"<whole-DB>\")}`);\n console.log(` schema_version : ${row.schemaVersion}`);\n console.log(` db_path : ${row.dbPath}`);\n console.log(` size : ${formatBytes(sizeBytes)}`);\n console.log(` created_at : ${row.createdAt}`);\n // The inspect-without-restoring hint shells out to raw sqlite3 on\n // purpose: the snapshot file is a separate, frozen DB by definition,\n // so the \"use mu sql, not sqlite3\" convention (which targets the live\n // DB) does not apply. Snapshots are forensic / out-of-band; bypass mu.\n printNextSteps([\n { intent: \"Restore this snapshot\", command: `mu undo --to ${row.id} --yes` },\n {\n intent: \"Inspect the snapshot's data without restoring (snapshot is forensic; bypass mu)\",\n command: `sqlite3 ${row.dbPath} \"SELECT * FROM tasks\"`,\n },\n ]);\n}\n\n// ─── commander wiring ────────────────────────────────────────────────\n//\n// wireSnapshotCommands is called by buildProgram() in src/cli.ts. Wired here so\n// every per-namespace builder lives next to its cmd functions.\n\nimport { type Command, InvalidArgumentError } from \"commander\";\nimport { JSON_OPT, handle, parseLines } from \"../cli.js\";\n\nexport function wireSnapshotCommands(program: Command): void {\n //\n // `mu undo` lives at the top level (not under `mu snapshot`) because\n // it's the user-facing recovery verb — same prominence as `mu state`,\n // `mu doctor`. The list/show inspector verbs nest under `mu snapshot`\n // since they're scoped operations on the snapshots collection.\n\n program\n .command(\"undo\")\n .description(\n \"Restore the most recent snapshot (or one selected via --to). Pass --yes to actually restore; otherwise prints a dry-run summary. tmux state is NOT rolled back — the post-restore reconcile prunes ghost agents and surfaces orphan panes; re-spawn or `mu adopt` as needed.\",\n )\n .option(\"--to <id>\", \"snapshot id to restore (default: most recent)\", parseLines)\n .option(\"-y, --yes\", \"actually restore (without this flag, prints a dry-run summary)\")\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as {\n to?: number;\n yes?: boolean;\n json?: boolean;\n };\n return handle((db) => cmdUndo(db, opts), this as Command)();\n });\n\n const snapshot = program\n .command(\"snapshot\")\n .description(\"Snapshot inspection (use `mu undo` to restore one)\");\n\n snapshot\n .command(\"list\")\n .description(\"List snapshots, newest first.\")\n .option(\"-n, --lines <n>\", \"cap rows; default 20\", parseLines)\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as { lines?: number; json?: boolean };\n return handle((db) => cmdSnapshotList(db, opts), this as Command)();\n });\n\n snapshot\n .command(\"show <id>\")\n .description(\"Show one snapshot's full metadata.\")\n .option(...JSON_OPT)\n .action(function (idArg: string) {\n const id = parseLines(idArg);\n const opts = (this as Command).opts() as { json?: boolean };\n return handle((db) => cmdSnapshotShow(db, id, opts), this as Command)();\n });\n\n snapshot\n .command(\"prune\")\n .description(\n \"Prune snapshots. Bare form runs the GC policy (count + age caps); flags select alternate modes. Two-phase: prints a dry-run summary; rerun with --yes to actually delete. --all auto-captures a safety-net snapshot first.\",\n )\n .option(\"--keep-last <n>\", \"keep only the N most recent snapshots\", (v) => parseLines(v))\n .option(\"--older-than <days>\", \"drop snapshots older than this; accepts '7' or '7d'\", (v) =>\n parseOlderThanDays(v),\n )\n .option(\n \"--stale-version\",\n \"drop snapshots whose schema_version != current (unrestorable; pure disk weight)\",\n )\n .option(\n \"--all\",\n \"drop EVERY snapshot (--yes auto-captures a safety-net snapshot of the live DB first)\",\n )\n .option(\"-y, --yes\", \"actually prune (without this flag, prints a dry-run summary)\")\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as PruneCliOptions;\n return handle((db) => cmdSnapshotPrune(db, opts), this as Command)();\n });\n\n snapshot\n .command(\"delete <id>\")\n .description(\n \"Delete one snapshot row + its on-disk .db file. Errors with SnapshotNotFoundError on miss. Does NOT auto-snapshot first — deleting one stepping-stone can't break `mu undo`.\",\n )\n .option(...JSON_OPT)\n .action(function (idArg: string) {\n const id = parseLines(idArg);\n const opts = (this as Command).opts() as { json?: boolean };\n return handle((db) => cmdSnapshotDelete(db, id, opts), this as Command)();\n });\n}\n","// mu — `mu sql` escape-hatch verb.\n//\n// Read OR write — `mu sql` is the explicit escape hatch. Single-statement\n// path uses better-sqlite3's prepare() so we can distinguish read\n// (.all() rows) from write (.run() change count). Multi-statement path\n// uses db.exec() which handles BEGIN/COMMIT and multiple semicolon-\n// separated statements but returns nothing structured.\n//\n// `--confirm-rows N` adds a transactional safety belt: if the actual\n// affected-row count doesn't match N, the whole thing rolls back.\n//\n// Extracted from src/cli.ts as part of refactor_split_large_src_files.\n\nimport { UsageError, emitJson } from \"../cli.js\";\nimport type { Db } from \"../db.js\";\nimport { muTable, pc } from \"../output.js\";\n\nexport async function cmdSql(\n db: Db,\n query: string,\n opts: { json?: boolean; confirmRows?: number } = {},\n): Promise<void> {\n // Read OR write — `mu sql` is the explicit escape hatch.\n //\n // Single-statement path uses better-sqlite3's prepare() so we can\n // distinguish read (.all() rows) from write (.run() change count).\n // Multi-statement path uses db.exec() which handles BEGIN/COMMIT and\n // multiple semicolon-separated statements but returns nothing\n // structured. We try prepare() first; if it throws the\n // 'more than one statement' SqliteError, we fall back to exec().\n // This keeps the simple case fast and well-typed while making\n // multi-statement migrations / cleanup scripts a one-shot.\n const trimmed = query.trim();\n // Probe whether this is a single statement by trying prepare(); if\n // better-sqlite3 throws 'more than one statement', use exec() instead.\n // Capture the prepared statement so the single-statement path below\n // doesn't re-prepare (review_code_sql_double_prepare).\n let stmt: ReturnType<typeof db.prepare> | undefined;\n let isMulti = false;\n try {\n stmt = db.prepare(trimmed);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (/more than one statement/i.test(msg)) {\n isMulti = true;\n } else {\n throw err;\n }\n }\n\n if (isMulti) {\n // Multi-statement: db.exec() doesn't report a per-statement change\n // count, so for --confirm-rows we wrap the whole script in a manual\n // transaction and diff total_changes() before/after. Note: scripts\n // that contain their own BEGIN/COMMIT will fail under --confirm-rows\n // (sqlite refuses nested transactions); that's the price of an\n // atomic confirm-or-rollback wrapper around an opaque blob.\n if (opts.confirmRows !== undefined) {\n const expected = opts.confirmRows;\n const before = (db.prepare(\"SELECT total_changes() AS n\").get() as { n: number }).n;\n db.exec(\"BEGIN\");\n let actual: number;\n try {\n db.exec(trimmed);\n const after = (db.prepare(\"SELECT total_changes() AS n\").get() as { n: number }).n;\n actual = after - before;\n } catch (e) {\n try {\n db.exec(\"ROLLBACK\");\n } catch {}\n throw e;\n }\n if (actual !== expected) {\n db.exec(\"ROLLBACK\");\n throw new UsageError(\n `expected ${expected} rows, would have affected ${actual} (rolled back). Re-run with --confirm-rows ${actual} if intentional.`,\n );\n }\n db.exec(\"COMMIT\");\n const n = countTopLevelStatements(trimmed);\n if (opts.json) {\n emitJson({\n statements: n,\n multiStatement: true,\n confirmRows: expected,\n actualRows: actual,\n });\n return;\n }\n console.log(\n pc.dim(\n `ran ${n} statement${n === 1 ? \"\" : \"s\"} (${actual} row${actual === 1 ? \"\" : \"s\"} affected)`,\n ),\n );\n return;\n }\n db.exec(trimmed);\n const n = countTopLevelStatements(trimmed);\n if (opts.json) {\n emitJson({ statements: n, multiStatement: true });\n return;\n }\n console.log(pc.dim(`ran ${n} statement${n === 1 ? \"\" : \"s\"}`));\n return;\n }\n\n const lower = trimmed.toLowerCase();\n const isRead =\n lower.startsWith(\"select\") || lower.startsWith(\"with\") || lower.startsWith(\"explain\");\n if (opts.confirmRows !== undefined && isRead) {\n throw new UsageError(\n \"--confirm-rows is only meaningful on write statements (UPDATE / DELETE / INSERT / REPLACE)\",\n );\n }\n // Past the multi-statement branch: prepare() succeeded, so `stmt` must\n // be set. Narrow once instead of repeating the unreachable check at\n // every read/write fork below.\n if (!stmt) throw new Error(\"unreachable: stmt should be set on the single-statement path\");\n const single = stmt;\n if (isRead) {\n const rows = single.all([]);\n if (opts.json) {\n emitJson(rows);\n return;\n }\n if (rows.length === 0) {\n console.log(pc.dim(\"(no rows)\"));\n return;\n }\n const first = rows[0] as Record<string, unknown>;\n const keys = Object.keys(first);\n // SQL is the escape hatch: every column is user-data of unknown\n // length. Divide the terminal width evenly across columns (subject\n // to a 12-char floor) so a single wide cell can't push the table\n // out to ~200 cols and break the TUI layout. cli-table3's\n // wordWrap:false (via muTable) truncates with … when a cell\n // exceeds its column width (tables_truncate_long_cols_audit).\n const termWidth = process.stdout.columns ?? 100;\n const padding = keys.length * 3 + 1; // 2-char cell padding + 1-char border per col\n const perCol = Math.max(12, Math.floor((termWidth - padding) / Math.max(1, keys.length)));\n const colWidths = keys.map(() => perCol);\n const table = muTable({\n head: keys.map((k) => pc.bold(k)),\n colWidths,\n style: { head: [] },\n });\n for (const row of rows) {\n const obj = row as Record<string, unknown>;\n table.push(keys.map((k) => formatCell(obj[k])));\n }\n console.log(table.toString());\n console.log(pc.dim(`(${rows.length} row${rows.length === 1 ? \"\" : \"s\"})`));\n } else {\n if (opts.confirmRows !== undefined) {\n const expected = opts.confirmRows;\n db.exec(\"BEGIN\");\n let result: { changes: number; lastInsertRowid: number | bigint };\n try {\n result = single.run([]);\n } catch (e) {\n try {\n db.exec(\"ROLLBACK\");\n } catch {}\n throw e;\n }\n if (result.changes !== expected) {\n db.exec(\"ROLLBACK\");\n throw new UsageError(\n `expected ${expected} rows, would have affected ${result.changes} (rolled back). Re-run with --confirm-rows ${result.changes} if intentional.`,\n );\n }\n db.exec(\"COMMIT\");\n if (opts.json) {\n emitJson({\n changes: result.changes,\n lastInsertRowid: Number(result.lastInsertRowid),\n confirmRows: expected,\n actualRows: result.changes,\n });\n return;\n }\n console.log(pc.dim(`${result.changes} row${result.changes === 1 ? \"\" : \"s\"} affected`));\n return;\n }\n const result = single.run([]);\n if (opts.json) {\n emitJson({\n changes: result.changes,\n lastInsertRowid: Number(result.lastInsertRowid),\n });\n return;\n }\n console.log(pc.dim(`${result.changes} row${result.changes === 1 ? \"\" : \"s\"} affected`));\n }\n}\n\n/**\n * Count top-level SQL statements in `sql`, ignoring semicolons inside\n * single-quoted strings, double-quoted identifiers, line comments\n * (`-- ...`), and block comments (`/* ... *\\/`). Used by `mu sql`'s\n * multi-statement path to report 'ran N statements'.\n *\n * Hand-rolled rather than pulling in a SQL parser — mu's escape hatch\n * is for human-typed scripts, not arbitrary SQL. The state machine\n * covers the cases we care about; pathological inputs (nested\n * comments, dollar-quoted strings, etc.) may miscount but won't crash\n * the verb (db.exec already ran successfully by then).\n */\nexport function countTopLevelStatements(sql: string): number {\n let count = 0;\n let inSingle = false;\n let inDouble = false;\n let inLineComment = false;\n let inBlockComment = false;\n let sawNonWs = false;\n for (let i = 0; i < sql.length; i++) {\n const c = sql[i];\n const next = sql[i + 1];\n if (inLineComment) {\n if (c === \"\\n\") inLineComment = false;\n continue;\n }\n if (inBlockComment) {\n if (c === \"*\" && next === \"/\") {\n inBlockComment = false;\n i++;\n }\n continue;\n }\n if (inSingle) {\n // SQL-style escaped single quote: '' inside a string\n if (c === \"'\" && next === \"'\") {\n i++;\n continue;\n }\n if (c === \"'\") inSingle = false;\n continue;\n }\n if (inDouble) {\n if (c === '\"' && next === '\"') {\n i++;\n continue;\n }\n if (c === '\"') inDouble = false;\n continue;\n }\n if (c === \"-\" && next === \"-\") {\n inLineComment = true;\n i++;\n continue;\n }\n if (c === \"/\" && next === \"*\") {\n inBlockComment = true;\n i++;\n continue;\n }\n if (c === \"'\") {\n inSingle = true;\n continue;\n }\n if (c === '\"') {\n inDouble = true;\n continue;\n }\n if (c === \";\") {\n if (sawNonWs) {\n count++;\n sawNonWs = false;\n }\n continue;\n }\n if (c !== undefined && /\\S/.test(c)) sawNonWs = true;\n }\n if (sawNonWs) count++; // trailing statement without `;`\n return count;\n}\n\nfunction formatCell(v: unknown): string {\n if (v === null || v === undefined) return pc.dim(\"null\");\n if (typeof v === \"string\") return v;\n return String(v);\n}\n\n// ─── commander wiring ────────────────────────────────────────────────\n//\n// wireSqlCommand is called by buildProgram() in src/cli.ts. Wired here so\n// every per-namespace builder lives next to its cmd functions.\n\nimport type { Command } from \"commander\";\nimport { JSON_OPT, handle, parseLines } from \"../cli.js\";\n\nexport function wireSqlCommand(program: Command): void {\n program\n .command(\"sql <query>\")\n .description(\"Run a SQL query against the live mu DB (SELECT / UPDATE / DELETE all allowed)\")\n .option(...JSON_OPT)\n .option(\n \"--confirm-rows <n>\",\n \"abort if affected-row count differs from N (rollback)\",\n parseLines,\n )\n .action(function (query: string) {\n const opts = (this as Command).opts() as { json?: boolean; confirmRows?: number };\n return handle((db) => cmdSql(db, query, opts), this as Command)();\n });\n}\n","// mu — `mu state` (canonical state card) + bare `mu` (mission control).\n//\n// One verb, three render modes (merge_state_into_hud_render_mode, v0.3):\n//\n// mu state full card: agents + orphans + tracks +\n// ready/in-progress/blocked/recent-closed +\n// workspaces + recent events. Top-to-bottom,\n// every section gets its full table. JSON-\n// first by design (per Ilya's council\n// critique: state cards as the default\n// attention surface; SQL/raw verbs as the\n// escape hatch underneath).\n//\n// mu state --hud dynamic-fit render: greedy top-down table\n// layout that fills the terminal (or tmux\n// pane) height + width with as much useful\n// data as fits. Section ordering is fixed:\n// header / agents / ready / in-progress /\n// tracks / recent. Truncated tables get a\n// \"… +N more (<verb>)\" footer. Designed for\n// `watch -n 5 mu state --hud` /\n// `tmux display-popup -E 'mu state --hud'`.\n//\n// mu state --mission stripped 5-column glance card: agents +\n// orphans + tracks + ready. The bare-`mu`\n// muscle-memory orient call (\"what's going\n// on?\"). Bare `mu` (no verb) is an alias.\n//\n// All three modes share the same data set (loaded once via\n// loadWorkstreamData); only the rendering strategy differs. --hud and\n// --mission are mutually exclusive.\n//\n// All three modes support variadic `-w X[,Y]...` / `-w X -w Y` and\n// `--all`. N=1 renders single-mode (legacy shape); N≥2 stacks per-\n// workstream cards (full / mission) or unions with a leading workstream\n// column (hud).\n//\n// All three modes pass mode: \"status-only\" to listLiveAgents — refresh\n// status + pane title (the operator's primary signal) but skip prune\n// + reap, so the periodic poll never deletes mid-spawn placeholders\n// (bug_agent_spawn_workspace_fk_failure) and the pane border indicator\n// stays fresh between mutating verbs\n// (bug_pane_title_glyph_stuck_at_needs_input).\n\nimport Table from \"cli-table3\";\nimport { type AgentRow, type AgentStatus, listLiveAgents } from \"../agents.js\";\nimport {\n IDLE_GLYPH,\n JSON_OPT,\n UsageError,\n byRoiDesc,\n emitJson,\n formatAgentsTable,\n formatReadyTable,\n formatTaskListTable,\n formatTracks,\n formatWorkspacesTable,\n formatWorkstreamsTable,\n handle,\n parseCsvFlag,\n parseLines,\n printLogRow,\n relTime,\n resolveOptionalWorkstream,\n statusIcon,\n truncate,\n withRoiAll,\n} from \"../cli.js\";\nimport { type Db, WorkstreamNotFoundError, tryResolveWorkstreamId } from \"../db.js\";\nimport { EVENT_VERB_PREFIXES, type LogRow, displayEventPayload, listLogs } from \"../logs.js\";\nimport { pc } from \"../output.js\";\nimport {\n type TaskRow,\n listBlocked,\n listInProgress,\n listReady,\n listRecentClosed,\n listTasksByOwner,\n} from \"../tasks.js\";\nimport { currentPaneSize } from \"../tmux.js\";\nimport { type Track, getParallelTracks } from \"../tracks.js\";\nimport {\n type WorkspaceOrphan,\n type WorkspaceRow,\n decorateWithStaleness,\n listWorkspaceOrphans,\n listWorkspaces,\n} from \"../workspace.js\";\nimport { listWorkstreams } from \"../workstream.js\";\n\n// ─── Per-workstream loaded data ─────────────────────────────────────\n\ninterface PerWsData {\n workstreamName: string;\n view: Awaited<ReturnType<typeof listLiveAgents>>;\n tracks: Track[];\n ready: TaskRow[];\n inProgress: TaskRow[];\n blocked: TaskRow[];\n recentClosed: TaskRow[];\n workspaces: WorkspaceRow[];\n workspaceOrphans: WorkspaceOrphan[];\n recent: LogRow[];\n}\n\nasync function loadWorkstreamData(\n db: Db,\n workstream: string,\n eventLimit: number,\n): Promise<PerWsData> {\n // status-only refresh: don't prune mid-spawn placeholders or reap\n // unreachable agents — every render-mode is a polling read surface\n // (`watch -n 5 mu state`, tmux popup, etc.). See module-level\n // comment for the bug history.\n const view = await listLiveAgents(db, { workstream, mode: \"status-only\" });\n const tracks = getParallelTracks(db, workstream);\n const ready = listReady(db, workstream).sort(byRoiDesc);\n const inProgress = listInProgress(db, workstream);\n const blocked = listBlocked(db, workstream);\n const recentClosed = listRecentClosed(db, workstream);\n // Decorate workspaces with staleness (commits-behind-main) per\n // bug_workspace_stale_parent_silent_drift. Pure observation: backends\n // never fetch; they read whatever the local refs cache says.\n const workspaces = await decorateWithStaleness(listWorkspaces(db, workstream));\n const workspaceOrphans = listWorkspaceOrphans(db, workstream);\n const recent = listLogs(db, { workstream, kind: \"event\", limit: eventLimit });\n return {\n workstreamName: workstream,\n view,\n tracks,\n ready,\n inProgress,\n blocked,\n recentClosed,\n workspaces,\n workspaceOrphans,\n recent,\n };\n}\n\n// ─── Workstream-set resolution ─────────────────────────────────────\n//\n// All three render modes accept TWO mutually-exclusive shapes (plus\n// auto-resolve):\n// -w X | -w X,Y | -w X -w Y explicit set (variadic + parseCsvFlag)\n// --all every workstream on this machine\n// (none) auto-resolve from $MU_SESSION/tmux (single ws)\n//\n// N=1 (single -w value, --all on a single-workstream machine, or\n// auto-resolve) renders single-mode (legacy column shape + flat JSON).\n// N≥2 grows the workstream-summary table to N rows (hud) or stacks\n// per-ws cards (full / mission).\n\ninterface StateOpts {\n // Variadic on every render mode.\n workstream?: string[];\n all?: boolean;\n json?: boolean;\n hud?: boolean;\n mission?: boolean;\n events?: number; // recent-events cap (default 20 full / 10 hud)\n lines?: number; // alias short-flag -n (hud muscle memory)\n}\n\nasync function resolveWorkstreamSet(db: Db, opts: StateOpts): Promise<string[]> {\n const explicitW = opts.workstream !== undefined && opts.workstream.length > 0;\n const explicitAll = opts.all === true;\n if (explicitAll && explicitW) {\n throw new UsageError(\"--all and -w/--workstream are mutually exclusive\");\n }\n if (explicitAll) {\n const all = await listWorkstreams(db);\n return all.map((w) => w.name);\n }\n if (explicitW) {\n // parseCsvFlag canonicalises repeat / comma / mixed forms into a\n // flat string[] (stripping whitespace + empty fragments).\n const names = parseCsvFlag(opts.workstream);\n const deduped = Array.from(new Set(names));\n if (deduped.length > 0) {\n // Strict validation: every entry must exist. A typo'd name\n // would silently render a half card.\n for (const n of deduped) {\n if (tryResolveWorkstreamId(db, n) === null) throw new WorkstreamNotFoundError(n);\n }\n return deduped;\n }\n }\n // No explicit -w (or it canonicalised away to nothing): auto-resolve\n // a single workstream from $MU_SESSION / tmux session.\n const single = await resolveOptionalWorkstream();\n if (single === null) return [];\n return [single];\n}\n\n// ─── JSON shape ─────────────────────────────────────────────────────\n//\n// Per merge_state_into_hud_render_mode (v0.3): unified single flat\n// shape across `mu state` and `mu state --hud`. `--mission` emits a\n// stripped subset for the muscle-memory glance use case.\n\nfunction fullJsonShape(d: PerWsData): Record<string, unknown> {\n return {\n workstreamName: d.workstreamName,\n agents: d.view.agents,\n orphans: d.view.orphans,\n tracks: d.tracks,\n ready: withRoiAll(d.ready),\n inProgress: withRoiAll(d.inProgress),\n blocked: withRoiAll(d.blocked),\n recentClosed: withRoiAll(d.recentClosed),\n workspaces: d.workspaces,\n workspaceOrphans: d.workspaceOrphans,\n recent: d.recent,\n };\n}\n\nfunction missionJsonShape(d: PerWsData): Record<string, unknown> {\n return {\n workstreamName: d.workstreamName,\n agents: d.view.agents,\n orphans: d.view.orphans,\n tracks: d.tracks,\n ready: withRoiAll(d.ready),\n };\n}\n\n// ─── cmdState — dispatch ────────────────────────────────────────────\n\nexport async function cmdState(db: Db, opts: StateOpts): Promise<void> {\n if (opts.hud === true && opts.mission === true) {\n throw new UsageError(\"--hud and --mission are mutually exclusive\");\n }\n\n // Mission is the only mode that survives a no-workstream context:\n // bare `mu` outside a tmux session is a discovery moment, not an\n // error. Default and --hud keep today's \"workstream required\"\n // failure (resolveWorkstreamSet throws via the resolveWorkstream\n // chain inside the explicit branches).\n if (opts.mission === true && (opts.workstream === undefined || opts.workstream.length === 0)) {\n if (opts.all !== true) {\n const auto = await resolveOptionalWorkstream();\n if (auto === null) {\n await renderMissionNoWorkstream(db, opts);\n return;\n }\n }\n }\n\n const workstreams = await resolveWorkstreamSet(db, opts);\n\n // --all on an empty machine: render empty hint cleanly.\n if (workstreams.length === 0) {\n if (opts.json === true) {\n emitJson({ workstreams: [] });\n return;\n }\n console.log(pc.dim(\"(no workstreams)\"));\n return;\n }\n\n const eventLimit = opts.events ?? opts.lines ?? (opts.hud === true ? 10 : 20);\n const perWs: PerWsData[] = [];\n for (const ws of workstreams) {\n perWs.push(await loadWorkstreamData(db, ws, eventLimit));\n }\n const multi = workstreams.length > 1;\n\n // ── JSON: render-mode-specific shape ──\n if (opts.json === true) {\n if (opts.mission === true) {\n if (multi) emitJson({ workstreams: perWs.map(missionJsonShape) });\n else {\n const single = perWs[0];\n if (single === undefined) throw new Error(\"invariant: workstreams non-empty\");\n emitJson(missionJsonShape(single));\n }\n return;\n }\n // Default + --hud share the same flat machine view.\n if (multi) emitJson({ workstreams: perWs.map(fullJsonShape) });\n else {\n const single = perWs[0];\n if (single === undefined) throw new Error(\"invariant: workstreams non-empty\");\n emitJson(fullJsonShape(single));\n }\n return;\n }\n\n // ── Human render: dispatch ──\n if (opts.mission === true) {\n renderMissionMode(perWs);\n return;\n }\n if (opts.hud === true) {\n await renderHudMode(db, perWs, eventLimit, multi, workstreams);\n return;\n }\n renderFullMode(perWs);\n}\n\n// ─── Render: full mode (default `mu state`) ────────────────────────\n\nfunction renderFullMode(perWs: PerWsData[]): void {\n perWs.forEach((d, i) => {\n if (i > 0) console.log(\"\");\n renderFullCard(d);\n });\n}\n\nfunction renderFullCard(d: PerWsData): void {\n const { workstreamName, view, tracks, ready, inProgress, blocked, recentClosed, recent } = d;\n const STALE_THRESHOLD = 10;\n const staleWorkspaces = d.workspaces.filter(\n (w) =>\n w.commitsBehindMain !== undefined &&\n w.commitsBehindMain !== null &&\n w.commitsBehindMain >= STALE_THRESHOLD,\n );\n\n console.log(pc.bold(`State of mu-${workstreamName}`));\n console.log(\"\");\n console.log(pc.bold(`Agents (${view.agents.length} active, ${view.orphans.length} orphan)`));\n console.log(formatAgentsTable(view.agents));\n if (view.orphans.length > 0) {\n for (const orphan of view.orphans) {\n console.log(\n ` ${pc.yellow(\"orphan\")} ${pc.dim(orphan.paneId)} title=${pc.bold(orphan.title)} cli=${orphan.command}`,\n );\n }\n }\n console.log(\"\");\n console.log(pc.bold(`Tracks (${tracks.length})`));\n console.log(formatTracks(tracks));\n console.log(\"\");\n console.log(pc.bold(`Ready (${ready.length})`));\n console.log(ready.length === 0 ? pc.dim(\" (none)\") : formatTaskListTable(ready));\n console.log(\"\");\n console.log(pc.bold(`In progress (${inProgress.length})`));\n console.log(inProgress.length === 0 ? pc.dim(\" (none)\") : formatTaskListTable(inProgress));\n console.log(\"\");\n console.log(pc.bold(`Blocked (${blocked.length})`));\n console.log(blocked.length === 0 ? pc.dim(\" (none)\") : formatTaskListTable(blocked));\n console.log(\"\");\n console.log(pc.bold(`Recent closed (${recentClosed.length})`));\n console.log(recentClosed.length === 0 ? pc.dim(\" (none)\") : formatTaskListTable(recentClosed));\n console.log(\"\");\n // Workspaces: warn line + tip when ANY row is ≥ STALE_THRESHOLD\n // commits behind main. Per bug_workspace_stale_parent_silent_drift.\n const workspacesHeader =\n staleWorkspaces.length > 0\n ? `${pc.bold(`Workspaces (${d.workspaces.length})`)} ${pc.yellow(`⚠ (${staleWorkspaces.length} stale ≥${STALE_THRESHOLD} commits behind):`)}`\n : pc.bold(`Workspaces (${d.workspaces.length})`);\n console.log(workspacesHeader);\n if (d.workspaces.length === 0) {\n console.log(pc.dim(\" (none)\"));\n } else {\n console.log(formatWorkspacesTable(d.workspaces));\n }\n if (staleWorkspaces.length > 0) {\n const example = staleWorkspaces[0]?.agentName ?? \"<agent>\";\n console.log(\n pc.yellow(\n `⚠ Tip: Free + recreate stale workspaces to land patches against current main: mu workspace free ${example} + mu workspace create ${example}`,\n ),\n );\n }\n if (d.workspaceOrphans.length > 0) {\n console.log(\"\");\n console.log(\n pc.yellow(\n `Workspace orphans (${d.workspaceOrphans.length}, on disk but no DB row — will block --workspace spawns):`,\n ),\n );\n for (const o of d.workspaceOrphans) {\n console.log(` ${pc.bold(o.agentName)} ${pc.dim(o.path)}`);\n }\n console.log(pc.dim(` Run \\`mu workspace orphans -w ${workstreamName}\\` for cleanup hints.`));\n }\n console.log(\"\");\n console.log(pc.bold(`Recent events (last ${recent.length} of kind=event)`));\n if (recent.length === 0) {\n console.log(pc.dim(\" (none)\"));\n } else {\n for (const row of recent) printLogRow(row);\n }\n}\n\n// ─── Render: mission mode (bare `mu` / `mu state --mission`) ───────\n\nfunction renderMissionMode(perWs: PerWsData[]): void {\n perWs.forEach((d, i) => {\n if (i > 0) console.log(\"\");\n renderMissionCard(d);\n });\n}\n\nfunction renderMissionCard(d: PerWsData): void {\n console.log(pc.bold(`mu-${d.workstreamName}`));\n console.log(\"\");\n console.log(pc.bold(`Agents (${d.view.agents.length})`));\n console.log(formatAgentsTable(d.view.agents));\n if (d.view.orphans.length > 0) {\n console.log(\"\");\n console.log(pc.yellow(`Orphan panes (${d.view.orphans.length})`));\n for (const orphan of d.view.orphans) {\n console.log(\n ` ${pc.dim(orphan.paneId)} title=${pc.bold(orphan.title)} cli=${orphan.command}`,\n );\n }\n }\n console.log(\"\");\n console.log(pc.bold(`Tracks (${d.tracks.length})`));\n console.log(formatTracks(d.tracks));\n console.log(\"\");\n console.log(pc.bold(`Ready (${d.ready.length})`));\n console.log(formatReadyTable(d.ready));\n}\n\n/**\n * Mission fallback when bare `mu` runs but no workstream resolves —\n * not in a tmux session, no `$MU_SESSION`, no `-w` flag. Show what\n * workstreams exist on this machine and a hint at next steps. Exit 0\n * (orientation, not failure). For `--json`, emit a structured shape so\n * scripts can detect the case without parsing prose.\n */\nasync function renderMissionNoWorkstream(db: Db, opts: StateOpts): Promise<void> {\n const summaries = await listWorkstreams(db);\n if (opts.json === true) {\n emitJson({ workstreamName: null, workstreams: summaries });\n return;\n }\n console.log(pc.dim(\"(no workstream resolved from $MU_SESSION or current tmux session)\"));\n console.log(\"\");\n if (summaries.length === 0) {\n console.log(\"No workstreams exist yet.\");\n console.log(\"\");\n console.log(\"Create one with:\");\n console.log(` ${pc.bold(\"mu workstream init <name>\")}`);\n console.log(\"\");\n console.log(\n `Then ${pc.bold(\"tmux a -t mu-<name>\")} to attach, or pass ${pc.bold(\"-w <name>\")}`,\n );\n console.log(\"to subsequent commands.\");\n return;\n }\n console.log(pc.bold(`Workstreams on this machine (${summaries.length})`));\n console.log(formatWorkstreamsTable(summaries));\n console.log(\"\");\n console.log(\"Pick one with any of:\");\n console.log(` ${pc.bold(\"tmux a -t mu-<name>\")} # attach to its tmux session`);\n console.log(` ${pc.bold(\"export MU_SESSION=<name>\")} # then bare \\`mu\\` resolves it`);\n console.log(\n ` ${pc.bold(\"mu -w <name>\")} (and similarly: ${pc.bold(\"mu state -w <name>\")}, etc.)`,\n );\n}\n\n// ─── Render: hud mode (--hud, dynamic-fit) ─────────────────────────\n//\n// Greedy top-down table layout that fills the available terminal (or\n// tmux pane) height + width with as much useful data as fits. No flags\n// to pick a size — the substrate is already telling us the size.\n//\n// Sections (in priority order — the higher one wins the budget when\n// squeezed):\n// 1. Header line (1 line)\n// 2. Agents table (always; usually small)\n// 3. Ready tasks table (operator's 'what to dispatch next')\n// 4. In-progress table ('what's already running')\n// 5. Tracks table (parallelism overview)\n// 6. Recent events table ('what just happened')\n//\n// Every section is a header-less cli-table3 with hint words baked into\n// each cell (\"agent worker-1\", \"ready build_x\", \"track 1\", etc.) so\n// no column headers are needed. Saves 2 vertical lines per table.\n\n/**\n * Resolve the HUD's render budget (width × height) from the substrate.\n *\n * Order:\n * 1. `MU_HUD_FORCE_SIZE=WxH` env override — deterministic for tests +\n * the only way an operator can force a non-default size on demand.\n * 2. `process.stdout` if it's a TTY (the easy path — same as every\n * other terminal app).\n * 3. `currentPaneSize()` (tmux's `display-message -p '#{pane_width}\n * #{pane_height}'`) — catches `watch -n 5 mu state --hud`,\n * `tmux display-popup -E 'mu state --hud'`, and any other case\n * where stdout is a pipe but the surrounding tmux pane has a real\n * size.\n * 4. `120 × 30` fallback — a wide-ish dev-laptop shape so a non-tmux\n * pipe still gets a usable layout.\n */\nasync function hudPaneSize(): Promise<{ width: number; height: number }> {\n const forced = process.env.MU_HUD_FORCE_SIZE;\n if (forced !== undefined) {\n const m = forced.match(/^(\\d+)x(\\d+)$/);\n if (m?.[1] !== undefined && m[2] !== undefined) {\n const width = Number.parseInt(m[1], 10);\n const height = Number.parseInt(m[2], 10);\n if (width > 0 && height > 0) return { width, height };\n }\n throw new UsageError(\n `MU_HUD_FORCE_SIZE must be 'WIDTHxHEIGHT' (e.g. '80x24'); got ${JSON.stringify(forced)}`,\n );\n }\n if (process.stdout.isTTY && process.stdout.columns && process.stdout.rows) {\n return { width: process.stdout.columns, height: process.stdout.rows };\n }\n const tmuxSize = await currentPaneSize().catch(() => undefined);\n if (tmuxSize !== undefined) return tmuxSize;\n return { width: 120, height: 30 };\n}\n\n/**\n * Build a header-less HUD table.\n *\n * `wordWrap: false` is the load-bearing setting: when a cell exceeds\n * its column width cli-table3 truncates with `…` instead of wrapping\n * to a second visual row. The HUD's row budget assumes one screen row\n * per data row — wrap would silently blow that out and push lower-\n * priority sections off the bottom.\n */\nfunction newHudTable(): InstanceType<typeof Table> {\n return new Table({ style: { border: [] }, wordWrap: false });\n}\n\n// In multi-mode, every per-workstream row needs its workstream tag so\n// we can prepend a leading `workstream` cell AND sort by (workstream,\n// intra-key). The SDK row types don't all carry workstream identity\n// (Track has no workstreamName field), so we tag at the load seam.\ntype Tagged<T> = { ws: string; row: T };\nconst tag =\n <T>(ws: string) =>\n (row: T): Tagged<T> => ({ ws, row });\n\nconst wsCell = (ws: string): string => pc.bold(pc.cyan(ws));\n\nfunction formatHudAgentsTable(\n db: Db,\n agents: readonly Tagged<AgentRow>[],\n width: number,\n rowCap: number,\n multi: boolean,\n): { rendered: string; rowsShown: number; rowsTotal: number } {\n const total = agents.length;\n const shown = agents.slice(0, rowCap);\n const now = Date.now();\n let nameW = \"agent \".length;\n let agoW = \"+ago\".length;\n let wsW = \"workstream\".length;\n const taskBits: string[] = [];\n for (const { ws, row: a } of shown) {\n nameW = Math.max(nameW, `agent ${a.name}`.length);\n wsW = Math.max(wsW, ws.length);\n // Scope by the agent's own workstream so a same-named worker\n // elsewhere can't pollute this row's task count.\n const owned = listTasksByOwner(db, a.workstreamName, a.name);\n const taskBit =\n owned.length === 0 ? \"—\" : owned.length === 1 ? (owned[0]?.name ?? \"—\") : `⊕${owned.length}`;\n taskBits.push(taskBit);\n const ago = `+${relTime(now - new Date(a.updatedAt).getTime())}`;\n agoW = Math.max(agoW, ago.length);\n }\n const statusW = 2;\n const numCols = multi ? 5 : 4;\n const padding = numCols * 3 + 1;\n const leadW = multi ? wsW : 0;\n const taskBudget = Math.max(8, width - leadW - statusW - nameW - agoW - padding);\n const table = newHudTable();\n shown.forEach(({ ws, row: a }, i) => {\n const ago = `+${relTime(now - new Date(a.updatedAt).getTime())}`;\n const taskBit = taskBits[i] ?? \"—\";\n const truncated = truncate(taskBit, taskBudget);\n const taskCell =\n taskBit === \"—\" || taskBit.startsWith(\"⊕\") ? pc.dim(truncated) : pc.cyan(truncated);\n // Idle (alive + assigned + no recent progress): prepend the ⚠\n // glyph to the status cell and yellow the agent name. Status text\n // stays truthful ('needs_input') — idle is a supplement, not a\n // 5th status. See idle_assigned_agent_detection.\n const idle = a.idle === true;\n const statusCell = idle\n ? `${pc.yellow(IDLE_GLYPH)} ${statusIcon(a.status)}`\n : statusIcon(a.status);\n const nameCell = idle\n ? `${pc.dim(\"agent\")} ${pc.yellow(a.name)}`\n : `${pc.dim(\"agent\")} ${pc.bold(a.name)}`;\n const cells: string[] = [];\n if (multi) cells.push(wsCell(ws));\n cells.push(statusCell, nameCell, taskCell, pc.dim(ago));\n table.push(cells);\n });\n return { rendered: table.toString(), rowsShown: shown.length, rowsTotal: total };\n}\n\nfunction formatHudTasksTable(\n tasks: readonly Tagged<TaskRow>[],\n width: number,\n rowCap: number,\n opts: { withOwner: boolean; multi: boolean },\n): { rendered: string; rowsShown: number; rowsTotal: number } {\n const total = tasks.length;\n const shown = tasks.slice(0, rowCap);\n const sectionPrefix = opts.withOwner ? \"in-progress\" : \"ready\";\n let idW = `${sectionPrefix} `.length;\n let roiW = \"ROI 100\".length;\n let ownerW = opts.withOwner ? \"owner\".length : 0;\n let wsW = \"workstream\".length;\n for (const { ws, row: t } of shown) {\n idW = Math.max(idW, `${sectionPrefix} ${t.name}`.length);\n const roi = t.effortDays > 0 ? (t.impact / t.effortDays).toFixed(0) : \"∞\";\n roiW = Math.max(roiW, `ROI ${roi}`.length);\n ownerW = Math.max(ownerW, (t.ownerName ?? \"—\").length);\n wsW = Math.max(wsW, ws.length);\n }\n const numCols = (opts.withOwner ? 4 : 3) + (opts.multi ? 1 : 0);\n const padding = numCols * 3 + 1;\n const leadW = opts.multi ? wsW : 0;\n const fixed = leadW + idW + roiW + (opts.withOwner ? ownerW : 0);\n const titleBudget = Math.max(10, width - fixed - padding);\n const table = newHudTable();\n for (const { ws, row: t } of shown) {\n const roiNum = t.effortDays > 0 ? t.impact / t.effortDays : Number.POSITIVE_INFINITY;\n const roiStr = Number.isFinite(roiNum) ? roiNum.toFixed(0) : \"∞\";\n const roiColor = roiNum >= 100 ? pc.green : roiNum >= 50 ? pc.yellow : pc.dim;\n const idCell = `${pc.dim(sectionPrefix)} ${pc.cyan(t.name)}`;\n const row: string[] = [];\n if (opts.multi) row.push(wsCell(ws));\n row.push(idCell, truncate(t.title, titleBudget), `${pc.dim(\"ROI\")} ${roiColor(roiStr)}`);\n if (opts.withOwner) row.push(t.ownerName ? pc.bold(pc.cyan(t.ownerName)) : pc.dim(\"—\"));\n table.push(row);\n }\n return { rendered: table.toString(), rowsShown: shown.length, rowsTotal: total };\n}\n\nfunction formatHudRecentTable(\n events: readonly Tagged<LogRow>[],\n width: number,\n rowCap: number,\n multi: boolean,\n): { rendered: string; rowsShown: number; rowsTotal: number } {\n const total = events.length;\n const shown = events.slice(0, rowCap);\n const now = Date.now();\n let agoW = \"+ago\".length;\n let wsW = \"workstream\".length;\n for (const { ws, row: e } of shown) {\n const ago = `+${relTime(now - new Date(e.createdAt).getTime())}`;\n agoW = Math.max(agoW, ago.length);\n wsW = Math.max(wsW, ws.length);\n }\n const numCols = multi ? 3 : 2;\n const padding = numCols * 3 + 1;\n const leadW = multi ? wsW : 0;\n const payloadBudget = Math.max(20, width - leadW - agoW - padding);\n const table = newHudTable();\n for (const { ws, row: e } of shown) {\n const ago = `+${relTime(now - new Date(e.createdAt).getTime())}`;\n const display = displayEventPayload(e.payload);\n const cells: string[] = [];\n if (multi) cells.push(wsCell(ws));\n cells.push(pc.dim(ago), colorEventPayload(truncate(display, payloadBudget)));\n table.push(cells);\n }\n return { rendered: table.toString(), rowsShown: shown.length, rowsTotal: total };\n}\n\n/** Recolour an event-log payload so the verb token (e.g. 'task close',\n * 'agent spawn', 'workspace create') stands out. Drives off\n * EVENT_VERB_PREFIXES in src/logs.ts — the same list the SDK uses to\n * document its emitter contract — so the HUD can't silently drift\n * away from the verbs callers actually emit (the original ad-hoc\n * regex did, missing `task block` / `approval granted` / `task\n * reparent`; see review_code_hud_event_color_regex_drift). Falls back\n * to the original string when nothing matches so we never lose\n * information just because we couldn't classify. Exported for tests.\n */\nexport function colorEventPayload(payload: string): string {\n for (const verb of EVENT_VERB_PREFIXES) {\n if (!payload.startsWith(verb)) continue;\n const next = payload.charCodeAt(verb.length);\n if (!Number.isNaN(next) && next !== 0x20 && next !== 0x09) continue;\n const rest = payload.slice(verb.length);\n return `${pc.cyan(verb)}${rest}`;\n }\n return payload;\n}\n\nfunction formatHudTracksTable(\n tracks: readonly Tagged<Track>[],\n width: number,\n rowCap: number,\n multi: boolean,\n): { rendered: string; rowsShown: number; rowsTotal: number } {\n const total = tracks.length;\n const shown = tracks.slice(0, rowCap);\n let idxW = \"track N\".length;\n let tasksW = \"N tasks\".length;\n let readyW = \"N ready\".length;\n let wsW = \"workstream\".length;\n const kindW = \"merged\".length;\n shown.forEach(({ ws, row: t }, i) => {\n idxW = Math.max(idxW, `track ${i + 1}`.length);\n tasksW = Math.max(tasksW, `${t.taskIds.size} tasks`.length);\n readyW = Math.max(readyW, `${t.readyCount} ready`.length);\n wsW = Math.max(wsW, ws.length);\n });\n const numCols = multi ? 6 : 5;\n const padding = numCols * 3 + 1;\n const leadW = multi ? wsW : 0;\n const rootsBudget = Math.max(10, width - leadW - idxW - tasksW - readyW - kindW - padding);\n const table = newHudTable();\n shown.forEach(({ ws, row: t }, i) => {\n const roots = t.roots.map((r) => r.name).join(\", \");\n const kind = t.roots.length > 1 ? \"merged\" : \"track\";\n const kindCell = t.roots.length > 1 ? pc.yellow(kind) : pc.dim(kind);\n const readyCell = `${t.readyCount > 0 ? pc.green(String(t.readyCount)) : pc.dim(\"0\")} ${pc.dim(\"ready\")}`;\n const cells: string[] = [];\n if (multi) cells.push(wsCell(ws));\n cells.push(\n `${pc.dim(\"track\")} ${pc.bold(String(i + 1))}`,\n pc.cyan(truncate(roots, rootsBudget)),\n `${t.taskIds.size} ${pc.dim(\"tasks\")}`,\n readyCell,\n kindCell,\n );\n table.push(cells);\n });\n return { rendered: table.toString(), rowsShown: shown.length, rowsTotal: total };\n}\n\n/** Compact agent-status histogram for the HUD header. Each emoji is\n * STATUS_COLORS-coloured so the same green/cyan/yellow signal that\n * appears in the agents table shows up in the summary cell too. */\nfunction agentStatusHistogram(agents: readonly AgentRow[]): string {\n const counts = new Map<AgentStatus, number>();\n for (const a of agents) counts.set(a.status, (counts.get(a.status) ?? 0) + 1);\n if (counts.size === 0) return pc.dim(\"none\");\n const parts: string[] = [];\n for (const [status, n] of counts) parts.push(`${statusIcon(status)}${n}`);\n return parts.join(\" \");\n}\n\nasync function renderHudMode(\n db: Db,\n perWs: PerWsData[],\n eventLimit: number,\n multi: boolean,\n workstreams: string[],\n): Promise<void> {\n const { width, height } = await hudPaneSize();\n let remaining = height;\n\n const printTable = (rendered: string): number => {\n console.log(rendered);\n return rendered.split(\"\\n\").length;\n };\n\n // 1. Workstream-summary table. ONE data row per workstream, no\n // header — each cell carries its own dim section word (`2 ready`,\n // `0 in-progress`, ...). Cost with bottom dropped + no header =\n // 2N+1 lines total.\n const colorCount = (n: number, color: (s: string) => string): string =>\n n > 0 ? color(String(n)) : pc.dim(\"0\");\n const headerTable = newHudTable();\n for (const d of perWs) {\n headerTable.push([\n pc.bold(pc.cyan(d.workstreamName)),\n `${colorCount(d.ready.length, pc.green)} ${pc.dim(\"ready\")}`,\n `${colorCount(d.inProgress.length, pc.yellow)} ${pc.dim(\"in-progress\")}`,\n `${pc.bold(String(d.tracks.length))} ${pc.dim(\"tracks\")}`,\n `${pc.bold(String(d.view.agents.length))} ${pc.dim(\"agents\")}`,\n agentStatusHistogram(d.view.agents),\n ]);\n }\n remaining -= printTable(headerTable.toString());\n\n // ── Union the per-workstream collections. Stable sort by\n // workstream so within-ws order is preserved (Array.prototype.sort\n // is stable in modern V8). In single-mode (multi=false) this is a\n // no-op since every row has the same ws.\n const allAgents: Tagged<AgentRow>[] = perWs.flatMap((d) =>\n d.view.agents.map(tag(d.workstreamName)),\n );\n const allReady: Tagged<TaskRow>[] = perWs.flatMap((d) => d.ready.map(tag(d.workstreamName)));\n const allInProgress: Tagged<TaskRow>[] = perWs.flatMap((d) =>\n d.inProgress.map(tag(d.workstreamName)),\n );\n const allTracks: Tagged<Track>[] = perWs.flatMap((d) => d.tracks.map(tag(d.workstreamName)));\n if (multi) {\n const byWs = (a: { ws: string }, b: { ws: string }): number => a.ws.localeCompare(b.ws);\n allAgents.sort(byWs);\n allReady.sort(byWs);\n allInProgress.sort(byWs);\n allTracks.sort(byWs);\n }\n // Recent events: union with DESC sort by created_at (cross-workstream\n // timeline view), then take the first eventLimit.\n const allRecent: Tagged<LogRow>[] = perWs.flatMap((d) => d.recent.map(tag(d.workstreamName)));\n allRecent.sort(\n (a, b) => new Date(b.row.createdAt).getTime() - new Date(a.row.createdAt).getTime(),\n );\n const recentBounded = allRecent.slice(0, eventLimit);\n\n // Helper: render a section if there's room, deducting its cost.\n // Sizing math (per section, all tables header-less, bottom kept):\n // actualCost(N) = 2N + 1 (top + N·(data + sep))\n // footer line = 1 (when truncated)\n const renderSection = (\n ren: (rowCap: number) => { rendered: string; rowsShown: number; rowsTotal: number },\n full: number,\n moreVerb: string,\n ): void => {\n if (full === 0) return; // section legitimately empty — don't render anything\n let rowCap: number;\n let willTruncate: boolean;\n if (2 * full + 1 <= remaining) {\n rowCap = full;\n willTruncate = false;\n } else {\n const slot = remaining - 2;\n rowCap = slot < 2 ? 0 : Math.floor(slot / 2);\n if (rowCap === 0) return;\n willTruncate = true;\n }\n const out = ren(rowCap);\n let cost = printTable(out.rendered);\n if (willTruncate && out.rowsShown < out.rowsTotal) {\n const extra = out.rowsTotal - out.rowsShown;\n console.log(pc.dim(` … +${extra} more (${moreVerb})`));\n cost += 1;\n }\n remaining -= cost;\n };\n\n // The \"more\" footer hints reference a single workstream by name in\n // single-mode; in multi-mode we drop the `-w` suffix and let the\n // operator pick which workstream they want to drill into.\n const moreScope = multi ? \"\" : ` -w ${workstreams[0]}`;\n\n // 2. Agents.\n renderSection(\n (cap) => formatHudAgentsTable(db, allAgents, width, cap, multi),\n allAgents.length,\n `mu agent list${moreScope}`,\n );\n\n // 3. Ready.\n renderSection(\n (cap) => formatHudTasksTable(allReady, width, cap, { withOwner: false, multi }),\n allReady.length,\n `mu task next -n 0${moreScope}`,\n );\n\n // 4. In progress.\n renderSection(\n (cap) => formatHudTasksTable(allInProgress, width, cap, { withOwner: true, multi }),\n allInProgress.length,\n `mu task list --status IN_PROGRESS${moreScope}`,\n );\n\n // 5. Tracks.\n renderSection(\n (cap) => formatHudTracksTable(allTracks, width, cap, multi),\n allTracks.length,\n `mu state${moreScope}`,\n );\n\n // 6. Recent events.\n renderSection(\n (cap) => formatHudRecentTable(recentBounded, width, cap, multi),\n recentBounded.length,\n `mu log${moreScope} --kind event`,\n );\n}\n\n// ─── commander wiring ────────────────────────────────────────────────\n//\n// wireStateCommands is called by buildProgram() in src/cli.ts. Wired\n// here so every per-namespace builder lives next to its cmd functions.\n\nimport type { Command } from \"commander\";\n\nexport function wireStateCommands(program: Command): void {\n program\n .command(\"state\")\n .description(\n \"Canonical state card: agents + orphans + tracks + ready/in-progress/blocked/recent-closed tasks + workspaces + recent events. The 'what does an LLM look at first?' verb. JSON-first. --hud switches to a dynamic-fit renderer that fills the terminal/pane (header + agents + ready + in-progress + tracks + recent — designed for `watch -n 5 mu state --hud` / `tmux display-popup -E 'mu state --hud'`); --mission emits the stripped 5-col glance card (agents + orphans + tracks + ready) — bare `mu` is an alias. -w accepts repeat or comma-separate (or both); --all is sugar for every workstream on this machine. N≥2 stacks per-workstream cards (full / mission) or unions with a leading workstream column (--hud).\",\n )\n .option(\n \"-w, --workstream <names...>\",\n \"workstream(s) to render (repeat or comma-separate; or both; defaults to $MU_SESSION or current tmux session)\",\n )\n .option(\"--all\", \"include every workstream on this machine\")\n .option(\"--hud\", \"dynamic-fit render: greedy top-down layout that fills the terminal/pane\")\n .option(\"--mission\", \"stripped 5-column glance card (agents + orphans + tracks + ready)\")\n .option(\n \"--events <n>\",\n \"how many recent kind=event log entries to include (default 20 for full / 10 for --hud)\",\n parseLines,\n )\n .option(\"-n, --lines <n>\", \"alias for --events (kept for --hud muscle memory)\", parseLines)\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as StateOpts;\n return handle((db) => cmdState(db, opts), this as Command)();\n });\n}\n","// mu — parallel-track detection via union-find with diamond merge.\n//\n// Port of a parallel-tracks union-find algorithm cribbed from a\n// prior internal multi-agent runtime. The killer feature: when two goals share a prerequisite, their subgraphs\n// overlap and they collapse into ONE track, so two agents are never\n// assigned tasks that share a dependency.\n//\n// goal_a goal_b goal_a goal_b\n// \\ / \\ /\n// shared → shared (1 track)\n// | |\n// leaf leaf\n//\n// Algorithm:\n// 1. Get all open goals (tasks with no outgoing edges, not CLOSED).\n// 2. For each goal, compute its prerequisite subgraph\n// (everything transitively reachable via reverse edges).\n// 3. Build union-find: merge any two goals whose subgraphs intersect.\n// 4. Each connected component is one Track.\n\nimport type { Db } from \"./db.js\";\nimport {\n STATUSES_TERMINAL_OR_PARKED,\n type TaskRow,\n getPrerequisites,\n listGoals,\n listReady,\n} from \"./tasks.js\";\n\nexport interface Track {\n /** Goal tasks (no outgoing edges) belonging to this track. */\n roots: TaskRow[];\n /** Every task id reachable as a prerequisite of any root in this track. */\n taskIds: ReadonlySet<string>;\n /** Number of READY tasks (per the SQL view) within this track's subgraph. */\n readyCount: number;\n}\n\n/**\n * Identify independent task subtrees suitable for parallel assignment\n * within a workstream. Open goals only; CLOSED goals are excluded as\n * they no longer represent work to schedule.\n *\n * Scoping: only goals belonging to `workstream` are considered.\n * Cross-workstream edges are forbidden by addTask, so a goal's\n * prerequisite subgraph is naturally workstream-internal.\n */\nexport function getParallelTracks(db: Db, workstream: string): Track[] {\n // listGoals already filters via the SQL view (NOT IN CLOSED/REJECTED/\n // DEFERRED), but defence-in-depth: a stale db snapshot or future view\n // tweak shouldn't let parked/terminal goals leak into track count.\n const goals = listGoals(db, workstream).filter(\n (g) => !STATUSES_TERMINAL_OR_PARKED.includes(g.status),\n );\n if (goals.length === 0) return [];\n\n // 2. Compute prerequisite subgraph for each goal.\n const subgraphs = new Map<string, Set<string>>();\n for (const goal of goals) {\n subgraphs.set(goal.name, getPrerequisites(db, goal.name, workstream));\n }\n\n // 3. Union-find: merge goals whose subgraphs overlap.\n const uf = new UnionFind(goals.map((g) => g.name));\n for (let i = 0; i < goals.length; i++) {\n const a = goals[i];\n if (!a) continue;\n for (let j = i + 1; j < goals.length; j++) {\n const b = goals[j];\n if (!b) continue;\n const subA = subgraphs.get(a.name);\n const subB = subgraphs.get(b.name);\n if (subA && subB && overlaps(subA, subB)) {\n uf.union(a.name, b.name);\n }\n }\n }\n\n // 4. Group goals + subgraph task ids by union-find root.\n const componentTaskIds = new Map<string, Set<string>>();\n const componentRoots = new Map<string, TaskRow[]>();\n for (const goal of goals) {\n const root = uf.find(goal.name);\n let bucket = componentTaskIds.get(root);\n if (!bucket) {\n bucket = new Set<string>();\n componentTaskIds.set(root, bucket);\n componentRoots.set(root, []);\n }\n componentRoots.get(root)?.push(goal);\n const sub = subgraphs.get(goal.name);\n if (sub) {\n for (const id of sub) bucket.add(id);\n }\n }\n\n // 5. Compute ready counts per track.\n const readyIds = new Set(listReady(db, workstream).map((t) => t.name));\n const tracks: Track[] = [];\n for (const [root, taskIds] of componentTaskIds) {\n const trackRoots = componentRoots.get(root) ?? [];\n let readyCount = 0;\n for (const id of taskIds) if (readyIds.has(id)) readyCount++;\n tracks.push({ roots: trackRoots, taskIds, readyCount });\n }\n\n // Stable order: by primary root's localId so output is deterministic.\n tracks.sort((a, b) => {\n const an = a.roots[0]?.name ?? \"\";\n const bn = b.roots[0]?.name ?? \"\";\n return an.localeCompare(bn);\n });\n return tracks;\n}\n\nfunction overlaps(a: Set<string>, b: Set<string>): boolean {\n // Iterate the smaller set for O(min(|a|, |b|)) lookups.\n const [small, large] = a.size <= b.size ? [a, b] : [b, a];\n for (const x of small) if (large.has(x)) return true;\n return false;\n}\n\nclass UnionFind {\n private readonly parent = new Map<string, string>();\n private readonly rank = new Map<string, number>();\n\n constructor(items: readonly string[]) {\n for (const item of items) {\n this.parent.set(item, item);\n this.rank.set(item, 0);\n }\n }\n\n find(x: string): string {\n let root = x;\n while (true) {\n const next = this.parent.get(root);\n if (next === undefined || next === root) break;\n root = next;\n }\n // Path compression.\n let curr = x;\n while (curr !== root) {\n const next = this.parent.get(curr);\n if (next === undefined) break;\n this.parent.set(curr, root);\n curr = next;\n }\n return root;\n }\n\n union(a: string, b: string): void {\n const rootA = this.find(a);\n const rootB = this.find(b);\n if (rootA === rootB) return;\n const rankA = this.rank.get(rootA) ?? 0;\n const rankB = this.rank.get(rootB) ?? 0;\n if (rankA < rankB) {\n this.parent.set(rootA, rootB);\n } else if (rankA > rankB) {\n this.parent.set(rootB, rootA);\n } else {\n this.parent.set(rootB, rootA);\n this.rank.set(rootA, rankA + 1);\n }\n }\n}\n","// mu — `mu workspace` verbs (create / list / free / path / orphans).\n//\n// Per-agent VCS workspaces (registry layer on top of vcs.ts).\n//\n// Extracted from src/cli.ts as part of refactor_split_large_src_files.\n\nimport {\n assertAgentInWorkstream,\n emitJson,\n emitJsonCollection,\n formatWorkspacesTable,\n resolveEntityRef,\n resolveWorkstream,\n} from \"../cli.js\";\nimport { type Db, WorkstreamNotFoundError, tryResolveWorkstreamId } from \"../db.js\";\nimport { type NextStep, pc, printNextSteps } from \"../output.js\";\nimport type { VcsBackendName } from \"../vcs.js\";\nimport {\n type StrandedWorkspaceOrphan,\n WorkspaceNotFoundError,\n createWorkspace,\n decorateWithStaleness,\n freeWorkspace,\n getWorkspaceForAgent,\n listAllOrphanWorkspaces,\n listCommitsForWorkspace,\n listWorkspaceOrphans,\n listWorkspaces,\n refreshWorkspace,\n} from \"../workspace.js\";\n\nexport async function cmdWorkspaceCreate(\n db: Db,\n rawAgent: string,\n opts: {\n workstream?: string;\n backend?: VcsBackendName;\n from?: string;\n projectRoot?: string;\n json?: boolean;\n },\n): Promise<void> {\n const { name: agent } = await resolveEntityRef(db, rawAgent, opts, \"workspace\");\n const workstream = await resolveWorkstream(opts.workstream);\n const createOpts: Parameters<typeof createWorkspace>[1] = { agent, workstream };\n if (opts.backend !== undefined) createOpts.backend = opts.backend;\n if (opts.from !== undefined) createOpts.parentRef = opts.from;\n if (opts.projectRoot !== undefined) createOpts.projectRoot = opts.projectRoot;\n const ws = await createWorkspace(db, createOpts);\n const nextSteps: NextStep[] = [\n { intent: \"cd into the workspace\", command: `cd $(mu workspace path ${agent})` },\n {\n intent: \"Free it later (with optional --commit)\",\n command: `mu workspace free ${agent} (--commit to commit pending changes first)`,\n },\n {\n intent: \"Spawn an agent that uses this workspace as cwd\",\n command: `mu agent spawn <name> -w ${workstream} --workspace`,\n },\n ];\n if (opts.json) {\n emitJson({ workspace: ws, nextSteps });\n return;\n }\n console.log(\n `Created workspace ${pc.bold(ws.path)} ${pc.dim(`(backend=${ws.backend}, agent=${ws.agentName}, parent=${ws.parentRef ?? \"—\"})`)}`,\n );\n printNextSteps(nextSteps);\n}\n\nexport async function cmdWorkspaceList(\n db: Db,\n opts: { workstream?: string; all?: boolean; json?: boolean },\n): Promise<void> {\n const workstream = opts.all ? undefined : await resolveWorkstream(opts.workstream);\n const rows = listWorkspaces(db, workstream);\n // decorateWithStaleness is the staleness signal from\n // bug_workspace_stale_parent_silent_drift: ask each row's backend how\n // many commits its parent_ref is behind main. Pure observation; no\n // automatic fetch.\n const decorated = await decorateWithStaleness(rows);\n if (opts.json) {\n emitJsonCollection(decorated);\n return;\n }\n if (decorated.length === 0) {\n console.log(pc.dim(workstream ? `(no workspaces in ${workstream})` : \"(no workspaces)\"));\n return;\n }\n console.log(formatWorkspacesTable(decorated));\n}\n\nexport async function cmdWorkspaceFree(\n db: Db,\n rawAgent: string,\n opts: { commit?: boolean; workstream?: string; json?: boolean },\n): Promise<void> {\n const { name: agent } = await resolveEntityRef(db, rawAgent, opts, \"workspace\");\n assertAgentInWorkstream(db, agent, opts.workstream);\n const ws = await resolveWorkstream(opts.workstream);\n const r = await freeWorkspace(db, agent, { commit: opts.commit ?? false, workstream: ws });\n if (opts.json) {\n emitJson({ agentName: agent, ...r });\n return;\n }\n if (!r.removed && !r.rowDeleted) {\n console.log(pc.dim(`no workspace for ${agent} (already gone?)`));\n return;\n }\n const committed = r.committedRef\n ? pc.dim(` (auto-committed: ${r.committedRef.slice(0, 12)})`)\n : \"\";\n console.log(`Freed workspace for ${pc.bold(agent)}${committed}`);\n}\n\nexport async function cmdWorkspaceRefresh(\n db: Db,\n rawAgent: string,\n opts: { workstream?: string; from?: string; json?: boolean } = {},\n): Promise<void> {\n const { name: agent } = await resolveEntityRef(db, rawAgent, opts, \"workspace\");\n assertAgentInWorkstream(db, agent, opts.workstream);\n const workstream = await resolveWorkstream(opts.workstream);\n const refreshOpts: Parameters<typeof refreshWorkspace>[1] = { agent, workstream };\n if (opts.from !== undefined) refreshOpts.fromRef = opts.from;\n const r = await refreshWorkspace(db, refreshOpts);\n const nextSteps: NextStep[] = [\n {\n intent: \"cd into the refreshed workspace\",\n command: `cd $(mu workspace path ${agent} -w ${workstream})`,\n },\n {\n intent: \"List commits replayed on top of the new base\",\n command: `mu workspace commits ${agent} -w ${workstream}`,\n },\n ];\n if (opts.json) {\n emitJson({ agent, ...r, nextSteps });\n return;\n }\n if (r.replayed.length === 0) {\n console.log(`Workspace ${pc.bold(agent)} already at ${pc.dim(r.fromRef)} — nothing to replay.`);\n } else {\n console.log(\n `Refreshed workspace ${pc.bold(agent)} onto ${pc.dim(r.fromRef)} ${pc.dim(`(backend=${r.vcs}, ${r.replayed.length} commit${r.replayed.length === 1 ? \"\" : \"s\"} replayed)`)}`,\n );\n for (const subject of r.replayed) {\n console.log(` ${pc.dim(\"•\")} ${subject}`);\n }\n }\n printNextSteps(nextSteps);\n}\n\nexport async function cmdWorkspaceCommits(\n db: Db,\n rawAgent: string,\n opts: { workstream?: string; since?: string; json?: boolean } = {},\n): Promise<void> {\n const { name: agent } = await resolveEntityRef(db, rawAgent, opts, \"workspace\");\n assertAgentInWorkstream(db, agent, opts.workstream);\n const workstream = await resolveWorkstream(opts.workstream);\n const listOpts: Parameters<typeof listCommitsForWorkspace>[2] = { workstream };\n if (opts.since !== undefined) listOpts.since = opts.since;\n const r = await listCommitsForWorkspace(db, agent, listOpts);\n if (opts.json) {\n emitJsonCollection(r.commits);\n return;\n }\n if (r.commits.length === 0) {\n console.log(pc.dim(`(no commits in ${agent} since ${r.baseRef.slice(0, 12)})`));\n return;\n }\n // Plain `<sha> <subject>` per line, oldest-first — the format the\n // dogfood incantation produced via `git log --oneline base..HEAD`.\n // Stays grep/awk/jq-friendly without --json.\n for (const c of r.commits) {\n console.log(`${c.sha} ${c.subject}`);\n }\n}\n\nexport async function cmdWorkspacePath(\n db: Db,\n rawAgent: string,\n opts: { workstream?: string; json?: boolean } = {},\n): Promise<void> {\n const { name: agent } = await resolveEntityRef(db, rawAgent, opts, \"workspace\");\n assertAgentInWorkstream(db, agent, opts.workstream);\n const wsName = await resolveWorkstream(opts.workstream);\n const ws = getWorkspaceForAgent(db, agent, wsName);\n if (!ws) throw new WorkspaceNotFoundError(agent);\n if (opts.json) {\n emitJson({ agentName: agent, path: ws.path, backend: ws.backend });\n return;\n }\n // Print just the path, no decoration: usable for `cd $(mu workspace path X)`.\n console.log(ws.path);\n}\n\nexport async function cmdWorkspaceOrphans(\n db: Db,\n opts: { workstream?: string; all?: boolean; json?: boolean } = {},\n): Promise<void> {\n // --all overrides scope: ignore -w and scan every workstream subdir\n // under <state-dir>/workspaces/, INCLUDING workstreams whose row is\n // gone (those orphans get `stranded: true`). See\n // workspace_orphans_misses_destroyed_workstreams.\n if (opts.all === true) {\n const orphans = listAllOrphanWorkspaces(db);\n const sample = orphans[0];\n const nextSteps: NextStep[] =\n sample === undefined\n ? []\n : [\n {\n intent: \"Remove a specific orphan dir (git: also prunes worktree registry)\",\n command: `(cd <project-root> && git worktree remove --force ${sample.path}) || rm -rf ${sample.path}`,\n },\n ];\n if (opts.json) {\n // audit_json_envelope_uniformity: collection envelope plus the\n // sibling nextSteps field. workstreamName is omitted because\n // --all spans the whole state dir.\n emitJson({ items: orphans, count: orphans.length, nextSteps });\n return;\n }\n if (orphans.length === 0) {\n console.log(pc.dim(\"(no orphan workspace dirs across all workstreams)\"));\n return;\n }\n console.log(pc.yellow(`${orphans.length} orphan workspace dir(s) across all workstreams:`));\n const grouped = new Map<string, StrandedWorkspaceOrphan[]>();\n for (const o of orphans) {\n const list = grouped.get(o.workstreamName) ?? [];\n list.push(o);\n grouped.set(o.workstreamName, list);\n }\n for (const [wsName, list] of grouped) {\n const sampleEntry = list[0];\n const strandedTag =\n sampleEntry?.stranded === true ? pc.red(\" (stranded: workstream destroyed)\") : \"\";\n console.log(` ${pc.bold(wsName)}${strandedTag}`);\n for (const o of list) {\n console.log(` ${pc.bold(o.agentName)} ${pc.dim(o.path)}`);\n }\n }\n printNextSteps(nextSteps);\n return;\n }\n\n const workstream = await resolveWorkstream(opts.workstream);\n // Tighten resolution: a typo'd or destroyed workstream name used to\n // happy-path to \"no orphans\" because listWorkspaceOrphans returns []\n // when the dir doesn't exist. Match the mutating verbs and surface\n // WorkstreamNotFoundError (exit 3) instead. See\n // workspace_orphans_misses_destroyed_workstreams option C.\n if (tryResolveWorkstreamId(db, workstream) === null) {\n throw new WorkstreamNotFoundError(workstream);\n }\n const orphans = listWorkspaceOrphans(db, workstream);\n const nextSteps: NextStep[] =\n orphans.length === 0\n ? []\n : [\n {\n intent: \"Remove a specific orphan dir (git: also prunes worktree registry)\",\n command: `(cd <project-root> && git worktree remove --force ${orphans[0]?.path}) || rm -rf ${orphans[0]?.path}`,\n },\n ];\n if (opts.json) {\n // audit_json_envelope_uniformity: items + count for the\n // collection envelope; workstreamName + nextSteps as siblings.\n emitJson({\n workstreamName: workstream,\n items: orphans,\n count: orphans.length,\n nextSteps,\n });\n return;\n }\n if (orphans.length === 0) {\n console.log(pc.dim(`(no orphan workspace dirs in ${workstream})`));\n return;\n }\n console.log(pc.yellow(`${orphans.length} orphan workspace dir(s) in ${pc.bold(workstream)}:`));\n for (const o of orphans) {\n console.log(` ${pc.bold(o.agentName)} ${pc.dim(o.path)}`);\n }\n printNextSteps(nextSteps);\n}\n\n// ─── commander wiring ────────────────────────────────────────────────\n//\n// wireWorkspaceCommands is called by buildProgram() in src/cli.ts. Wired here so\n// every per-namespace builder lives next to its cmd functions.\n\nimport type { Command } from \"commander\";\nimport { JSON_OPT, WORKSTREAM_OPT, handle } from \"../cli.js\";\n\nexport function wireWorkspaceCommands(program: Command): void {\n const workspace = program\n .command(\"workspace\")\n .description(\"VCS workspace commands (per-agent isolated working copies)\");\n\n workspace\n .command(\"create <agent>\")\n .description(\n \"Create a fresh isolated working copy for an agent. Backend auto-detected (jj > sl > git > none) unless --backend overrides.\",\n )\n .option(\"--backend <name>\", \"force a backend instead of auto-detecting (jj | sl | git | none)\")\n .option(\"--from <ref>\", \"base the workspace on a specific commit / branch / changeset\")\n .option(\"--project-root <path>\", \"override the project root to branch from (default: cwd)\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (agent: string) {\n const opts = (this as Command).opts() as {\n backend?: VcsBackendName;\n from?: string;\n projectRoot?: string;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdWorkspaceCreate(db, agent, opts), this as Command)();\n });\n\n workspace\n .command(\"list\")\n .description(\"List workspaces in the current workstream (--all spans every workstream)\")\n .option(\"--all\", \"list workspaces across every workstream\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as {\n all?: boolean;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdWorkspaceList(db, opts), this as Command)();\n });\n\n workspace\n .command(\"refresh <agent>\")\n .description(\n \"Rebase an agent's workspace onto a fresh base WITHOUT touching the agent or pane. Default base = the backend's tracked main (origin/HEAD for git, trunk() for jj/sl); override with --from <ref>. Refuses on dirty WC (git/sl) with the file list and a Next: hint to commit/stash. On rebase conflict, leaves the workspace in a resolvable state and exits 5 with a `cd` hint. The `none` backend errors (refresh requires a real VCS).\",\n )\n .option(\"--from <ref>\", \"override the rebase target (default: backend's tracked main)\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (agent: string) {\n const opts = (this as Command).opts() as {\n from?: string;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdWorkspaceRefresh(db, agent, opts), this as Command)();\n });\n\n workspace\n .command(\"free <agent>\")\n .description(\n \"Tear down an agent's workspace. With --commit, attempt to auto-commit pending changes first; without it, pending changes are lost.\",\n )\n .option(\"--commit\", \"auto-commit pending changes before removing the workspace\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (agent: string) {\n const opts = (this as Command).opts() as {\n commit?: boolean;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdWorkspaceFree(db, agent, opts), this as Command)();\n });\n\n workspace\n .command(\"commits <agent>\")\n .description(\n \"Print commits the agent's workspace has on top of its recorded parent_ref (the fork point), oldest-first. Default text output is `<sha> <subject>` per line; --json emits the full array `[{sha, subject, body, authorDate}]` for piping. --since <ref> overrides the base. The `none` backend errors (no fork point to compare against).\",\n )\n .option(\"--since <ref>\", \"override the base ref (default: workspace's recorded parent_ref)\")\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (agent: string) {\n const opts = (this as Command).opts() as {\n since?: string;\n workstream?: string;\n json?: boolean;\n };\n return handle((db) => cmdWorkspaceCommits(db, agent, opts), this as Command)();\n });\n\n workspace\n .command(\"path <agent>\")\n .description(\n \"Print the on-disk path of an agent's workspace. Usable as `cd $(mu workspace path foo)`.\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function (agent: string) {\n const opts = (this as Command).opts() as { workstream?: string; json?: boolean };\n return handle((db) => cmdWorkspacePath(db, agent, opts), this as Command)();\n });\n\n workspace\n .command(\"orphans\")\n .description(\n \"List on-disk workspace dirs in <state-dir>/workspaces/<workstream>/ that have no DB row. These block subsequent `--workspace` spawns; surfaced by bug_workspace_orphan_not_in_state. With --all, scans every workstream subdir under <state-dir>/workspaces/ INCLUDING workstreams whose row is gone (those rows are tagged `stranded: workstream destroyed`); --all overrides -w. With -w <unknown>, errors via WorkstreamNotFoundError (exit 3) instead of silently printing 'no orphans'. Cleanup recipe shown in Next: hints.\",\n )\n .option(\n \"--all\",\n \"scan every workstream subdir on disk (overrides -w; surfaces dirs whose workstream row is gone as `stranded`)\",\n )\n .option(...WORKSTREAM_OPT)\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as {\n workstream?: string;\n all?: boolean;\n json?: boolean;\n };\n return handle((db) => cmdWorkspaceOrphans(db, opts), this as Command)();\n });\n}\n","// mu — `mu workstream` verbs (init / list / destroy).\n//\n// A workstream = one tmux session (`mu-<name>`) + every DB row tagged\n// with that name (agents / tasks / edges / notes / workspaces / logs).\n// `init` creates the session + DB row pair; `list` shows\n// every workstream on the machine; `destroy` is the symmetric inverse,\n// two-phase by default (dry-run; `--yes` commits).\n//\n// Extracted from src/cli.ts as part of refactor_split_large_src_files.\n\nimport { join } from \"node:path\";\nimport { type AddToArchiveResult, addToArchive, getArchive } from \"../archives.js\";\nimport {\n UsageError,\n emitJson,\n emitJsonCollection,\n formatWorkstreamsTable,\n parseCsvFlag,\n resolveWorkstream,\n} from \"../cli.js\";\nimport { type Db, defaultStateDir } from \"../db.js\";\nimport { type ImportBucketResult, importBucket } from \"../importing.js\";\nimport { type NextStep, muTable, pc, printNextSteps } from \"../output.js\";\nimport { captureSnapshot } from \"../snapshots.js\";\nimport {\n enableMuPaneBordersForSession,\n listWindows,\n newSession,\n newWindow,\n sessionExists,\n} from \"../tmux.js\";\nimport {\n destroyWorkstream,\n ensureWorkstream,\n exportWorkstream,\n listEmptyWorkstreams,\n listWorkstreams,\n summarizeWorkstream,\n} from \"../workstream.js\";\n\nexport async function cmdInit(db: Db, name: string, opts: { json?: boolean } = {}): Promise<void> {\n const sessionName = `mu-${name}`;\n const dbCreated = ensureWorkstream(db, name);\n const tmuxAlready = await sessionExists(sessionName);\n let muWindowRepaired = false;\n if (!tmuxAlready) {\n await newSession(sessionName, { detached: true, windowName: \"_mu\" });\n } else {\n // Session already exists — check whether the placeholder `_mu`\n // window is still there. Common reason for it being missing:\n // operator killed it manually after spawning the first agent.\n // Without it, tmux a -t mu-<ws> lands on the most recent agent's\n // pane, which surprises the operator who expects an empty\n // orchestration shell. Recreate idempotently.\n // (review_bug_workstream_init_does_not_repair_missing_mu_window)\n const windows = await listWindows(sessionName).catch(() => []);\n const hasMuWindow = windows.some((w) => w.name === \"_mu\");\n if (!hasMuWindow) {\n await newWindow({\n session: sessionName,\n name: \"_mu\",\n command: process.env.SHELL ?? \"/bin/sh\",\n detached: true,\n });\n muWindowRepaired = true;\n }\n }\n // Always (re)apply the pane-border-status options so re-init or\n // upgrade-from-pre-banner-mu sessions both pick up the cue. tmux\n // set-option is idempotent. enableMuPaneBordersForSession self-checks\n // MU_BANNER_QUIET=1 (covers this and the spawn-time decoration; see\n // spawnAgent). Older tmux without pane-border-status support is benign\n // here: the cue is a nice-to-have, not load-bearing. Don't fail init.\n await enableMuPaneBordersForSession(sessionName).catch(() => {});\n const created = !tmuxAlready || dbCreated;\n const nextSteps: NextStep[] = [\n { intent: \"Attach the tmux session\", command: `tmux a -t ${sessionName}` },\n {\n intent: \"Plan tasks\",\n command: `mu task add -w ${name} --title \"...\" --impact 50 --effort-days 1`,\n },\n { intent: \"Spawn an agent\", command: `mu agent spawn <name> -w ${name}` },\n { intent: \"See state\", command: `mu state -w ${name}` },\n ];\n if (opts.json) {\n emitJson({\n workstreamName: name,\n sessionName,\n created,\n tmuxSessionAlreadyExisted: tmuxAlready,\n dbRowAlreadyExisted: !dbCreated,\n muWindowRepaired,\n nextSteps,\n });\n return;\n }\n if (tmuxAlready && !dbCreated) {\n const repaired = muWindowRepaired ? ` — ${pc.yellow(\"repaired missing _mu window\")}` : \"\";\n console.log(\n pc.dim(\n `workstream \"${name}\" already exists (tmux session ${sessionName}, DB row registered)${repaired}`,\n ),\n );\n printNextSteps(nextSteps);\n return;\n }\n console.log(`Created workstream ${pc.bold(name)} (tmux session ${pc.bold(sessionName)})`);\n printNextSteps(nextSteps);\n}\n\nexport async function cmdWorkstreamList(db: Db, opts: { json?: boolean } = {}): Promise<void> {\n const summaries = await listWorkstreams(db);\n if (opts.json) {\n emitJsonCollection(summaries);\n return;\n }\n if (summaries.length === 0) {\n console.log(pc.dim(\"no workstreams found (no DB rows, no mu-* tmux sessions)\"));\n return;\n }\n console.log(formatWorkstreamsTable(summaries));\n}\n\nexport async function cmdWorkstreamExport(\n db: Db,\n opts: { workstream?: string; out?: string; json?: boolean },\n): Promise<void> {\n const workstream = await resolveWorkstream(opts.workstream);\n const result = exportWorkstream(db, { workstream, outDir: opts.out });\n const nextSteps: NextStep[] = [\n { intent: \"Browse the bucket\", command: `ls ${result.outDir}` },\n {\n intent: \"Append another workstream to the same bucket (additive)\",\n command: `mu workstream export -w <other-ws> --out ${result.outDir}`,\n },\n {\n intent: \"Track in git\",\n command: `(cd ${result.outDir} && git init && git add . && git commit -m '${workstream} export')`,\n },\n ];\n if (opts.json) {\n emitJson({\n workstreamName: workstream,\n outDir: result.outDir,\n bucketLayoutVersion: result.manifest.bucketVersion,\n written: result.written,\n unchanged: result.unchanged,\n preserved: result.preserved,\n manifestPath: result.manifestPath,\n tasks: result.source.tasks,\n sourceCount: Object.keys(result.manifest.sources).length,\n nextSteps,\n });\n return;\n }\n console.log(\n `Exported ${pc.bold(workstream)} → ${pc.bold(result.outDir)} ${pc.dim(\n `(written=${result.written}, unchanged=${result.unchanged}, preserved=${result.preserved}; bucket sources=${Object.keys(result.manifest.sources).length})`,\n )}`,\n );\n printNextSteps(nextSteps);\n}\n\nexport async function cmdWorkstreamImport(\n db: Db,\n bucketDir: string,\n opts: {\n workstream?: string;\n dryRun?: boolean;\n json?: boolean;\n sourceWs?: string[];\n },\n): Promise<void> {\n // Canonicalise --source-ws via parseCsvFlag (repeat OR comma-separate\n // OR both, per cli_audit_plurality_uniformity). Distinguish \"flag\n // not passed\" (undefined) from \"passed but every entry is empty\"\n // (e.g. --source-ws ',,'): the latter is a UsageError so a typo\n // doesn't silently fall back to importing the entire bucket.\n let sourceWs: string[] | undefined;\n if (opts.sourceWs !== undefined) {\n const canonical = parseCsvFlag(opts.sourceWs);\n if (canonical.length === 0) {\n throw new UsageError(\n \"--source-ws was passed but resolved to zero names (empty strings / commas only); pass at least one source-ws name or drop the flag\",\n );\n }\n sourceWs = canonical;\n }\n const result: ImportBucketResult = importBucket(db, {\n bucketDir,\n workstreamOverride: opts.workstream,\n sourceWs,\n dryRun: opts.dryRun,\n });\n const totalTasks = result.sources.reduce((acc, s) => acc + s.tasksImported, 0);\n const totalEdges = result.sources.reduce((acc, s) => acc + s.edgesImported, 0);\n const totalNotes = result.sources.reduce((acc, s) => acc + s.notesImported, 0);\n const totalTombstones = result.sources.reduce((acc, s) => acc + s.tombstonesSkipped, 0);\n const importedNames = result.sources.map((s) => s.workstreamName);\n const nextSteps: NextStep[] = [];\n if (!opts.dryRun) {\n for (const name of importedNames) {\n nextSteps.push({\n intent: `Inspect ${name}`,\n command: `mu task tree -w ${name}`,\n });\n }\n nextSteps.push({\n intent: \"Re-export to verify the round trip is byte-stable\",\n command: `mu workstream export -w ${importedNames[0] ?? \"<ws>\"} --out <new-dir>`,\n });\n } else {\n const sourceWsFlag =\n sourceWs !== undefined && sourceWs.length > 0 ? ` --source-ws ${sourceWs.join(\",\")}` : \"\";\n nextSteps.push({\n intent: \"Run the import for real\",\n command: `mu workstream import ${bucketDir}${opts.workstream ? ` --workstream ${opts.workstream}` : \"\"}${sourceWsFlag}`,\n });\n }\n if (opts.json) {\n emitJson({\n ...result,\n bucketDir,\n dryRun: opts.dryRun === true,\n totals: {\n tasks: totalTasks,\n edges: totalEdges,\n notes: totalNotes,\n tombstones: totalTombstones,\n },\n nextSteps,\n });\n return;\n }\n const verb = opts.dryRun ? \"Would import\" : \"Imported\";\n console.log(\n `${verb} ${pc.bold(String(result.sources.length))} source-ws from ${pc.bold(bucketDir)} ${pc.dim(\n `(bucketVersion=${result.bucketVersion}${result.bucketLabel ? `, label=${result.bucketLabel}` : \"\"}; tasks=${totalTasks}, edges=${totalEdges}, notes=${totalNotes}, tombstones_skipped=${totalTombstones})`,\n )}`,\n );\n for (const s of result.sources) {\n console.log(\n ` ${pc.bold(s.workstreamName)}: tasks=${s.tasksImported}, edges=${s.edgesImported}, notes=${s.notesImported}, tombstones=${s.tombstonesSkipped}`,\n );\n }\n printNextSteps(nextSteps);\n}\n\n/** Default auto-export path used by `mu workstream destroy`'s\n * pre-destroy hook. Lives under the state directory so it survives\n * the destroy itself; the timestamp is suffixed so back-to-back\n * destroy/recreate cycles don't clobber prior exports. */\nfunction autoExportDir(workstream: string): string {\n const ts = new Date().toISOString().replace(/[:.]/g, \"-\");\n return join(defaultStateDir(), \"exports\", `${workstream}-${ts}`);\n}\n\nexport async function cmdDestroy(\n db: Db,\n opts: {\n workstream?: string;\n yes?: boolean;\n json?: boolean;\n export?: boolean;\n archive?: string;\n empty?: boolean;\n },\n): Promise<void> {\n if (opts.empty) {\n await cmdDestroyEmpty(db, opts);\n return;\n }\n const workstream = await resolveWorkstream(opts.workstream);\n // Validate --archive label FIRST so an unknown label refuses the\n // destroy entirely (anti-feature: no auto-create — operators run\n // `mu archive create <label>` themselves). getArchive throws\n // ArchiveNotFoundError on miss; classifyError maps that to exit 3.\n if (opts.archive !== undefined) {\n getArchive(db, opts.archive);\n }\n const summary = await summarizeWorkstream(db, { workstream });\n // Empty-but-registered workstreams (a row in `workstreams` with no\n // agents/tasks/etc.) ARE worth destroying — otherwise the bare\n // registry row is orphaned forever. nothingToDo is the strict\n // intersection: nothing on disk, in tmux, OR in the DB.\n const nothingToDo =\n !summary.tmuxAlive &&\n !summary.registered &&\n summary.agentCount === 0 &&\n summary.taskCount === 0 &&\n summary.noteCount === 0 &&\n summary.workspaceCount === 0;\n\n if (nothingToDo) {\n if (opts.json) {\n emitJson({\n workstreamName: workstream,\n destroyed: false,\n reason: \"nothing to destroy\",\n summary,\n });\n return;\n }\n console.log(\n pc.dim(`workstream \"${workstream}\" has no tmux session and no DB rows; nothing to destroy`),\n );\n return;\n }\n\n if (!opts.yes) {\n if (opts.json) {\n emitJson({\n workstreamName: workstream,\n destroyed: false,\n dryRun: true,\n summary,\n archive:\n opts.archive !== undefined\n ? { label: opts.archive, wouldArchiveTasks: summary.taskCount }\n : undefined,\n nextSteps: [\n {\n intent: \"Confirm and actually destroy\",\n command: `mu workstream destroy -w ${workstream} --yes${opts.archive !== undefined ? ` --archive ${opts.archive}` : \"\"}`,\n },\n {\n intent: \"After destroying, undo if you regret it (DB only; tmux NOT rolled back)\",\n command: \"mu undo --yes\",\n },\n ],\n });\n return;\n }\n console.log(pc.bold(`Workstream ${workstream} (tmux session ${summary.tmuxSession})`));\n console.log(\n ` tmux session : ${summary.tmuxAlive ? pc.yellow(\"alive (will be killed)\") : pc.dim(\"not running\")}`,\n );\n console.log(` agents : ${summary.agentCount}`);\n console.log(\n ` tasks : ${summary.taskCount} (edges: ${summary.edgeCount}, notes: ${summary.noteCount})`,\n );\n console.log(\n ` workspaces : ${summary.workspaceCount}${summary.workspaceCount > 0 ? pc.dim(\" (will be cleaned via per-backend remove)\") : \"\"}`,\n );\n if (opts.archive !== undefined) {\n console.log(\n ` archive : ${pc.yellow(`would archive ${summary.taskCount} tasks to ${opts.archive}`)}`,\n );\n }\n console.log(\"\");\n console.log(pc.dim(\"(dry-run; rerun with --yes to actually destroy)\"));\n console.log(\n pc.dim(\n \"A snapshot will be taken before the destroy; `mu undo --yes` reverts it (DB only — tmux panes / on-disk workspace dirs are NOT rolled back).\",\n ),\n );\n printNextSteps([\n {\n intent: \"Confirm and actually destroy\",\n command: `mu workstream destroy -w ${workstream} --yes`,\n },\n {\n intent: \"After destroying, undo if you regret it\",\n command: \"mu undo --yes\",\n },\n ]);\n return;\n }\n\n // Auto-export to the state dir BEFORE killing tmux / dropping rows.\n // Opt-out via --no-export. Per the originating design note: a failed\n // export must NOT block the destroy (warn + proceed) — operators\n // running destroy in a CI cleanup script should not be silently\n // gated by a transient disk error in an artifact dir.\n const autoExport = opts.export !== false;\n let autoExportOutDir: string | undefined;\n let autoExportError: string | undefined;\n if (autoExport) {\n const dir = autoExportDir(workstream);\n try {\n const exp = exportWorkstream(db, { workstream, outDir: dir });\n autoExportOutDir = exp.outDir;\n } catch (err) {\n autoExportError = err instanceof Error ? err.message : String(err);\n if (!opts.json) {\n console.log(\n pc.yellow(\n `WARNING: auto-export to ${dir} failed: ${autoExportError}; proceeding with destroy anyway`,\n ),\n );\n }\n }\n }\n\n // Archive BEFORE destroy: if the archive add fails, abort. We\n // already validated the label up top so the only reasons for a\n // throw here are transient (DB lock / disk error) — surface them\n // and leave the workstream untouched. No rollback needed (we\n // haven't destroyed yet).\n let archiveResult: AddToArchiveResult | undefined;\n if (opts.archive !== undefined) {\n archiveResult = addToArchive(db, opts.archive, workstream);\n }\n\n const result = await destroyWorkstream(db, { workstream });\n if (opts.json) {\n emitJson({\n workstreamName: workstream,\n destroyed: true,\n ...result,\n archive:\n opts.archive !== undefined && archiveResult !== undefined\n ? { label: opts.archive, ...archiveResult }\n : undefined,\n autoExport: autoExport\n ? { outDir: autoExportOutDir, error: autoExportError }\n : { skipped: true },\n // snap_destroy_safety: machine-readable hint that the destroy is\n // reversible (DB-only) via mu undo. Suppressed when there are\n // workspace failures so the cleanup steps stay the headline.\n nextSteps:\n result.failedWorkspaces.length === 0\n ? [\n {\n intent:\n \"Undo (a snapshot was taken before the destroy; DB only, tmux not rolled back)\",\n command: \"mu undo --yes\",\n },\n ]\n : undefined,\n });\n return;\n }\n console.log(pc.bold(`Workstream ${workstream} (tmux session ${summary.tmuxSession})`));\n console.log(\n ` tmux session : ${summary.tmuxAlive ? pc.yellow(\"alive (will be killed)\") : pc.dim(\"not running\")}`,\n );\n console.log(` agents : ${summary.agentCount}`);\n console.log(\n ` tasks : ${summary.taskCount} (edges: ${summary.edgeCount}, notes: ${summary.noteCount})`,\n );\n console.log(` workspaces : ${summary.workspaceCount}`);\n console.log(\"\");\n if (archiveResult !== undefined && opts.archive !== undefined) {\n console.log(\n `Archived ${pc.bold(workstream)} to ${pc.bold(opts.archive)} ${pc.dim(\n `(tasks=${archiveResult.addedTasks}, edges=${archiveResult.addedEdges}, notes=${archiveResult.addedNotes}, events=${archiveResult.addedEvents}, skipped_existing=${archiveResult.skippedTasks})`,\n )}`,\n );\n }\n console.log(\n `Destroyed ${pc.bold(workstream)}: killed tmux=${result.killedTmux}, agents=${result.deletedAgents}, tasks=${result.deletedTasks}, edges=${result.deletedEdges}, notes=${result.deletedNotes}, workspaces=${result.freedWorkspaces}/${summary.workspaceCount}${result.alreadyGoneWorkspaces > 0 ? ` (${result.alreadyGoneWorkspaces} already gone on disk)` : \"\"}`,\n );\n if (autoExportOutDir !== undefined) {\n console.log(pc.dim(`Pre-destroy export: ${autoExportOutDir}`));\n }\n // snap_destroy_safety: advertise the undo path that destroyWorkstream\n // gave us via captureSnapshot. Suppressed when there are workspace\n // failures so the WARNING + cleanup steps below stay the headline.\n if (result.failedWorkspaces.length === 0) {\n printNextSteps([\n {\n intent: \"Undo (a snapshot was taken before the destroy; DB only, tmux not rolled back)\",\n command: \"mu undo --yes\",\n },\n ]);\n }\n if (result.failedWorkspaces.length > 0) {\n console.log(\"\");\n console.log(\n pc.yellow(\n `WARNING: ${result.failedWorkspaces.length} workspace(s) could not be freed cleanly. The DB rows are gone (FK cascade); the on-disk paths remain and need manual cleanup:`,\n ),\n );\n for (const f of result.failedWorkspaces) {\n console.log(` - ${f.agent} (${f.backend}): ${f.path}`);\n console.log(` error: ${f.error}`);\n }\n printNextSteps([\n {\n intent: \"For each git worktree above, run\",\n command: \"git worktree remove --force <path>\",\n },\n { intent: \"For each jj workspace above, run\", command: \"jj workspace forget <name>\" },\n { intent: \"As a last resort\", command: \"rm -rf <path>\" },\n ]);\n }\n}\n\n// ─── cmdDestroyEmpty ─────────────────────────────────────────────────\n//\n// `mu workstream destroy --empty` sweeps every workstream with no\n// user-meaningful state (zero tasks, agents, vcs_workspaces).\n// One snapshot covers the whole sweep; per-workstream destroy errors\n// are accumulated into a `failed` array so a single bad pane doesn't\n// abort the rest of the cleanup. See workstream_destroy_empty_sweep.\n\ninterface EmptyDestroyResult {\n workstreamName: string;\n killedTmux: boolean;\n deletedAgents: number;\n deletedTasks: number;\n deletedNotes: number;\n deletedEdges: number;\n freedWorkspaces: number;\n alreadyGoneWorkspaces: number;\n}\n\ninterface EmptyDestroyFailure {\n workstreamName: string;\n error: string;\n}\n\n/** Read created_at for a registered workstream. Returns the empty\n * string for tmux-only rows that listEmptyWorkstreams won't surface\n * anyway (the predicate requires a workstreams row), keeping the\n * signature total. */\nfunction workstreamCreatedAt(db: Db, name: string): string {\n const row = db.prepare(\"SELECT created_at FROM workstreams WHERE name = ?\").get(name) as\n | { created_at: string }\n | undefined;\n return row?.created_at ?? \"\";\n}\n\nasync function cmdDestroyEmpty(\n db: Db,\n opts: {\n workstream?: string;\n yes?: boolean;\n json?: boolean;\n archive?: string;\n },\n): Promise<void> {\n // --empty is a sweep verb; -w (target a single workstream) and\n // --archive (snapshot one workstream INTO an archive) both contradict\n // it. Fail loud with exit 2 (UsageError) so a typo (`--empty -w foo`)\n // doesn't silently sweep instead of targeting `foo`.\n if (opts.workstream !== undefined) {\n throw new UsageError(\n \"--empty is mutually exclusive with -w/--workstream (the sweep targets every empty workstream; -w would contradict that)\",\n );\n }\n if (opts.archive !== undefined) {\n throw new UsageError(\n \"--empty is mutually exclusive with --archive (an empty workstream has nothing to archive; if you wanted to archive a single workstream's contents, drop --empty and use -w <name> --archive <label>)\",\n );\n }\n\n const empties = await listEmptyWorkstreams(db);\n\n if (!opts.yes) {\n if (opts.json) {\n emitJsonCollection(empties);\n return;\n }\n if (empties.length === 0) {\n console.log(pc.dim(\"no empty workstreams found\"));\n return;\n }\n const table = muTable({\n head: [\"workstream\", \"created_at\", \"tmux\"].map((h) => pc.bold(h)),\n colWidths: [40, null, null],\n });\n for (const ws of empties) {\n const createdAt = workstreamCreatedAt(db, ws.name);\n // Tmux-only entries have no DB row and so no created_at;\n // render an em-dash placeholder so the column never goes\n // visually empty (matches the tmux column's idiom below).\n const createdCell = createdAt === \"\" ? pc.dim(\"\\u2014\") : pc.dim(createdAt);\n table.push([ws.name, createdCell, ws.tmuxAlive ? pc.green(\"alive\") : pc.dim(\"\\u2014\")]);\n }\n console.log(table.toString());\n console.log(\"\");\n console.log(\n pc.dim(\n `${empties.length} empty workstream${empties.length === 1 ? \"\" : \"s\"} would be destroyed (dry-run; rerun with --yes to actually destroy).`,\n ),\n );\n console.log(\n pc.dim(\n \"A single whole-DB snapshot covers the whole sweep; `mu undo --yes` reverts it (DB only \\u2014 tmux NOT rolled back).\",\n ),\n );\n printNextSteps([\n {\n intent: \"Confirm and actually destroy every empty workstream\",\n command: \"mu workstream destroy --empty --yes\",\n },\n ]);\n return;\n }\n\n // --yes path. No-op early if there's nothing to do; do NOT take a\n // snapshot for a zero-row sweep (would clutter the snapshot log\n // with empty entries on every CI cleanup).\n if (empties.length === 0) {\n if (opts.json) {\n emitJson({ destroyed: 0, results: [], failed: [] });\n return;\n }\n console.log(pc.dim(\"no empty workstreams found; nothing to destroy\"));\n return;\n }\n\n // ONE snapshot covers the whole sweep. Per-call destroyWorkstream\n // would otherwise capture N snapshots (one per workstream), which is\n // both noisier and N× more disk for an operation the operator\n // logically thinks of as a single batch.\n captureSnapshot(\n db,\n `workstream destroy --empty sweep (${empties.length} workstream${empties.length === 1 ? \"\" : \"s\"})`,\n null,\n );\n\n const results: EmptyDestroyResult[] = [];\n const failed: EmptyDestroyFailure[] = [];\n for (const ws of empties) {\n try {\n const result = await destroyWorkstream(db, { workstream: ws.name });\n results.push({\n workstreamName: ws.name,\n killedTmux: result.killedTmux,\n deletedAgents: result.deletedAgents,\n deletedTasks: result.deletedTasks,\n deletedNotes: result.deletedNotes,\n deletedEdges: result.deletedEdges,\n freedWorkspaces: result.freedWorkspaces,\n alreadyGoneWorkspaces: result.alreadyGoneWorkspaces,\n });\n } catch (err) {\n // Best-effort sweep: log the failure and keep going. The snapshot\n // captured above is the recovery anchor for the whole batch, so\n // even a half-completed sweep is undoable.\n failed.push({\n workstreamName: ws.name,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n if (opts.json) {\n emitJson({ destroyed: results.length, results, failed });\n return;\n }\n for (const r of results) {\n console.log(\n `Destroyed ${pc.bold(r.workstreamName)} ${pc.dim(\n `(killedTmux=${r.killedTmux}, agents=${r.deletedAgents}, tasks=${r.deletedTasks}, notes=${r.deletedNotes}, edges=${r.deletedEdges})`,\n )}`,\n );\n }\n if (failed.length > 0) {\n console.log(\"\");\n console.log(\n pc.yellow(\n `WARNING: ${failed.length} workstream${failed.length === 1 ? \"\" : \"s\"} could not be destroyed cleanly:`,\n ),\n );\n for (const f of failed) {\n console.log(` - ${f.workstreamName}: ${f.error}`);\n }\n }\n console.log(\"\");\n console.log(pc.dim(`Sweep complete: destroyed=${results.length}, failed=${failed.length}.`));\n if (failed.length === 0) {\n printNextSteps([\n {\n intent: \"Undo (a snapshot was taken before the sweep; DB only, tmux not rolled back)\",\n command: \"mu undo --yes\",\n },\n ]);\n }\n}\n\n// ─── commander wiring ────────────────────────────────────────────────\n//\n// wireWorkstreamCommands is called by buildProgram() in src/cli.ts. Wired here so\n// every per-namespace builder lives next to its cmd functions.\n\nimport type { Command } from \"commander\";\nimport { JSON_OPT, WORKSTREAM_OPT, handle } from \"../cli.js\";\n\nexport function wireWorkstreamCommands(program: Command): void {\n const workstream = program.command(\"workstream\").description(\"Workstream-level commands\");\n\n workstream\n .command(\"init <name>\")\n .description(\"Create the workstream's tmux session and register it in the DB\")\n .option(...JSON_OPT)\n .action(function (name: string) {\n const opts = (this as Command).opts() as { json?: boolean };\n return handle((db) => cmdInit(db, name, opts), this as Command)();\n });\n\n workstream\n .command(\"list\")\n .description(\"List every workstream on this machine (DB rows + mu-* tmux sessions)\")\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as { json?: boolean };\n return handle((db) => cmdWorkstreamList(db, opts), this as Command)();\n });\n\n workstream\n .command(\"destroy\")\n .description(\n \"Tear down a workstream: kill its tmux session and cascade-delete every DB row tagged with its name. Pass --yes to actually destroy; otherwise prints a dry-run summary. With --empty, sweeps every empty workstream (zero tasks/agents/workspaces) in one call.\",\n )\n .option(...WORKSTREAM_OPT)\n .option(\"-y, --yes\", \"actually destroy (without this flag, prints a dry-run summary)\")\n .option(\"--no-export\", \"skip the pre-destroy markdown export to <state-dir>/exports/<ws>-<ts>/\")\n .option(\n \"--archive <label>\",\n \"in-DB archive label to add this workstream's contents to BEFORE destroy (atomic; if archive add fails, destroy aborts)\",\n )\n .option(\n \"--empty\",\n \"sweep every empty workstream (zero tasks, agents, vcs_workspaces); mutually exclusive with -w and --archive\",\n )\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as {\n workstream?: string;\n yes?: boolean;\n json?: boolean;\n export?: boolean;\n archive?: string;\n empty?: boolean;\n };\n return handle((db) => cmdDestroy(db, opts), this as Command)();\n });\n\n workstream\n .command(\"import <bucket-dir>\")\n .description(\n \"Rebuild a workstream (or a multi-source bucket of workstreams) from a v0.3 markdown export. Accepts EITHER a bucket directory (top-level manifest.json + per-source-ws subdirs) OR a single per-source-ws subdir (auto-detected via README.md + INDEX.md + tasks/, validated against the parent bucket's manifest). Per source-ws transactional: each source-ws is imported in its own SQLite transaction; siblings are unaffected by a sibling's failure. Refuses to merge silently into an existing workstream — pass --workstream <name> (single-source after any --source-ws filter only) or destroy the existing one first.\",\n )\n .option(\n \"--workstream <name>\",\n \"override the imported workstream's name (single-source after any --source-ws filter only)\",\n )\n .option(\n \"--source-ws <names...>\",\n \"restrict the import to a subset of source-ws subdirs (repeat or comma-separate; or both)\",\n )\n .option(\"--dry-run\", \"walk + parse + validate; report what WOULD be created; no DB writes\")\n .option(...JSON_OPT)\n .action(function (bucketDir: string) {\n const opts = (this as Command).opts() as {\n workstream?: string;\n dryRun?: boolean;\n json?: boolean;\n sourceWs?: string[];\n };\n return handle((db) => cmdWorkstreamImport(db, bucketDir, opts), this as Command)();\n });\n\n workstream\n .command(\"export\")\n .description(\n \"Render a workstream's task graph + notes to a bucket directory of markdown. Bucket layout: <out>/README.md + INDEX.md + manifest.json (bucketVersion 2) + <ws>/{README.md,INDEX.md,tasks/<id>.md}. Idempotent + additive: re-export refreshes only changed task files; passing -w with a different workstream into the same --out appends a sibling source-ws subdir; deleted tasks are preserved with a banner. Pre-0.3 export dirs are not migrated in place.\",\n )\n .option(...WORKSTREAM_OPT)\n .option(\"--out <dir>\", \"output directory (the bucket; defaults to ./<workstream>/)\")\n .option(...JSON_OPT)\n .action(function () {\n const opts = (this as Command).opts() as {\n workstream?: string;\n out?: string;\n json?: boolean;\n };\n return handle((db) => cmdWorkstreamExport(db, opts), this as Command)();\n });\n}\n","// mu — pure rendering helpers used by every cli/*.ts verb wrapper.\n//\n// Extracted from src/cli.ts (review_cli_ts_past_refactor_signal): the\n// table renderers, status colourers, truncators, and relative-time\n// formatter were the bulk of cli.ts's bloat (~600 LOC over the 800-LOC\n// refactor signal). Co-locating them here keeps cli.ts focused on\n// argument parsing, workstream resolution, and program wiring.\n//\n// All helpers are pure (no I/O beyond returning a string) except\n// `printLogRow` which writes a single line to stdout — kept here so\n// `mu log` and the `recent events` section of `mu state` share one\n// renderer. cli.ts re-exports every symbol for back-compat with the\n// existing import surface (tests + cli/* importers).\n\nimport { type AgentRow, type AgentStatus, STATUS_EMOJI } from \"../agents.js\";\nimport { type LogRow, displayEventPayload } from \"../logs.js\";\nimport { muTable, pc } from \"../output.js\";\nimport type { TaskRow } from \"../tasks.js\";\nimport type { Track } from \"../tracks.js\";\nimport type { WorkspaceRow } from \"../workspace.js\";\nimport type { WorkstreamSummary } from \"../workstream.js\";\n\n// ─── Status colours / icons ────────────────────────────────────────────\n\n/** Per-status colour for the table view. The glyph itself comes from\n * STATUS_EMOJI in src/agents.ts — single source of truth so the\n * table view and the pane-border / composeAgentTitle never drift\n * (review_code_status_emoji_two_sources caught a 2-of-7 disagreement). */\nconst STATUS_COLORS: Record<AgentStatus, (s: string) => string> = {\n spawning: pc.yellow,\n busy: pc.cyan,\n needs_input: pc.dim,\n needs_permission: pc.magenta,\n free: pc.green,\n unreachable: pc.red,\n terminated: pc.dim,\n};\n\nexport function statusIcon(status: AgentStatus): string {\n return STATUS_COLORS[status](STATUS_EMOJI[status]);\n}\n\n/**\n * Glyph used to flag the derived 'idle but assigned' state\n * (`AgentRow.idle === true`; idle_assigned_agent_detection). Plain\n * Unicode warning sign so it renders the same in cli-table3 cells\n * AND in single-line prose without needing a Nerd Font (the agent's\n * primary status glyph still uses Nerd Font via STATUS_EMOJI).\n */\nexport const IDLE_GLYPH = \"⚠\";\n\nexport function colorStatus(status: TaskRow[\"status\"]): string {\n switch (status) {\n case \"OPEN\":\n return pc.cyan(status);\n case \"IN_PROGRESS\":\n return pc.yellow(status);\n case \"CLOSED\":\n return pc.green(status);\n case \"REJECTED\":\n return pc.red(status);\n case \"DEFERRED\":\n return pc.dim(status);\n }\n}\n\n// ─── Width / truncation helpers ───────────────────────────────────────\n\n/**\n * Default fallback when stdout isn't a TTY (e.g. output is piped to\n * less/jq) and `process.stdout.columns` is undefined. 100 fits an 80-col\n * terminal with some breathing room; 100 is wide enough to keep most\n * rows on one line.\n */\nconst DEFAULT_TERMINAL_WIDTH = 100;\n\nfunction terminalWidth(): number {\n return process.stdout.columns ?? DEFAULT_TERMINAL_WIDTH;\n}\n\n/** Truncate `s` to fit `max` columns (counting display width as length;\n * good enough for ASCII titles, undercount for emoji/CJK — acceptable\n * trade-off given the terminal will visually clip anyway). Adds an\n * ellipsis when truncated. */\nexport function truncate(s: string, max: number): string {\n if (max <= 1) return s.slice(0, max);\n if (s.length <= max) return s;\n return `${s.slice(0, max - 1)}…`;\n}\n\n/** Front-truncate `s` to fit `max` columns, prepending an ellipsis when\n * truncated. Used for paths where the trailing `<workstream>/<agent>`\n * suffix is the only useful bit (the leading `~/.local/state/mu/...`\n * prefix is identical for every row). Surfaced live by `mu workspace\n * list` blowing the terminal width on the `path` column\n * (tables_truncate_long_cols_audit). */\nexport function truncateFront(s: string, max: number): string {\n if (max <= 1) return s.slice(-max);\n if (s.length <= max) return s;\n return `…${s.slice(-(max - 1))}`;\n}\n\n/** Format an elapsed duration (in ms) as a compact relative string:\n * '12s', '3m', '1h', '2d', '3w'. Used by the task list table when\n * `--sort recency`/`--sort age` is active so the timeframe the user\n * is sorting by is visible. Mirrors the helper in src/cli/state.ts\n * (hud render mode); kept in sync (the hud version omits weeks because it shows tighter\n * windows, but for task list a 5w-old item is real and worth a tag).\n */\nexport function relTime(ms: number): string {\n const sec = Math.max(0, Math.floor(ms / 1000));\n if (sec < 60) return `${sec}s`;\n const min = Math.floor(sec / 60);\n if (min < 60) return `${min}m`;\n const hr = Math.floor(min / 60);\n if (hr < 24) return `${hr}h`;\n const day = Math.floor(hr / 24);\n if (day < 7) return `${day}d`;\n return `${Math.floor(day / 7)}w`;\n}\n\n// ─── Table renderers ──────────────────────────────────────────────────\n\nexport function formatAgentsTable(agents: readonly AgentRow[]): string {\n if (agents.length === 0) return pc.dim(\" (no agents)\");\n // Cap the variable-width columns so a long tmux window name (or a\n // future free-text role) can't push the table past the terminal.\n // Other columns are bounded by their schemas (name 32-char cap,\n // cli is one of pi/codex/claude, status is one of OPEN/...).\n const table = muTable({\n head: [\n pc.bold(\"\"),\n pc.bold(\"name\"),\n pc.bold(\"cli\"),\n pc.bold(\"status\"),\n pc.bold(\"window\"),\n pc.bold(\"role\"),\n ],\n colWidths: [null, null, null, null, 32, 14],\n style: { head: [] },\n });\n for (const a of agents) {\n // Idle (alive + assigned + no recent progress): supplement the\n // status glyph with a yellow ⚠ prefix, and yellow the agent\n // name itself so the row is visually obvious. The status column\n // stays the truth ('needs_input') — the ⚠ is the supplement.\n const idle = a.idle === true;\n const glyphCell = idle\n ? `${pc.yellow(IDLE_GLYPH)} ${statusIcon(a.status)}`\n : statusIcon(a.status);\n const nameCell = idle ? pc.yellow(a.name) : a.name;\n table.push([\n glyphCell,\n nameCell,\n a.cli,\n a.status,\n a.tab ?? a.name,\n a.role === \"read-only\" ? pc.yellow(\"read-only\") : \"\",\n ]);\n }\n return table.toString();\n}\n\nexport function formatReadyTable(tasks: readonly TaskRow[]): string {\n if (tasks.length === 0) return pc.dim(\" (no ready tasks)\");\n // Sort by ROI descending.\n const sorted = [...tasks].sort((a, b) => b.impact / b.effortDays - a.impact / a.effortDays);\n // Same title-truncation treatment as formatTaskListTable so the\n // mission-control table doesn't blow out terminal width.\n let idW = \"name\".length;\n let impactW = \"impact\".length;\n let effortW = \"effort\".length;\n let roiW = \"ROI\".length;\n let ownerW = \"owner\".length;\n for (const t of sorted) {\n idW = Math.max(idW, t.name.length);\n impactW = Math.max(impactW, String(t.impact).length);\n effortW = Math.max(effortW, String(t.effortDays).length);\n const roi = (t.impact / t.effortDays).toFixed(1);\n roiW = Math.max(roiW, roi.length);\n ownerW = Math.max(ownerW, (t.ownerName ?? \"\").length);\n }\n const padding = 6 * 3 + 1; // 6 cols\n const titleBudget = Math.max(\n 20,\n terminalWidth() - (idW + impactW + effortW + roiW + ownerW) - padding,\n );\n\n // Title is pre-truncated above; use muTable so wordWrap:false acts\n // as a safety belt for any other cell we don't pre-trim.\n const table = muTable({\n head: [\n pc.bold(\"name\"),\n pc.bold(\"title\"),\n pc.bold(\"impact\"),\n pc.bold(\"effort\"),\n pc.bold(\"ROI\"),\n pc.bold(\"owner\"),\n ],\n style: { head: [] },\n });\n for (const t of sorted) {\n const roi = (t.impact / t.effortDays).toFixed(1);\n table.push([\n t.name,\n truncate(t.title, titleBudget),\n String(t.impact),\n String(t.effortDays),\n roi,\n t.ownerName ?? \"\",\n ]);\n }\n return table.toString();\n}\n\nexport function formatTracks(tracks: readonly Track[]): string {\n if (tracks.length === 0) return pc.dim(\" (no open tracks)\");\n const lines: string[] = [];\n tracks.forEach((track, i) => {\n const rootNames = track.roots.map((r) => r.name).join(\", \");\n const verb = track.roots.length > 1 ? \"merged\" : \"track\";\n lines.push(\n ` Track ${i + 1}: ${pc.bold(rootNames)} ${pc.dim(`(${track.taskIds.size} tasks, ${track.readyCount} ready, ${verb})`)}`,\n );\n });\n return lines.join(\"\\n\");\n}\n\n/** Workspaces table renderer. Used by `mu workspace list` and by\n * `mu state`'s Workspaces section — exported so cli/workspace.ts\n * can reuse it. The `behind` column shows how many commits the row's\n * parent_ref is behind the project's default branch HEAD; color-coded\n * green ≤2, yellow 3–9, red ≥10. Renders \"—\" when the count couldn't\n * be computed (no main resolvable, none-backend, missing parent_ref)\n * or wasn't asked for (no decorateWithStaleness call). Surfaced by\n * bug_workspace_stale_parent_silent_drift. */\nexport function formatWorkspacesTable(rows: readonly WorkspaceRow[]): string {\n // The path column is the one that bit operators today: an absolute\n // ~/.local/state/mu/workspaces/<ws>/worker-foo path runs ~70 chars\n // and pushed the table to ~200 cols. Front-truncate so the useful\n // trailing `<ws>/<agent>` suffix survives, then cap the column with\n // colWidths as a safety belt (tables_truncate_long_cols_audit).\n const PATH_BUDGET = 40;\n const table = muTable({\n head: [\"agent\", \"workstream\", \"backend\", \"path\", \"parent_ref\", \"behind\", \"created\"].map((h) =>\n pc.bold(h),\n ),\n colWidths: [null, null, null, PATH_BUDGET, null, null, null],\n });\n for (const r of rows) {\n table.push([\n r.agentName,\n r.workstreamName,\n r.backend,\n truncateFront(r.path, PATH_BUDGET - 2),\n r.parentRef ? pc.dim(r.parentRef.slice(0, 12)) : pc.dim(\"—\"),\n formatBehind(r.commitsBehindMain),\n pc.dim(r.createdAt),\n ]);\n }\n return table.toString();\n}\n\n/** Color-code the commits-behind-main count. Green ≤2 (fresh), yellow\n * 3–9 (drifting), red ≥10 (stale). Undefined / null renders as a dim\n * em-dash so the column stays well-typed even when staleness wasn't\n * computed. */\nfunction formatBehind(n: number | null | undefined): string {\n if (n === undefined || n === null) return pc.dim(\"—\");\n if (n <= 2) return pc.green(String(n));\n if (n <= 9) return pc.yellow(String(n));\n return pc.red(String(n));\n}\n\n/** One agent_logs row, human-formatted. Used by `mu log` (read + tail)\n * and by the `recent events` section of `mu state`. Exported so the\n * cli/log.ts module can reuse it. */\nexport function printLogRow(row: LogRow): void {\n const ws = row.workstreamName ?? pc.dim(\"—\");\n const time = row.createdAt.replace(\"T\", \" \").replace(/\\.\\d+Z$/, \"Z\");\n const kindColor =\n row.kind === \"event\" ? pc.cyan : row.kind === \"broadcast\" ? pc.yellow : (s: string) => s;\n // For `kind='event'`, strip the `task.claim<TAB>...` structured\n // prefix used by claim events; the human-readable prose tail is\n // what the user wants to see. Other event kinds pass through\n // unchanged. See review_code_last_claim_actor_brittle.\n const payload = row.kind === \"event\" ? displayEventPayload(row.payload) : row.payload;\n console.log(\n `${pc.dim(`#${row.seq}`)} ${pc.dim(time)} ${pc.bold(row.source)} ${kindColor(row.kind)} [${ws}] ${payload}`,\n );\n}\n\n/**\n * Workstreams summary table renderer. Used by `mu workstream list`\n * and `bare mu` (no-workstream discovery fallback). Both verbs render\n * the same shape; the helper lives here so cli/workstream.ts and\n * cli/state.ts can both import it without a lateral cli/* dependency.\n */\nexport function formatWorkstreamsTable(rows: WorkstreamSummary[]): string {\n // Workstream names are user-chosen free-form text; everything else\n // is a small int / fixed-shape token. Cap the name column so a long\n // workstream name doesn't push the row counts off-screen.\n const table = muTable({\n head: [\"name\", \"tmux\", \"agents\", \"tasks\", \"edges\", \"notes\"].map((h) => pc.bold(h)),\n colWidths: [40, null, null, null, null, null],\n });\n for (const r of rows) {\n table.push([\n r.name,\n r.tmuxAlive ? pc.green(\"alive\") : pc.dim(\"—\"),\n String(r.agentCount),\n String(r.taskCount),\n String(r.edgeCount),\n String(r.noteCount),\n ]);\n }\n return table.toString();\n}\n\nexport function formatTaskListTable(\n tasks: readonly TaskRow[],\n opts: { withWorkstream?: boolean; relTimeBasis?: \"updatedAt\" | \"createdAt\" } = {},\n): string {\n if (tasks.length === 0) return pc.dim(\" (no tasks)\");\n // The optional relative-time column is appended at the end; header\n // mirrors the timestamp basis (\"updated\" / \"created\") so a glance\n // says which sort key is live.\n const timeHeader =\n opts.relTimeBasis === \"updatedAt\"\n ? \"updated\"\n : opts.relTimeBasis === \"createdAt\"\n ? \"created\"\n : null;\n const baseHead = opts.withWorkstream\n ? [\"name\", \"workstream\", \"status\", \"title\", \"impact\", \"effort\", \"ROI\", \"owner\"]\n : [\"name\", \"status\", \"title\", \"impact\", \"effort\", \"ROI\", \"owner\"];\n const head = timeHeader === null ? baseHead : [...baseHead, timeHeader];\n\n // Pre-compute the relative-time strings (relative to NOW) so column\n // width and row text agree.\n const now = Date.now();\n const timeCells: string[] = [];\n if (opts.relTimeBasis !== undefined) {\n const basis = opts.relTimeBasis;\n for (const t of tasks) {\n const ts = basis === \"updatedAt\" ? t.updatedAt : t.createdAt;\n const ms = now - new Date(ts).getTime();\n timeCells.push(relTime(ms));\n }\n }\n\n // Compute a budget for the title column so the table fits the terminal.\n // Other columns are mostly short fixed-shape values; figure out how\n // wide they actually are, sum them up, and give title the leftover.\n const otherCols = opts.withWorkstream\n ? ([\"localId\", \"workstream\", \"status\", \"impact\", \"effortDays\", \"roi\", \"owner\"] as const)\n : ([\"localId\", \"status\", \"impact\", \"effortDays\", \"roi\", \"owner\"] as const);\n const widths = new Map<string, number>();\n for (const col of otherCols) widths.set(col, col.length); // header is the floor\n for (const t of tasks) {\n widths.set(\"localId\", Math.max(widths.get(\"localId\") ?? 0, t.name.length));\n if (opts.withWorkstream) {\n widths.set(\"workstream\", Math.max(widths.get(\"workstream\") ?? 0, t.workstreamName.length));\n }\n widths.set(\"status\", Math.max(widths.get(\"status\") ?? 0, t.status.length));\n widths.set(\"impact\", Math.max(widths.get(\"impact\") ?? 0, String(t.impact).length));\n widths.set(\"effortDays\", Math.max(widths.get(\"effortDays\") ?? 0, String(t.effortDays).length));\n const roi = t.effortDays > 0 ? (t.impact / t.effortDays).toFixed(1) : \"∞\";\n widths.set(\"roi\", Math.max(widths.get(\"roi\") ?? 0, roi.length));\n widths.set(\"owner\", Math.max(widths.get(\"owner\") ?? 0, (t.ownerName ?? \"—\").length));\n }\n let timeWidth = 0;\n if (timeHeader !== null) {\n timeWidth = timeHeader.length;\n for (const cell of timeCells) timeWidth = Math.max(timeWidth, cell.length);\n }\n // cli-table3 adds 2 chars of padding per cell + 1 char border per\n // column. Account for that to find the title budget.\n const numCols = head.length;\n const otherTotal = otherCols.reduce((acc, c) => acc + (widths.get(c) ?? 0), 0) + timeWidth;\n const padding = numCols * 3 + 1;\n const titleBudget = Math.max(20, terminalWidth() - otherTotal - padding);\n\n // Title is pre-truncated to titleBudget above; muTable adds the\n // wordWrap:false safety belt for any cell we don't trim.\n const table = muTable({\n head: head.map((h) => pc.bold(h)),\n });\n for (let i = 0; i < tasks.length; i++) {\n const t = tasks[i];\n if (!t) continue; // noUncheckedIndexedAccess\n const roi = t.effortDays > 0 ? (t.impact / t.effortDays).toFixed(1) : \"∞\";\n const title = truncate(t.title, titleBudget);\n const baseRow = opts.withWorkstream\n ? [\n t.name,\n t.workstreamName,\n colorStatus(t.status),\n title,\n String(t.impact),\n String(t.effortDays),\n roi,\n t.ownerName ?? pc.dim(\"—\"),\n ]\n : [\n t.name,\n colorStatus(t.status),\n title,\n String(t.impact),\n String(t.effortDays),\n roi,\n t.ownerName ?? pc.dim(\"—\"),\n ];\n const row = timeHeader === null ? baseRow : [...baseRow, pc.dim(timeCells[i] ?? \"\")];\n table.push(row);\n }\n return table.toString();\n}\n"],"mappings":"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","names":["readFileSync","dirname","join","fileURLToPath","InvalidArgumentError","resolve","out","mkdirSync","dirname","join","Database","rowFromDb","join","dirname","mkdirSync","Database","existsSync","readdirSync","join","resolve","existsSync","mkdirSync","statSync","dirname","join","existsSync","dirname","join","stat","statSync","mkdirSync","existsSync","dirname","join","workspacePath","existsSync","rmSync","homedir","join","resolve","workspacePath","rowFromDb","workspacePath","join","resolve","homedir","existsSync","rmSync","rows","result","join","existsSync","readdirSync","resolve","resolve","snap","rowFromDb","rowFromDb","row","rowFromDb","rows","reconcile","existsSync","readFileSync","readdirSync","statSync","dirname","join","readFileSync","i","require","existsSync","statSync","join","readdirSync","tasksDir","taskFiles","dirname","readFileSync","exitCode","workstream","n","result","Table","Table","ago","orphans","nextSteps","join","join","InvalidArgumentError","dirname","fileURLToPath","join","readFileSync"]}
|