@noir-ai/cli 1.14.0 → 1.15.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/bin.d.ts +10 -3
- package/dist/bin.js +1240 -365
- package/dist/bin.js.map +1 -1
- package/dist/index.d.ts +39 -12
- package/dist/index.js +1258 -383
- package/dist/index.js.map +1 -1
- package/dist/{output-BNE9htEm.d.ts → output-2T2lGR7a.d.ts} +3 -3
- package/dist/tui/{chunk-6LO4LXEH.js → chunk-5KWETEMF.js} +1191 -352
- package/dist/tui/chunk-5KWETEMF.js.map +1 -0
- package/dist/tui/{chunk-ESZTOQEC.js → chunk-KETMM3BA.js} +25 -3
- package/dist/tui/chunk-KETMM3BA.js.map +1 -0
- package/dist/tui/{chunk-2RK6AFZQ.js → chunk-TPNJEN4B.js} +2 -2
- package/dist/tui/chunk-TPNJEN4B.js.map +1 -0
- package/dist/tui/{create-FD4E626Z.js → create-6KDIFPUL.js} +10 -6
- package/dist/tui/create-6KDIFPUL.js.map +1 -0
- package/dist/tui/index.js +994 -120
- package/dist/tui/index.js.map +1 -1
- package/dist/tui/{install-YGTUSLRO.js → install-KA35ONQP.js} +2 -2
- package/dist/tui/{sync-N633SQSS.js → sync-AII5WCRO.js} +8 -4
- package/dist/tui/sync-AII5WCRO.js.map +1 -0
- package/dist/tui/{update-LET5BWOU.js → update-XGKJ4KBR.js} +3 -3
- package/package.json +11 -11
- package/dist/tui/chunk-2RK6AFZQ.js.map +0 -1
- package/dist/tui/chunk-6LO4LXEH.js.map +0 -1
- package/dist/tui/chunk-ESZTOQEC.js.map +0 -1
- package/dist/tui/create-FD4E626Z.js.map +0 -1
- package/dist/tui/sync-N633SQSS.js.map +0 -1
- /package/dist/tui/{install-YGTUSLRO.js.map → install-KA35ONQP.js.map} +0 -0
- /package/dist/tui/{update-LET5BWOU.js.map → update-XGKJ4KBR.js.map} +0 -0
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/daemon-client.ts","../src/theme.ts","../src/output.ts","../src/commands/context.ts","../src/workspace-mcp.ts","../src/commands/workspace.ts","../src/commands/daemon.ts","../src/commands/doctor.ts","../src/commands/env.ts","../src/commands/install.ts","../src/commands/update.ts","../src/commands/status.ts","../src/commands/task.ts","../src/commands/handoff.ts","../src/banner.ts","../src/tui/commands/registry.ts","../src/tui/commands/sections.ts","../src/commands/home.ts","../src/commands/memory.ts","../src/commands/release.ts","../src/shell-bridge.ts","../src/orchestrator.ts","../src/run-profiles.ts","../src/commands/run.ts","../src/commands/skills.ts","../src/conflict.ts","../src/dedup-write.ts","../src/init.ts","../src/serve.ts","../src/commands/create.ts","../src/sync.ts","../src/bin.ts"],"sourcesContent":["// S9 t3 — Daemon MCP client.\n//\n// Store-touching commands (`status`, `context *`, `memory *`, `task *`) are thin\n// MCP clients over the running Noir daemon rather than opening the store\n// in-process: that preserves the daemon's single-writer discipline and reuses\n// the MCP tool surface built in S6. This module is the single bridge from\n// the CLI to that daemon — every command module calls {@link callDaemonTool} (or\n// the multi-call {@link withDaemon}) instead of importing the store directly.\n//\n// Flow: {@link ensureDaemonRunning} (from @noir-ai/daemon) reads THIS project's\n// daemon record (`~/.noir/daemons/<projectId>.json`, NOIR_DAEMON_DIR override\n// for tests) and STARTS a foreground daemon if no healthy one is present,\n// returning its URL + a `stop()` tear-down. We connect a\n// @modelcontextprotocol/client `Client` over\n// Streamable HTTP to that URL (127.0.0.1 only — daemon §4), initialize, and\n// `callTool(name, args)`; the daemon's tools always return a single text content\n// block whose `text` is `JSON.stringify(payload)` (see `textResult` in\n// packages/daemon/src/server.ts), so we parse it back into the payload object.\n//\n// Failure handling (S9 exit-code contract): ANY failure to reach or use the\n// daemon — record missing/stale, ensure-start error, transport, connect,\n// protocol, or a tool call that rejects / returns no parseable text — maps to\n// exit code `4` (DAEMON_DOWN) with a stable remediation hint. `--verbose` surfaces\n// the underlying cause on stderr; `--json` shapes the failure as the S9\n// `{ok:false,error:{code,message}}` envelope ON STDOUT via `output.ts:fail`\n// (data→stdout discipline — the single canonical envelope is shaped once there;\n// `failDaemonDown` passes the PLAIN hint as the message, never a pre-stringified\n// inner envelope, which would doubly-encode `error.message`). A tool that returns\n// a logical-failure envelope (`{ok:false, degraded:true, …}`) is NOT a transport\n// failure — it parses cleanly and is returned to the caller as data.\n//\n// Auth (spec 6.1): the HTTP transport requires a bearer token, regenerated by\n// the daemon on every start and stored 0600 at `~/.noir/daemons/<scopeKey>.token`\n// (`readDaemonToken`). The scope key is the daemon's IDENTITY: a project daemon's\n// projectId, a workspace daemon's workspace NAME (spec §6.3). Every `/mcp`\n// connect below sends the token for the daemon it is addressing via\n// `httpTransport` — never another identity's; `/health` is token-free, so the\n// liveness probe and the daemon record are unaffected. The stdio transport never\n// uses a token (no network surface, no header trust).\n\nimport { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';\nimport { ContextEngine, createEmbedFn, resolveEmbedderConfig } from '@noir-ai/context';\nimport {\n loadProjectInfo,\n NOIR_VERSION,\n type ProjectInfo,\n readWorkspaceMarker,\n workspaceStoreDbPath,\n} from '@noir-ai/core';\nimport {\n ensureDaemonRunning,\n pidAlive,\n readDaemonToken,\n readProjectDaemonRecord,\n readWorkspaceDaemonRecord,\n resolveGateConfig,\n tokenPath,\n} from '@noir-ai/daemon';\nimport { createMemoryEngine, type MemoryEngine } from '@noir-ai/memory';\nimport { openStore, type Store } from '@noir-ai/store';\nimport { WorkflowEngine } from '@noir-ai/workflow';\nimport { EXIT, fail } from './bin.js';\n\n/** Options shared by every daemon-client entry point. */\nexport interface DaemonClientOptions {\n /** Emit the daemon-down failure as a JSON `{ok,error}` envelope (S9 --json). */\n readonly json?: boolean;\n /** Surface transport / connection detail on stderr (S9 --verbose). */\n readonly verbose?: boolean;\n /**\n * Project used to resolve the daemon idle timeout. Defaults to\n * `loadProjectInfo(process.cwd())` — the bin's `--cwd` preAction hook has\n * already chdir'd, so process.cwd() honors `--cwd`.\n */\n readonly project?: ProjectInfo;\n /** Override the daemon idle timeout (sec). Defaults to the project's config. */\n readonly idleTimeoutSec?: number;\n}\n\n/** A handle for calling daemon MCP tools over an already-connected client. */\nexport interface DaemonToolCaller {\n /**\n * Call a daemon MCP tool and parse its text-JSON result. Transport / parse\n * failures throw an S9 {@link NoirCliError} (exit 4); a tool's own logical\n * failure envelope (`{ok:false,…}`) is returned as-is.\n */\n callTool<T = unknown>(name: string, args?: Record<string, unknown>): Promise<T>;\n /**\n * List the names of the tools the connected daemon currently exposes.\n * Required so a command can tell \"the daemon is up but does not register\n * tool X\" (an opt-in / engine-not-wired condition, e.g. `memory_consolidate`\n * when consolidation is disabled) apart from \"the daemon is down\" — calling\n * an unregistered tool would otherwise mis-map onto exit 4 (DAEMON_DOWN).\n * Transport failures throw exit 4 exactly like {@link callTool}.\n */\n listTools(): Promise<string[]>;\n}\n\n/** Daemon-down remediation hint (S9). Stable across releases. */\nexport const DAEMON_DOWN_HINT = 'daemon not reachable — try `noir daemon start`';\n\n/**\n * Bounded /health probe window (ms). A probe must never hang: a stale daemon\n * record can point at a port bound by an unrelated process that accepts TCP but\n * never answers, and the probe is now the FIRST hop for every read command's\n * fallback decision. A timeout is treated as \"not running\" (reads degrade to the\n * read-only engine); the bound also keeps the daemon-up fast path snappy.\n */\nexport const PROBE_TIMEOUT_MS = 1500;\n\nfunction describeCause(cause: unknown): string {\n if (cause instanceof Error) return `${cause.name}: ${cause.message}`;\n try {\n return JSON.stringify(cause);\n } catch {\n return String(cause);\n }\n}\n\n/**\n * Map any daemon / transport failure onto the S9 DAEMON_DOWN exit code. Always\n * throws (via {@link fail}); the `never` return lets callers use it as a\n * diverging expression inside parsed result handling.\n */\nfunction failDaemonDown(opts: DaemonClientOptions, cause: unknown): never {\n if (opts.verbose) {\n process.stderr.write(`noir: daemon transport detail: ${describeCause(cause)}\\n`);\n }\n // Pass the PLAIN hint + opts and let `output.ts:fail` shape the single S9\n // `{ok:false,error:{code:EXIT.DAEMON_DOWN,message:hint}}` envelope ONCE. The\n // prior branch pre-stringified an inner `{ok,error}` and passed it as `message`,\n // which `fail` then wrapped AGAIN → `error.message` was a JSON string\n // (doubly-encoded). `opts` carries `--json`/`--verbose` so `fail` selects the\n // stdout envelope vs. the plain stderr line.\n fail(EXIT.DAEMON_DOWN, DAEMON_DOWN_HINT, opts);\n}\n\n// ---------------------------------------------------------------------------\n// Liveness probe (read-only). `noir status` uses this to report daemon state\n// HONESTLY without auto-starting one: it reads the daemon record + checks the\n// pid + GETs /health, and returns {running:false} on any miss. It NEVER calls\n// ensureDaemonRunning. Active commands keep using {@link withDaemon} (which may\n// start a daemon); only the informational `status` command is probe-only.\n// ---------------------------------------------------------------------------\n\n/** Outcome of a read-only daemon liveness probe (never starts a daemon). */\nexport interface DaemonProbe {\n running: boolean;\n pid?: number;\n port?: number;\n uptimeSec?: number;\n}\n\n/**\n * Probe whether a healthy daemon is currently running, WITHOUT starting one.\n * Resolves the CALLER's project id first (that id IS the record lookup key),\n * reads the per-project record via {@link readProjectDaemonRecord}, checks\n * {@link pidAlive}, and GETs `http://127.0.0.1:<port>/health`. Any miss (no\n * project, no record, stale pid, non-200, unreachable, malformed body) →\n * `{running:false}` — this function NEVER throws and NEVER starts a daemon.\n * Under `--verbose` the reason for a miss is logged to stderr (NF5: honest\n * degradation).\n */\nexport async function probeDaemon(opts: DaemonClientOptions = {}): Promise<DaemonProbe> {\n // Resolve the project FIRST — with per-project records the ProjectId IS the\n // lookup key, so the record cannot be read before it is known.\n const expectedProject =\n opts.project?.id ??\n (() => {\n try {\n return loadProjectInfo(process.cwd()).id;\n } catch {\n return undefined; // uninitialized — no caller project to look up\n }\n })();\n if (expectedProject === undefined) {\n if (opts.verbose) process.stderr.write('noir: daemon probe: no project (uninitialized)\\n');\n return { running: false };\n }\n const rec = readProjectDaemonRecord(expectedProject);\n if (!rec) {\n if (opts.verbose) process.stderr.write('noir: daemon probe: no daemon record\\n');\n return { running: false };\n }\n if (!pidAlive(rec.pid)) {\n if (opts.verbose) process.stderr.write(`noir: daemon probe: pid ${rec.pid} not alive\\n`);\n return { running: false };\n }\n try {\n // Bounded probe: a stale daemon record pointing at a port held by an\n // unrelated process that accepts TCP but never responds (a blackhole) must\n // NOT hang the probe (or any read command that probes first). Treat a\n // timeout as \"not running\" so reads fall back to the read-only engine.\n const res = await fetch(`http://127.0.0.1:${rec.port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n if (!res.ok) {\n if (opts.verbose) process.stderr.write(`noir: daemon probe: /health → HTTP ${res.status}\\n`);\n return { running: false };\n }\n const body = (await res.json().catch(() => null)) as {\n ok?: boolean;\n pid?: number;\n uptimeSec?: number;\n } | null;\n // PID-reuse guard (same invariant as @noir-ai/daemon ensure.ts isHealthy +\n // commands/daemon.ts isHealthy): the responding /health must carry OUR\n // recorded pid — a missing or mismatched pid means a foreign process holds\n // the port; report NOT running. (The record is already scoped to the caller's\n // project, so there is no cross-project body check left to perform.)\n const pidOk = typeof body?.pid === 'number' && body.pid === rec.pid;\n if (body?.ok !== true || !pidOk) {\n if (opts.verbose)\n process.stderr.write(\n !pidOk\n ? 'noir: daemon probe: pid missing/mismatched (foreign process on recorded port)\\n'\n : 'noir: daemon probe: /health body not ok\\n',\n );\n return { running: false };\n }\n return {\n running: true,\n pid: typeof body.pid === 'number' ? body.pid : rec.pid,\n port: rec.port,\n uptimeSec: typeof body.uptimeSec === 'number' ? body.uptimeSec : undefined,\n };\n } catch (err) {\n if (opts.verbose)\n process.stderr.write(`noir: daemon probe: unreachable (${describeCause(err)})\\n`);\n return { running: false };\n }\n}\n\n/**\n * Resolve a CONNECTED daemon client for the caller's project, plus the\n * tear-down for any daemon this call started. Maps any start / connect error\n * onto exit 4.\n *\n * Ordering is CONNECT-FIRST (spec 7.1). When `daemon.port` gives a stable\n * address we attempt the MCP session against it BEFORE spawning anything, so a\n * daemon starts because a connection was genuinely attempted rather than as a\n * side effect of running a command. Only when that attempt fails — a refused\n * connection is the common case — do we fall through to\n * {@link ensureDaemonRunning} (still the single spawn primitive; only the\n * DECISION POINT moved) and connect to whatever it returns. Without a\n * configured `daemon.port` there is no address to probe, so the previous\n * ensure-first ordering is retained verbatim (spec 7.1: D depends on B).\n *\n * The failure path is BOUNDED, in both senses. Structurally: at most one direct\n * attempt, one ensure, and one retry connect — never a loop. In time: the direct\n * probe is raced against {@link PROBE_TIMEOUT_MS}, so a port that accepts TCP\n * and never answers (\"blackhole\") costs the probe window, not a multi-minute\n * stall. A permanently dead port therefore ends in the S9 exit-4 DAEMON_DOWN\n * envelope via {@link failDaemonDown}, not a hang.\n *\n * This function cleans up after ITSELF before any throw: every client it created\n * is closed, and a daemon it started (an ensure result's `stop`) is torn down —\n * because throwing from here means `withDaemon`'s `finally` never runs, so the\n * caller's tear-down cannot be relied on for this leg.\n */\nasync function resolveDaemon(\n opts: DaemonClientOptions,\n): Promise<{ client: Client; stop: () => Promise<void> }> {\n // Lazy default: when the caller didn't inject a project (the common case from\n // command modules, which only forward --json/--verbose), resolve one here.\n // An uninitialized project (`loadProjectInfo` throws \"Run `noir init` first\")\n // is a USAGE error (exit 1), not a daemon-down (exit 4) — so route it through\n // fail() BEFORE the ensureDaemonRunning try, which maps errors to exit 4. This\n // keeps exit-1 semantics AND emits the canonical {ok:false,error} envelope on\n // stdout under --json (a plain Error would leave stdout EMPTY). It also runs\n // BEFORE any connect attempt: a project id is the daemon scope key, so there\n // is nothing to address (and no token to present) without one.\n let project: ProjectInfo;\n try {\n project = opts.project ?? loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n const idleTimeoutSec = opts.idleTimeoutSec ?? project.config.daemon.idleTimeoutSec;\n const preferred = project.config.daemon.port;\n\n // Connect-first when a stable address exists. `0` is a configured PREFERENCE\n // for an ephemeral port, i.e. explicitly NOT a stable address — it takes the\n // ensure-first path below, which still forwards the preference to the spawn.\n if (preferred !== undefined && preferred !== 0) {\n const direct = `http://127.0.0.1:${preferred}/mcp`;\n const probe = newClient();\n let probeTimer: ReturnType<typeof setTimeout> | undefined;\n try {\n // Raw connect, NOT connectClient: a failure here is the SIGNAL TO SPAWN,\n // not a command-fatal error, so it must never be mapped onto exit 4.\n //\n // BOUNDED, exactly like every other address probe in this module: a\n // configured port is a BLIND address — unlike the port\n // `ensureDaemonRunning` has just verified via `/health`, a foreign process\n // may hold it and accept TCP without ever answering. `connect()` carries\n // no deadline of its own (the only backstop would be undici's ~300s\n // headersTimeout), so the probe is raced against the same\n // {@link PROBE_TIMEOUT_MS} window the `/health` probe uses and a timeout\n // is treated as \"no daemon here\", i.e. fall through to the spawn.\n await Promise.race([\n probe.connect(httpTransport(direct, project.id)),\n new Promise<never>((_, reject) => {\n probeTimer = setTimeout(\n () => reject(new Error(`no response within ${PROBE_TIMEOUT_MS}ms`)),\n PROBE_TIMEOUT_MS,\n );\n }),\n ]);\n // A daemon was already answering at the configured address. Nothing was\n // started, so tear-down is a no-op — `stop` must stay inert (there is no\n // ensure result to defer to, and stopping a daemon we did not start would\n // kill the user's).\n return { client: probe, stop: async () => {} };\n } catch (err) {\n // Refused connection (nothing listening), no answer inside the probe\n // window (a blackhole port), a stale token from a before-restart daemon,\n // or a foreign process holding the port — each means \"no USABLE daemon at\n // this address\". Discard the probe and fall through to ensure + one retry;\n // the token is re-read at retry connect time, so a stale-token case\n // self-heals against the same (still healthy) daemon.\n if (opts.verbose) {\n process.stderr.write(\n `noir: no daemon at the configured port ${preferred} (${describeCause(err)})\\n`,\n );\n }\n await probe.close().catch(() => {\n /* the probe connection is already unusable; never mask the spawn path */\n });\n } finally {\n // Release the loser: on success the pending timer must not hold the event\n // loop open, and on failure it has already fired. (A connect that settles\n // AFTER the timeout is ignored by the settled race — and `close()` above\n // has already torn its transport down.)\n if (probeTimer !== undefined) clearTimeout(probeTimer);\n }\n }\n\n let url: string;\n let stop: () => Promise<void>;\n try {\n const ensured = await ensureDaemonRunning({\n project,\n idleTimeoutSec,\n ...(preferred !== undefined ? { port: preferred } : {}),\n });\n url = ensured.url;\n stop = ensured.stop;\n } catch (err) {\n failDaemonDown(opts, err);\n }\n\n // The retry (or the direct path when no port is configured). A failure HERE\n // is genuinely fatal — ensure already succeeded, so there is no further\n // recovery and the S9 exit-4 mapping applies (unlike the probe above).\n const client = newClient();\n try {\n await connectClient(client, url, opts, project.id);\n } catch (err) {\n // This function is about to THROW, so `withDaemon`'s finally — the one that\n // tears down a daemon we started — never runs. Tear down BOTH here, or a\n // daemon `ensureDaemonRunning` just spawned is stranded (running, with no\n // caller left to stop it) behind an exit-4 failure. Reachable on the plain\n // ensure-first path too, not just connect-first: ensure succeeds, then the\n // connect to the daemon it returned fails.\n await client.close().catch(() => {\n /* never mask the connect failure with a close failure */\n });\n await stop().catch(() => {\n /* ditto for the tear-down — the connect failure is the real error */\n });\n throw err;\n }\n return { client, stop };\n}\n\n/**\n * Build a Streamable HTTP transport for `url`, carrying the daemon's bearer\n * token when one is on disk for `scopeKey`. The daemon regenerates its token\n * on every start (spec 6.1), so the header is resolved at CONNECT time — never\n * cached across commands — and an absent token file simply means \"send no\n * header\" (a stdio-only or pre-auth daemon); the request then fails as any\n * other unauthorized request does (exit 4 with the daemon's own remediation).\n * `scopeKey` is the daemon identity: a project daemon's projectId, a workspace\n * daemon's name.\n */\nfunction httpTransport(url: string, scopeKey: string | null): StreamableHTTPClientTransport {\n const token = scopeKey === null ? null : readDaemonToken(scopeKey);\n return new StreamableHTTPClientTransport(\n new URL(url),\n // Verified present: StreamableHTTPClientTransportOptions.requestInit?: RequestInit\n token ? { requestInit: { headers: { Authorization: `Bearer ${token}` } } } : {},\n );\n}\n\n/**\n * The daemon scope key for the caller's project (its projectId), or `null` when\n * the project cannot be resolved. `null` ⇒ connect without a token header; the\n * daemon then answers 401 and the caller maps it to exit 4 exactly like any\n * other unreachable daemon (the caller has no project to scope a token to).\n */\nfunction callerScopeKey(opts: DaemonClientOptions): string | null {\n try {\n return (opts.project ?? loadProjectInfo(process.cwd())).id;\n } catch {\n return null;\n }\n}\n\n/**\n * Build an unconnected MCP client for the CLI. One client per connection: the\n * SDK's `connect` binds a transport to the client, so a client is never reused\n * across two daemons (the connect-first probe discards its own on failure).\n */\nfunction newClient(): Client {\n return new Client(\n { name: 'noir-cli', version: NOIR_VERSION },\n { versionNegotiation: { mode: 'auto' } },\n );\n}\n\n/** Connect a Client over Streamable HTTP, rethrowing the RAW transport error so\n * the caller owns the failure mapping. Used by {@link withWorkspaceDaemon},\n * which can read more out of a connect failure than \"the daemon is down\" (a\n * 401 means it is up and reachable but rejected our credential). Everything\n * else goes through {@link connectClient}, which maps onto exit 4. */\nasync function connectRaw(client: Client, url: string, scopeKey: string | null): Promise<void> {\n await client.connect(httpTransport(url, scopeKey));\n}\n\n/** Connect a Client over Streamable HTTP; map transport failure to exit 4. */\nasync function connectClient(\n client: Client,\n url: string,\n opts: DaemonClientOptions,\n scopeKey: string | null,\n): Promise<void> {\n try {\n await connectRaw(client, url, scopeKey);\n } catch (err) {\n failDaemonDown(opts, err);\n }\n}\n\n/**\n * Map a WORKSPACE connect failure onto exit 4 with an accurate message.\n *\n * A 401 is not \"the daemon is down\": the daemon answered, so it IS reachable —\n * it rejected our bearer token (a stale token file, or one we cannot read). The\n * generic {@link DAEMON_DOWN_HINT} sends the user to `noir daemon start`, which\n * fixes nothing. Name the token file (the workspace scope key is the workspace\n * NAME) and the remedy instead. Conservative by construction: only the MESSAGE\n * differs for the recognizable-401 case — the exit code and every non-401\n * failure keep the exact previous behaviour.\n */\nfunction failWorkspaceConnect(\n opts: DaemonClientOptions,\n routing: WorkspaceRouting,\n cause: unknown,\n): never {\n const text = String(cause).toLowerCase();\n if (text.includes('401') || text.includes('unauthorized')) {\n if (opts.verbose) {\n process.stderr.write(`noir: daemon transport detail: ${describeCause(cause)}\\n`);\n }\n // Names only, never a value: the token file's path, never its contents.\n fail(\n EXIT.DAEMON_DOWN,\n `workspace ${JSON.stringify(routing.name)} daemon is running but rejected this client's token (HTTP 401) — its token file is ${tokenPath(routing.name)}; make sure it is readable, or restart the workspace daemon with \\`noir daemon start --workspace ${routing.name}\\` to mint a fresh one.`,\n opts,\n );\n }\n failDaemonDown(opts, cause);\n}\n\n/**\n * Call one daemon tool and parse its text-JSON result. A tool's own\n * logical-failure envelope is returned as data; transport / parse failures\n * throw exit 4.\n */\nasync function callToolParse<T>(\n client: Client,\n opts: DaemonClientOptions,\n name: string,\n args: Record<string, unknown>,\n): Promise<T> {\n // `.catch` whose handler returns `never` (failDaemonDown always throws) keeps\n // `result` typed as the callTool payload — no evolving-any `let`, and the only\n // way past this line is a successful call.\n const result = await client\n .callTool({ name, arguments: args })\n .catch((err: unknown) => failDaemonDown(opts, err));\n // The daemon's tools always return `{content:[{type:'text', text: JSON}]}` via\n // `textResult`. Read defensively: any non-text / empty / non-JSON block means\n // the MCP surface is unusable → exit 4 (not a tool's logical error).\n const block = (result.content as readonly unknown[] | undefined)?.[0] as\n | { type?: string; text?: unknown }\n | undefined;\n if (block?.type !== 'text' || typeof block.text !== 'string') {\n failDaemonDown(opts, new Error(`tool '${name}' returned no text content`));\n }\n const text: string = block.text;\n try {\n return JSON.parse(text) as T;\n } catch (err) {\n failDaemonDown(opts, err);\n }\n}\n\n/**\n * Build a {@link DaemonToolCaller} over an already-connected {@link Client}.\n * Shared by {@link withDaemon} (active commands — failures map to exit 4) and\n * {@link withRunningDaemon} (status — connect failures degrade to null, caught\n * by its outer try). `callTool`/`listTools` still route transport / parse\n * failures through {@link failDaemonDown}; {@link withRunningDaemon} wraps the\n * connect+fn body so those become a null result instead of an exit.\n */\nfunction buildCaller(client: Client, opts: DaemonClientOptions): DaemonToolCaller {\n return {\n callTool: <U = unknown>(name: string, args: Record<string, unknown> = {}): Promise<U> =>\n callToolParse<U>(client, opts, name, args),\n listTools: async (): Promise<string[]> => {\n // `listTools()` is the standard MCP capability discovery method; the\n // daemon's McpServer answers with every registered tool name. Any\n // transport failure maps to exit 4 (DAEMON_DOWN) exactly like callTool;\n // a defensive read of `.tools` keeps a surprising payload from crashing.\n const res = await client.listTools().catch((err: unknown) => failDaemonDown(opts, err));\n const tools = (res as { tools?: unknown } | null)?.tools;\n if (!Array.isArray(tools)) return [];\n const names: string[] = [];\n for (const t of tools) {\n const name = (t as { name?: unknown } | null)?.name;\n if (typeof name === 'string') names.push(name);\n }\n return names;\n },\n };\n}\n\n/**\n * Connect to the daemon and run `fn` against a {@link DaemonToolCaller} that\n * shares one MCP connection across any number of tool calls.\n *\n * {@link resolveDaemon} owns the CONNECT-FIRST ordering (spec 7.1) and returns\n * a connection it has already established, so this body only has to run `fn`\n * and tear down. The client is closed and any daemon this call started is torn\n * down in `finally`, even when `fn` throws — so a one-shot CLI command never\n * strands a server or leaks a connection, while a reused (already-running)\n * daemon is left untouched (both `ensureDaemonRunning`'s `stop` for a reused\n * daemon and the connect-first path's `stop` are no-ops in that case).\n *\n * That `finally` is reached only once `resolveDaemon` has RETURNED. A throw from\n * `resolveDaemon` itself (exit 4) skips it entirely, so the same tear-down is\n * performed inside `resolveDaemon` before it throws — the two legs must stay in\n * step, or a spawned daemon is stranded. See the retry-failure catch there.\n *\n * `fn`'s own errors propagate untouched (the command module owns their exit\n * code); only transport / connection / tool-parse failures map to exit 4 here.\n */\nexport async function withDaemon<T>(\n opts: DaemonClientOptions,\n fn: (caller: DaemonToolCaller) => Promise<T>,\n): Promise<T> {\n const { client, stop } = await resolveDaemon(opts);\n try {\n return await fn(buildCaller(client, opts));\n } finally {\n await client.close().catch(() => {\n /* a close error must not mask the real failure / swallowed result */\n });\n await stop().catch(() => {\n /* ditto: tear-down failures never override the command's outcome */\n });\n }\n}\n\n/**\n * Connect to an ALREADY-RUNNING daemon (located via {@link probeDaemon}) and run\n * `fn` over one MCP connection. NEVER starts a daemon — returns `null` when the\n * probe finds none. This is the read-only, best-effort path `noir status` uses:\n * a down daemon is reported honestly (the caller renders `daemon:{running:false}`\n * and exits 0 — status is informational, not a write), and any connect failure\n * that crops up after a successful probe (the daemon died mid-snapshot) degrades\n * to `null` rather than escalating to exit 4 (DAEMON_DOWN). Active commands that\n * NEED a daemon use {@link withDaemon} (which auto-starts + maps failures to\n * exit 4); only `status` is probe-only.\n *\n * `fn` should wrap its own tool calls in a try/catch (status uses `tryTool`) so a\n * single missing engine folds to `null` instead of failing the snapshot.\n *\n * `probe` lets a caller that has ALREADY probed (status, to populate the daemon\n * section whether up or down) pass the result in so we don't GET /health twice.\n */\nexport async function withRunningDaemon<T>(\n opts: DaemonClientOptions,\n fn: (caller: DaemonToolCaller) => Promise<T>,\n probe?: DaemonProbe,\n): Promise<T | null> {\n const p = probe ?? (await probeDaemon(opts));\n if (!p.running) return null;\n const url = `http://127.0.0.1:${p.port}/mcp`;\n let client: Client | undefined;\n try {\n client = newClient();\n const connected: Client = client;\n // Connect directly to the probed port — NO ensureDaemonRunning. If the daemon\n // died between probe and connect (a race), this throws and we degrade to null\n // (the probe already supplied pid/uptime for the snapshot). The bearer header\n // is required here too: /health is token-free (the probe above passed without\n // one), but the /mcp snapshot connect is authenticated like every other one.\n await connected.connect(httpTransport(url, callerScopeKey(opts)));\n return await fn(buildCaller(connected, opts));\n } catch {\n // Best-effort: probe said up but connect/transport failed mid-snapshot.\n // Report the probe data + null sections instead of exit 4.\n return null;\n } finally {\n if (client) {\n await client.close().catch(() => {\n /* a close error must not mask the degraded result */\n });\n }\n }\n}\n\n/**\n * Call a single daemon MCP tool and return its parsed JSON payload. The\n * convenience most command modules use; built on {@link withDaemon} so connect\n * + close + tear-down are handled once.\n */\nexport async function callDaemonTool<T = unknown>(\n opts: DaemonClientOptions,\n name: string,\n args: Record<string, unknown> = {},\n): Promise<T> {\n return withDaemon<T>(opts, (caller) => caller.callTool<T>(name, args));\n}\n\n// ---------------------------------------------------------------------------\n// In-process read-only fallback (S9 DS-5). READS only — the single-writer\n// invariant is preserved because the store is opened READ-ONLY and every engine\n// built over it (context retriever, memory recall/sessions, workflow status) is\n// a pure reader. Writes keep the daemon-required path (exit 4 when down).\n// ---------------------------------------------------------------------------\n\n/** Engines constructed in-process by {@link withInProcessRead} (reads only). */\nexport interface InProcessEngines {\n /** Hybrid-retrieval engine over the read-only store handle. */\n context: ContextEngine;\n /** Cross-session memory engine over the same handle (recall/sessions read). */\n memory: MemoryEngine;\n /** Workflow engine over the same handle (status read). */\n workflow: WorkflowEngine;\n}\n\n/**\n * Open the project's store READ-ONLY, build the context + memory + workflow\n * engines in-process over that one handle, run `fn`, and close the store in\n * `finally`. This is the daemon-down fallback for READ commands (`context\n * search`, `memory recall`, `memory sessions`, `task status`): a read-only\n * handle preserves the daemon's single-writer discipline (the store is never\n * opened for writes here), and reads (FTS, kNN, counts, KV) keep working on it\n * exactly as they do for a degraded daemon handle.\n *\n * Construction mirrors {@link openStoreForDaemon} + the daemon's stdio/http\n * seams: the context engine resolves its embedder from the project's\n * `context:` config via {@link resolveEmbedderConfig} (a pure projection —\n * construction never touches the network or the native runtime; a `kind:'none'`\n * or unavailable embedder degrades reads to BM25-only, F8); the memory engine\n * shares the SAME embed; the workflow engine reads the store KV. All three\n * reuse the injected handle — no second connection.\n *\n * `storeDegraded: true` is threaded into both engines so their persistent\n * `degraded` flag is honest (a read-only handle) and any accidental write\n * refuses with the daemon's \"store is read-only (daemon down)\" error rather\n * than silently succeeding.\n *\n * The store is ALWAYS closed in `finally`, even when `fn` throws (a one-shot\n * CLI command never strands a handle).\n *\n * @param opts Daemon-client options. `project` resolves the store path + the\n * embedder config; when absent it is resolved from `process.cwd()`\n * (uninitialized project → throws the \"Run `noir init` first\" exit-1 hint,\n * mirroring {@link resolveDaemon}).\n */\nexport async function withInProcessRead<T>(\n opts: DaemonClientOptions,\n fn: (engines: InProcessEngines) => Promise<T>,\n): Promise<T> {\n let project: ProjectInfo;\n try {\n project = opts.project ?? loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n // Route a store-open failure through fail() so --json emits {ok:false,error}\n // (a raw throw would leave stdout empty).\n let store: Store;\n try {\n store = await openStore({ projectId: project.id, root: project.root, readonly: true });\n } catch (err) {\n fail(\n EXIT.ERROR,\n `could not open the store read-only: ${err instanceof Error ? err.message : String(err)}`,\n opts,\n );\n }\n try {\n // READ-ONLY handle: `storeDegraded` is threaded so status/degraded flags are\n // honest and any accidental write refuses cleanly (single writer preserved).\n const embedderCfg = resolveEmbedderConfig(project.config.context);\n const embed = createEmbedFn(embedderCfg).embed;\n const context = new ContextEngine({\n store,\n root: project.root,\n projectId: project.id,\n embedderCfg,\n storeDegraded: true,\n });\n const memory = createMemoryEngine({\n store,\n root: project.root,\n projectId: project.id,\n embed,\n storeDegraded: true,\n });\n const workflow = new WorkflowEngine(\n store,\n project.root,\n project.id,\n resolveGateConfig(project.config),\n );\n return await fn({ context, memory, workflow });\n } finally {\n await store.close().catch(() => {\n /* a close error must not mask the command's outcome */\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Workspace-aware routing (ADR-0009). In a repo that joined a workspace (a\n// `.noir/workspace.json` marker), `memory *` commands route to the WORKSPACE\n// daemon (`?p=<projectId>`), not the per-project daemon. The read fallback opens\n// the workspace store read-only (single-writer preserved). Context/workflow/task\n// commands keep the project daemon (spec §6).\n// ---------------------------------------------------------------------------\n\n/** A repo's workspace membership (name + this repo's project id), or null. */\nexport interface WorkspaceRouting {\n name: string;\n projectId: string;\n}\n\nexport function workspaceRoutingTarget(root: string): WorkspaceRouting | null {\n const name = readWorkspaceMarker(root);\n if (name === null) return null;\n try {\n return { name, projectId: loadProjectInfo(root).id };\n } catch {\n return null;\n }\n}\n\n/** Read-only probe of a workspace daemon (never starts one). */\nexport async function probeWorkspaceDaemon(routing: WorkspaceRouting): Promise<DaemonProbe> {\n const rec = readWorkspaceDaemonRecord(routing.name);\n if (rec === null || !pidAlive(rec.pid)) return { running: false };\n try {\n const res = await fetch(`http://127.0.0.1:${rec.port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n if (!res.ok) return { running: false };\n const body = (await res.json().catch(() => null)) as {\n ok?: boolean;\n pid?: number;\n workspace?: string;\n } | null;\n if (body?.ok !== true || body.workspace !== routing.name || body.pid !== rec.pid) {\n return { running: false };\n }\n return { running: true, pid: rec.pid, port: rec.port };\n } catch {\n return { running: false };\n }\n}\n\n/** Connect to the workspace daemon (`?p=<projectId>`) and run `fn` (active path). */\nexport async function withWorkspaceDaemon<T>(\n opts: DaemonClientOptions,\n routing: WorkspaceRouting,\n fn: (caller: DaemonToolCaller) => Promise<T>,\n): Promise<T> {\n // Spec §11: a write/read against a workspace whose daemon is DOWN must fail\n // with clear guidance, NOT silently auto-start a fresh daemon (which would\n // look identical to \"up\" but surprise the user). Probe-only — no ensure.\n const probe = await probeWorkspaceDaemon(routing);\n if (!probe.running || probe.port === undefined) {\n fail(\n EXIT.DAEMON_DOWN,\n `workspace ${JSON.stringify(routing.name)} daemon is not running — start it with \\`noir daemon start --workspace ${routing.name}\\` (or run \\`noir workspace status\\`).`,\n opts,\n );\n }\n const url = `http://127.0.0.1:${probe.port}/mcp?p=${routing.projectId}`;\n let client: Client | undefined;\n try {\n client = newClient();\n const connected: Client = client;\n // The workspace daemon's token scope key is the workspace NAME — its own\n // secret, resolved from disk at connect time like every other token. In\n // particular the CALLER's project token is never handed to a workspace\n // server: the two scope keys are different identities, so a repo can only\n // ever present the credential of the daemon it is actually addressing.\n // `connectRaw` (not `connectClient`) so a 401 rejection reaches\n // `failWorkspaceConnect` with its cause intact, instead of being flattened\n // into the generic \"daemon not reachable\" hint.\n await connectRaw(connected, url, routing.name).catch((err: unknown) =>\n failWorkspaceConnect(opts, routing, err),\n );\n return await fn(buildCaller(connected, opts));\n } finally {\n if (client) {\n await client.close().catch(() => {\n /* a close error must not mask the real failure */\n });\n }\n }\n}\n\n/** Open the workspace store read-only and run `fn` over a memory engine (read fallback). */\nexport async function withWorkspaceMemoryRead<T>(\n opts: DaemonClientOptions,\n routing: WorkspaceRouting,\n project: ProjectInfo,\n fn: (memory: MemoryEngine) => Promise<T>,\n): Promise<T> {\n let store: Store;\n try {\n store = await openStore({\n projectId: `ws-${routing.name}`,\n root: project.root,\n dbPath: workspaceStoreDbPath(routing.name),\n readonly: true,\n });\n } catch (err) {\n // Reached only when the workspace daemon is already down (this is its read\n // fallback). The dominant cause is a store that was never created, so give\n // the same actionable guidance + DAEMON_DOWN exit code the active path uses\n // rather than a bare store-open error — the caller's next step is the same\n // either way: start the daemon.\n fail(\n EXIT.DAEMON_DOWN,\n `workspace ${JSON.stringify(routing.name)} store is unavailable (${err instanceof Error ? err.message : String(err)}) — start its daemon with \\`noir daemon start --workspace ${routing.name}\\` (or run \\`noir workspace status\\`).`,\n opts,\n );\n }\n const embedderCfg = resolveEmbedderConfig(project.config.context);\n const embed = createEmbedFn(embedderCfg).embed;\n const memory = createMemoryEngine({\n store,\n root: project.root,\n projectId: `ws-${routing.name}`,\n embed,\n storeDegraded: true,\n softForget: true,\n });\n try {\n return await fn(memory);\n } finally {\n await store.close().catch(() => {\n /* a close error must not mask the command's outcome */\n });\n }\n}\n","// Single owner of the Noir CLI's semantic palette + status badges.\n//\n// Built on `picocolors` (the SAME lib the rest of the CLI already uses), so the\n// decoration strips consistently under NO_COLOR / non-TTY. cli-table3 styles\n// via a SECOND lib (@colors/colors) whose NO_COLOR/TTY semantics differ — that\n// split is what painted every table header red while the body stripped. Routing\n// ALL color through this module (the `c` object + `badge()`) closes that leak:\n// there is exactly one color authority, and it honors NO_COLOR / CLICOLOR_FORCE\n// / TTY uniformly.\n//\n// Accessibility:\n// - `badge()` ALWAYS returns SYMBOL + TEXT LABEL (e.g. `⚠ warn`), so NO_COLOR\n// and colorblind users get identical information. Color is decorative.\n// - `NOIR_ACCESSIBLE` disables the banner gradient (→ solid accent) and is the\n// documented opt-in for users who want the most legible form; badges already\n// carry symbol+text unconditionally, so the flag is a no-op for them.\n\nimport pc from 'picocolors';\n\n// A private always-on colorizer. The PUBLIC `c` object gates every call through\n// `useColor()`, so `useColor()` is the single live authority over whether ANY\n// ANSI leaves this module. (The default `picocolors` export captures its\n// `isColorSupported` snapshot once at module load and does NOT honor\n// CLICOLOR_FORCE; gating a forced instance here fixes both.)\nconst FORCE = pc.createColors(true);\n\n/** Badge state → semantic color role. */\nexport type BadgeState = 'ok' | 'warn' | 'error' | 'info';\n\n/**\n * The semantic palette. Each method returns its input unchanged when color is\n * off (NO_COLOR / non-TTY / not forced), so callers can wrap freely without\n * guarding. Compose with `c.bold(c.info(s))` etc.\n */\nexport const c = {\n /** Green — success / healthy. */\n ok: (s: string): string => (useColor() ? FORCE.green(s) : s),\n /** Yellow — degraded but usable. */\n warn: (s: string): string => (useColor() ? FORCE.yellow(s) : s),\n /** Red — reserved strictly for ERROR. Never a table header. */\n error: (s: string): string => (useColor() ? FORCE.red(s) : s),\n /** Cyan — informational / brand-neutral accent for labels. */\n info: (s: string): string => (useColor() ? FORCE.cyan(s) : s),\n /** Blue — the Midnight Cobalt brand role (banner / accent). */\n accent: (s: string): string => (useColor() ? FORCE.blue(s) : s),\n /** Dim — secondary text, table borders when color is on. */\n dim: (s: string): string => (useColor() ? FORCE.dim(s) : s),\n /** Bold — emphasis (table headers compose this with `info`). */\n bold: (s: string): string => (useColor() ? FORCE.bold(s) : s),\n /** Reverse video (SGR 7) — inverted background for the active list row. */\n inverse: (s: string): string => (useColor() ? FORCE.inverse(s) : s),\n};\n\n// @clack-style status symbols (the vocabulary Noir inherits via @clack/prompts\n// + ora). Paired 1:1 with a state name so a screen reader / pipe / colorblind\n// user reads the same fact a sighted interactive user sees.\nconst BADGE_SYMBOL: Readonly<Record<BadgeState, string>> = {\n ok: '✓',\n warn: '⚠',\n error: '✗',\n info: 'ℹ',\n};\n\n/**\n * Render a status badge: always `SYMBOL LABEL`, colored by `state` when color\n * is on. The TEXT LABEL is unconditional — this is the accessibility invariant.\n * `label` defaults to the state name, so `badge('ok')` reads `✓ ok`.\n *\n * @example badge('ok') // → green \"✓ ok\"\n * @example badge('warn','degraded') // → yellow \"⚠ degraded\"\n * @example badge('error','down') // → red \"✗ down\"\n */\nexport function badge(state: BadgeState, label: string = state): string {\n const text = `${BADGE_SYMBOL[state]} ${label}`;\n switch (state) {\n case 'ok':\n return c.ok(text);\n case 'warn':\n return c.warn(text);\n case 'error':\n return c.error(text);\n case 'info':\n return c.info(text);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Environment gates. Evaluated LIVE (not captured at module load) so tests that\n// flip NO_COLOR / CLICOLOR_FORCE between cases see the right answer, and so a\n// user exporting NO_COLOR mid-session is honored on the next render.\n// ---------------------------------------------------------------------------\n\nfunction envFlagSet(name: string): boolean {\n // NO_COLOR spec: present AND non-empty disables color (any value).\n const v = process.env[name];\n return v !== undefined && v !== '';\n}\n\n/**\n * True when running under a CI runner (GitHub Actions / GitLab / CircleCI /\n * Drone set `CI=true` at the conventional value). Treats the explicit opt-outs\n * `0` / `false` as \"not CI\" so a user can force-disable the guard. Owned here\n * (the color authority) and re-exported so output.ts doesn't duplicate it.\n */\nexport function isCiEnv(): boolean {\n const v = process.env.CI;\n if (v === undefined || v === '') return false;\n return v !== '0' && v !== 'false';\n}\n\n/**\n * The single authority on whether decoration should emit ANSI. Honors:\n * - `NO_COLOR` → always off (spec: present + non-empty).\n * - `CLICOLOR_FORCE=1` → always on (forces color on a redirected stream).\n * - `CI=true` (alone) → always OFF. picocolors' `isColorSupported`\n * snapshot turns color ON under `!!env.CI` (it\n * assumes a CI viewer that renders ANSI). But ANSI\n * escapes inflate the raw byte length of rendered\n * lines, which breaks the responsive-width\n * guarantee `max(line.length) <= terminalWidth()`\n * that `table()` exists to uphold — and the\n * regression test that locks it (fits 60/80/120 in\n * theme.test.ts). The CLI's own top-of-file\n * contract states decoration auto-disables under\n * CI / non-TTY; this honors that. A CI viewer that\n * wants color sets `CLICOLOR_FORCE=1` (above).\n * - otherwise → picocolors' detection (TTY / FORCE_COLOR).\n */\nexport function useColor(): boolean {\n if (envFlagSet('NO_COLOR')) return false;\n if (process.env.CLICOLOR_FORCE === '1') return true;\n if (isCiEnv()) return false;\n return pc.isColorSupported;\n}\n\n/**\n * `NOIR_ACCESSIBLE`: opt-in maximum legibility. Badges already carry symbol+\n * text unconditionally (so the flag is a no-op for them); the material effect\n * is disabling the banner gradient (→ solid accent) and is reserved here for\n * future accessible-display tweaks.\n */\nexport function accessibleMode(): boolean {\n return envFlagSet('NOIR_ACCESSIBLE');\n}\n\n/**\n * Terminal width in columns. Honors `COLUMNS` (the conventional override) then\n * `process.stdout.columns`, defaulting to 80 when neither is set (the same\n * default `picocolors` / `cli-table3` assume). Floored at a small minimum so a\n * misconfigured shell never produces a degenerate zero/negative-width table.\n */\nexport function terminalWidth(): number {\n const fromEnv = process.env.COLUMNS;\n if (fromEnv !== undefined && fromEnv !== '') {\n const n = Number(fromEnv);\n if (Number.isInteger(n) && n > 0) return Math.max(n, 20);\n }\n const cols = process.stdout.columns;\n if (typeof cols === 'number' && cols > 0) return Math.max(cols, 20);\n return 80;\n}\n\n/**\n * Usable content width inside a bordered TUI panel. Ink's `borderStyle` adds a\n * 1-char border on each side, and `paddingX={1}` adds 1 char inside that — so a\n * full-width rounded panel has `terminalWidth - 4` columns of usable text.\n * Components that truncate long lines (OutputPane) MUST use this, not\n * {@link terminalWidth}, or content overflows the border.\n *\n * @param border chars consumed by the border (default 2 = left + right).\n * @param padding chars consumed by inner padding (default 2 = left + right).\n */\nexport function contentWidth(border = 2, padding = 2): number {\n return Math.max(20, terminalWidth() - border - padding);\n}\n\n/**\n * A horizontal divider line of `─` sized for the current content width. Used\n * inside bordered panels to separate regions (e.g. status bar ↔ output ↔ input)\n * without nesting Ink borders. Always dim so it reads as structure, not content.\n */\nexport function divider(): string {\n return c.dim('─'.repeat(Math.max(1, contentWidth())));\n}\n","// S9 t2 — centralized output + exit-code infrastructure for `@noir-ai/cli`.\n//\n// Stream discipline (S9): machine-readable data → stdout; every other\n// diagnostic (progress, warnings, errors, tables, help) → stderr. Under `--json`\n// the decorated stderr helpers are silenced so stdout stays pristine JSON, and\n// `fail()` emits a structured `{ok:false,error}` envelope to stdout instead.\n//\n// Decoration (picocolors / cli-table3 / ora) auto-disables under `--json`,\n// `--quiet`, `CI`, `NO_COLOR`, or a non-TTY, so the same code path is safe in an\n// interactive shell, a pipe, and CI.\n//\n// Exit codes (S9): 0 ok · 1 error · 2 usage · 3 not-found · 4 daemon-down ·\n// 5 cancelled. These constants live HERE (the single source of truth); bin.ts\n// re-exports them so existing imports from `./bin.js` keep working.\n\nimport Table from 'cli-table3';\nimport { CommanderError } from 'commander';\nimport ora, { type Ora } from 'ora';\nimport { c, isCiEnv, terminalWidth } from './theme.js';\n\n// ---------------------------------------------------------------------------\n// Exit-code contract + error types\n// ---------------------------------------------------------------------------\nexport const EXIT = {\n OK: 0,\n ERROR: 1,\n USAGE: 2,\n NOT_FOUND: 3,\n DAEMON_DOWN: 4,\n CANCELLED: 5,\n} as const;\n\nexport type ExitCode = (typeof EXIT)[keyof typeof EXIT];\n\n/**\n * Application-level CLI error carrying an S9 exit code. Thrown directly by\n * bin.ts for legacy usage paths; `fail()` is the ergonomic throw helper.\n * `inferExitCode` / `handleError` map it onto `process.exitCode`.\n */\nexport class NoirCliError extends Error {\n readonly exitCode: number;\n constructor(exitCode: number, message: string) {\n super(message);\n this.name = 'NoirCliError';\n this.exitCode = exitCode;\n }\n}\n\n/**\n * The `code` stamped onto every CommanderError our own `fail()` throws, so\n * `inferExitCode` can tell \"we threw this on purpose with an authoritative\n * exitCode\" apart from \"commander threw this for help/usage/unknown-command\"\n * (which must be remapped onto the S9 contract by `commanderExitCode`).\n */\nconst NOIR_ERROR_CODE = 'noir.error';\n\n// ---------------------------------------------------------------------------\n// Global-option shape. Commander stores `--no-input` under the attribute\n// `input` (default `true`; the flag sets it `false`) — see commander's\n// `Option.attributeName()` which strips the leading `no-`. Both `input`\n// (commander's real key) and `noInput` (the spec's intent) are accepted so\n// callers may pass either commander's raw globals or a hand-built object.\n// ---------------------------------------------------------------------------\nexport interface CliOptions {\n readonly json?: boolean;\n readonly quiet?: boolean;\n readonly verbose?: boolean;\n /** Commander's storage for `--no-input` (`false` ⇒ no input). */\n readonly input?: boolean;\n /** Convenience alias matching the `--no-input` intent (`true` ⇒ no input). */\n readonly noInput?: boolean;\n /** TUI-policy flag. `false` (`--no-tui`) forces bare `noir` onto the\n * non-interactive `status` path even in a TTY; `true` (`--tui`) is an\n * advisory hint that still requires a TTY; absent (auto) defers to\n * {@link isInteractive}. Advisory only — never hard-gates a subcommand. */\n readonly tui?: boolean;\n /** Hint-suppression flag (`--no-tips`). When `true`, redirect /\n * deprecation hints (and any other {@link tip} output) are silenced for\n * CI / log-friendly runs. */\n readonly noTips?: boolean;\n}\n\nfunction isJsonMode(opts: CliOptions): boolean {\n return opts.json === true;\n}\n\nfunction isQuietMode(opts: CliOptions): boolean {\n return opts.quiet === true;\n}\n\n/** True when the user has asked for no interactive input (either spelling). */\nexport function isNoInput(opts: CliOptions = {}): boolean {\n return opts.noInput === true || opts.input === false;\n}\n\nfunction envFlagSet(name: string): boolean {\n // NO_COLOR spec: present AND non-empty (regardless of value) disables color.\n const v = process.env[name];\n return v !== undefined && v !== '';\n}\n\n// `isCiEnv` is imported from ./theme.js (the color authority) — see note there.\n\n// ---------------------------------------------------------------------------\n// Interactivity gate. Drives both the @clack home menu and the\n// decoration of every helper below. Requires BOTH stdin and stdout to be TTYs:\n// @clack reads keypresses from stdin while ora/picocolors render to stdout.\n// ---------------------------------------------------------------------------\nexport function isInteractive(opts: CliOptions = {}): boolean {\n if (isJsonMode(opts) || isNoInput(opts)) return false;\n if (isCiEnv() || envFlagSet('NO_COLOR')) return false;\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n}\n\n/**\n * Derive the explicit `interactive` flag the scaffold engine reads (via\n * {@link ScaffoldOptions.interactive}) from the bridge bin.ts owns. The bin's\n * `preAction` sets `NOIR_NON_INTERACTIVE=1` under `--json`/`--no-input`; this\n * helper folds that with the {@link isInteractive} TTY/CI/NO_COLOR gate so a\n * single boolean flows into the engine + `buildConflictOpts`. The ENGINE itself\n * never reads `process.env` for interactivity — only this CLI helper does.\n */\nexport function resolveInteractive(): boolean {\n if (flaggedNonInteractiveCli()) return false;\n return isInteractive();\n}\n\n/** True when the bin's `preAction` flagged this invocation non-interactive\n * (`--json` / `--no-input` → `NOIR_NON_INTERACTIVE`). CLI-internal; the engine\n * reads {@link ScaffoldOptions.interactive} instead. */\nfunction flaggedNonInteractiveCli(): boolean {\n const v = process.env.NOIR_NON_INTERACTIVE;\n return v !== undefined && v !== '';\n}\n\n// ---------------------------------------------------------------------------\n// Data output (stdout). The ONLY helper here that writes to stdout; under\n// `--json` a command calls this exactly once with its full payload and never\n// mixes other stdout writes, keeping machine output pristine.\n// ---------------------------------------------------------------------------\nexport function json(obj: unknown): void {\n process.stdout.write(`${JSON.stringify(obj)}\\n`);\n}\n\n// ---------------------------------------------------------------------------\n// Human diagnostics (stderr). picocolors auto-strips ANSI under NO_COLOR /\n// non-TTY, and each helper additionally short-circuits under `--json`\n// (decoration off, payload is the output) — and `info`/`log`/`success` also\n// under `--quiet`. `warn`/`error` survive `--quiet` (they carry signal).\n// ---------------------------------------------------------------------------\nexport function log(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts) || isQuietMode(opts)) return;\n process.stderr.write(`${msg}\\n`);\n}\n\nexport function info(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts) || isQuietMode(opts)) return;\n process.stderr.write(`${c.info(msg)}\\n`);\n}\n\nexport function success(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts) || isQuietMode(opts)) return;\n process.stderr.write(`${c.ok(msg)}\\n`);\n}\n\nexport function warn(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts)) return;\n process.stderr.write(`${c.warn(msg)}\\n`);\n}\n\nexport function error(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts)) return;\n process.stderr.write(`${c.error(msg)}\\n`);\n}\n\n/**\n * Redirect / deprecation hint (stderr). Silenced under `--json` (a CI\n * consumer's stdout envelope must stay pristine) AND under `--no-tips`\n * ({@link CliOptions.noTips}). This is the ONLY helper deprecation / redirect\n * notices should use, so a single `--no-tips` flag quiets them all in CI / logs.\n * Style matches {@link warn} (these are advisory, not errors).\n */\nexport function tip(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts) || opts.noTips === true) return;\n process.stderr.write(`${c.warn(msg)}\\n`);\n}\n\n// ---------------------------------------------------------------------------\n// Tables (stderr). Suppressed entirely under `--json` — the command has\n// already emitted the rows as a JSON array via `json()`.\n//\n// Design: cli-table3 styles headers/borders via `@colors/colors`\n// (whose NO_COLOR/TTY semantics differ from picocolors), which painted EVERY\n// header red while the body stripped — RED is now reserved strictly for ERROR.\n// We bypass `@colors/colors` entirely by passing EMPTY `style.head`/`style.border`\n// arrays (cli-table3's `wrapWithStyleColors` early-returns on length 0) and\n// PRE-COLOR the header strings ourselves via `theme.c` (picocolors). Result:\n// the header, border, and body all strip consistently under NO_COLOR / non-TTY,\n// because picocolors is the SOLE color authority. Responsive `colWidths` are\n// computed from `terminalWidth()` so tables never overflow; `wordWrap` keeps\n// free-text cells readable, and `truncate: '…'` caps any single over-long token.\n// ---------------------------------------------------------------------------\nexport function table(\n rows: readonly Record<string, unknown>[],\n cols: readonly string[],\n opts: CliOptions = {},\n): void {\n if (isJsonMode(opts)) return;\n if (rows.length === 0) {\n info('(no rows)', opts);\n return;\n }\n const t = new Table({\n head: cols.map((col) => c.bold(c.info(col))),\n // Empty arrays ⇒ cli-table3 applies NO @colors/colors wrap (neither the\n // default red header nor a colored border). All color is ours, via theme.\n style: { head: [], border: [] },\n colWidths: computeColWidths(rows, cols),\n wordWrap: true,\n truncate: '…',\n });\n for (const row of rows) {\n t.push(cols.map((col) => formatCell(row[col])));\n }\n process.stderr.write(`${t.toString()}\\n`);\n}\n\n/**\n * Compute per-column widths (cli-table3 `colWidths`, which INCLUDE each column's\n * 2 padding chars) that fit `terminalWidth()`. Strategy:\n *\n * 1. Measure each column's NATURAL content width (longest cell, at least the\n * header label so a header never wraps/truncates).\n * 2. If the natural total fits, use it as-is — every cell renders whole.\n * 3. Otherwise GREEDILY TRIM ONLY THE WIDEST column (down to its header width)\n * until the row fits. Narrow columns (paths, ids, statuses) keep their full\n * width — they're single tokens that can't word-wrap, so truncating them\n * loses information. The widest column is the free-text one (descriptions,\n * details, snippets); it has spaces and absorbs the shrink via `wordWrap`.\n *\n * cli-table3 accepts only positive integers (negatives collapse). Each returned\n * width is `content + 2` to reserve the column's padding, so the content area\n * equals the measured width and the math against `terminalWidth()` is exact.\n */\nfunction computeColWidths(\n rows: readonly Record<string, unknown>[],\n cols: readonly string[],\n): number[] {\n const n = cols.length;\n if (n === 0) return [];\n const headerLen = cols.map((col) => col.length);\n const natural = cols.map((col, i) => {\n let max = Math.max(headerLen[i] ?? col.length, 3);\n for (const row of rows) {\n const len = formatCell(row[col]).length;\n if (len > max) max = len;\n }\n return max;\n });\n // cli-table3 row overhead: per column 1 left border + 2 padding, plus a final\n // right border ⇒ 3n + 1 non-content chars. `contentBudget` is what remains for\n // the sum of column CONTENT areas.\n const contentBudget = Math.max(n * 4, terminalWidth() - 3 * n - 1);\n const naturalSum = natural.reduce((a, b) => a + b, 0);\n if (naturalSum <= contentBudget) {\n return natural.map((w) => w + 2);\n }\n // Overflow: greedily cut the widest reducible column (≥ its header width).\n const content = natural.slice();\n let sum = naturalSum;\n const floor = headerLen.map((h) => Math.max(h, 3));\n while (sum > contentBudget) {\n let idx = -1;\n let widest = -1;\n for (let i = 0; i < n; i++) {\n const c = content[i] ?? 0;\n const f = floor[i] ?? 3;\n if (c > f && c > widest) {\n widest = c;\n idx = i;\n }\n }\n if (idx === -1) break; // every column is at its header floor — stop\n const overshoot = sum - contentBudget;\n const reducible = (content[idx] ?? 0) - (floor[idx] ?? 3);\n const cut = Math.min(overshoot, reducible);\n content[idx] = (content[idx] ?? 0) - cut;\n sum -= cut;\n }\n return content.map((w) => w + 2);\n}\n\nfunction formatCell(value: unknown): string {\n if (value === undefined || value === null) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n return JSON.stringify(value);\n}\n\n// ---------------------------------------------------------------------------\n// Definition list (stderr). A two-column Field/Value rendering for snapshot-\n// shaped output (status / task / context-status). `status.ts` hand-rolled this\n// exact shape; this generalizes it. Rendered via the same responsive `table()`,\n// so it inherits the clean (non-red) header + NO_COLOR behavior automatically.\n// ---------------------------------------------------------------------------\nexport interface DefinitionRow {\n /** Field label (left column). */\n label: string;\n /** Field value; coerced to a string the same way table cells are. */\n value: unknown;\n}\n\nexport function definitionList(rows: readonly DefinitionRow[], opts: CliOptions = {}): void {\n table(\n rows.map((r) => ({ Field: r.label, Value: formatCell(r.value) })),\n ['Field', 'Value'],\n opts,\n );\n}\n\n/** Render a single `label: value` line to stderr (human only). */\nexport function kv(label: string, value: unknown, opts: CliOptions = {}): void {\n log(`${c.bold(label)}: ${formatCell(value)}`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// Spinner (stderr). ora when interactive; a no-op otherwise so scriptable /\n// CI / `--json` / `--quiet` runs pay nothing and never animate a pipe.\n// ---------------------------------------------------------------------------\nexport interface Spinner {\n start(text?: string): Spinner;\n stop(): Spinner;\n clear(): Spinner;\n succeed(text?: string): void;\n fail(text?: string): void;\n warn(text?: string): void;\n info(text?: string): void;\n text: string;\n}\n\nclass NoopSpinner implements Spinner {\n text = '';\n start(_text?: string): Spinner {\n return this;\n }\n stop(): Spinner {\n return this;\n }\n clear(): Spinner {\n return this;\n }\n succeed(_text?: string): void {}\n fail(_text?: string): void {}\n warn(_text?: string): void {}\n info(_text?: string): void {}\n}\n\n/** Wraps an `ora` instance so the `Spinner` interface stays decoupled from ora's exact type. */\nclass OraSpinner implements Spinner {\n private readonly handle: Ora;\n\n constructor(initialText: string) {\n this.handle = ora(initialText);\n }\n\n get text(): string {\n return this.handle.text;\n }\n set text(value: string) {\n this.handle.text = value;\n }\n\n start(text?: string): Spinner {\n if (text !== undefined) this.handle.text = text;\n this.handle.start();\n return this;\n }\n\n stop(): Spinner {\n this.handle.stop();\n return this;\n }\n\n clear(): Spinner {\n this.handle.clear();\n return this;\n }\n\n succeed(text?: string): void {\n this.handle.succeed(text);\n }\n\n fail(text?: string): void {\n this.handle.fail(text);\n }\n\n warn(text?: string): void {\n this.handle.warn(text);\n }\n\n info(text?: string): void {\n this.handle.info(text);\n }\n}\n\nexport function spinner(text = '', opts: CliOptions = {}): Spinner {\n if (isJsonMode(opts) || isQuietMode(opts) || !isInteractive(opts)) {\n return new NoopSpinner();\n }\n return new OraSpinner(text);\n}\n\n// ---------------------------------------------------------------------------\n// Failure + exit-code mapping\n// ---------------------------------------------------------------------------\n\n/**\n * Write a diagnostic and throw a `CommanderError` carrying the S9 exit code,\n * so bin.ts's `exitOverride` + `handleError` surface it as `process.exitCode`\n * without any mid-action `process.exit`. Under `--json` the message becomes a\n * structured `{ok:false,error}` envelope on STDOUT; otherwise plain text on\n * STDERR. Always throws (`never`).\n */\nexport function fail(exitCode: number, message: string, opts: CliOptions = {}): never {\n if (isJsonMode(opts)) {\n process.stdout.write(`${JSON.stringify({ ok: false, error: { code: exitCode, message } })}\\n`);\n } else if (message.length > 0) {\n process.stderr.write(`${message}\\n`);\n }\n throw new CommanderError(exitCode, NOIR_ERROR_CODE, message);\n}\n\n/**\n * Guard for commands that would otherwise block on a prompt. Honors the S9\n * hard rule: \"no blocking prompts when !isTTY or --no-input — error exit 2\n * naming the missing flag instead\". `hint` describes the operation so the\n * message points the user at the flag-based alternative.\n */\nexport function requireInteractive(opts: CliOptions, hint: string): void {\n if (!isInteractive(opts)) {\n fail(\n EXIT.USAGE,\n `${hint} needs an interactive terminal, but input is disabled (non-TTY, --no-input, --json, CI, or NO_COLOR). Re-run in a TTY or supply the value as a flag.`,\n opts,\n );\n }\n}\n\n// Remap commander's own exit codes onto the S9 contract:\n// help/version → 0 · unknown command → 3 · every other usage error → 2\nfunction commanderExitCode(err: CommanderError): number {\n switch (err.code) {\n case 'commander.helpDisplayed':\n case 'commander.versionDisplayed':\n case 'commander.version': // commander v12 uses this code for --version (exit 0)\n return EXIT.OK;\n case 'commander.unknownCommand':\n return EXIT.NOT_FOUND;\n default:\n return EXIT.USAGE;\n }\n}\n\n/** Map any thrown value onto the S9 exit-code contract. */\nexport function inferExitCode(err: unknown): number {\n if (err instanceof NoirCliError) return err.exitCode;\n if (err instanceof CommanderError) {\n // Errors our own `fail()` threw carry a `noir.*` code and an authoritative\n // exitCode — respect it. Commander's own errors are remapped by `code`.\n if (err.code.startsWith('noir.')) return err.exitCode;\n return commanderExitCode(err);\n }\n return EXIT.ERROR;\n}\n\n/**\n * Map a thrown error onto stderr + `process.exitCode`. Never throws, never\n * calls `process.exit` (commander's `exitOverride` already prevented that for\n * commander's own errors). Used by the bin entry's `run()`.\n */\nexport function handleError(err: unknown): void {\n if (err instanceof NoirCliError) {\n if (err.message.length > 0) process.stderr.write(`${err.message}\\n`);\n } else if (err instanceof CommanderError) {\n // If `fail()` threw it, the message is already written (stderr or the JSON\n // stdout envelope); if commander threw it, `configureOutput.writeErr`\n // already wrote it during parse. Don't double-write either way.\n } else {\n process.stderr.write(`noir: ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n process.exitCode = inferExitCode(err);\n}\n","// S9 — `noir context {search,index,status}`.\n//\n// Thin MCP-client commands over the running daemon's S6 context engine. Every\n// read/write here is a single `callDaemonTool` round-trip; the daemon is the\n// sole writer (blueprint §2), so the CLI never opens the store in-process. If\n// the daemon can't be reached, `callDaemonTool` maps the failure onto exit 4\n// (DAEMON_DOWN) with the stable remediation hint — see daemon-client.ts.\n//\n// A daemon tool's OWN logical-failure envelope (`{ok:false, degraded:true,\n// error}` — e.g. a read-only store refusing `context_index`, or the embedder\n// throwing inside `context_search`) is DATA, not a transport failure: it parses\n// cleanly, so this module surfaces it honestly as exit 1 (ERROR) with the\n// daemon's message, rather than re-packaging it as daemon-down.\n//\n// Stream discipline (S9): `--json` emits the versioned `{ok:true,data}`\n// envelope to STDOUT (the only stdout write); human tables / snippets go to\n// STDERR via the centralized `table()` / `log()` helpers (auto-stripped under\n// NO_COLOR / non-TTY / --json). `--limit` is coerced here; an invalid value is a\n// USAGE error (exit 2) before we touch the daemon.\n\nimport {\n callDaemonTool,\n type DaemonClientOptions,\n type DaemonProbe,\n probeDaemon,\n withInProcessRead,\n} from '../daemon-client.js';\nimport { type CliOptions, definitionList, EXIT, fail, info, log, table } from '../output.js';\nimport { badge } from '../theme.js';\n\n/** Options accepted by every `context` sub-command (globals + daemon knobs). */\nexport interface ContextOptions extends CliOptions, DaemonClientOptions {}\n\n// ---------------------------------------------------------------------------\n// Tool result shapes (the relevant slices of the daemon's wire payloads). The\n// daemon returns these as JSON text; daemon-client parses to `unknown`, so each\n// reader treats a foreign field as undefined and the command degrades to a clear\n// error rather than crashing. Local types = the CLI depends only on the MCP\n// wire contract (mirrors status.ts).\n// ---------------------------------------------------------------------------\n\n/** Normalized search hit rendered to humans + emitted in the JSON payload. */\nexport interface ContextHit {\n path: string;\n score: number;\n snippet: string;\n source: string;\n}\n\n/** `context_search` success payload (SearchResult + the echoed query). */\nexport interface ContextSearchData {\n query: string;\n hits: ContextHit[];\n consumedTokens: number;\n truncated: boolean;\n degraded: boolean;\n mode: string;\n}\n\n/** `context_index` success payload (IndexResult). */\nexport interface ContextIndexData {\n indexed: number;\n skipped: number;\n deleted: number;\n failed: number;\n totalChunks: number;\n degraded: boolean;\n}\n\n/** `context_status` payload (ContextStatus). `ok` is a literal so the success /\n * failure union narrows cleanly against {@link ToolFailure} (ok:false). */\nexport interface ContextStatusData {\n ok: true;\n projectId: string;\n docCount: number;\n vecCount: number;\n indexedFiles: number;\n embedder: { kind: string; model?: string; dim: number };\n degraded: boolean;\n}\n\n/** A daemon logical-failure envelope (`{ok:false,…}`) — read generically. */\ninterface ToolFailure {\n ok: false;\n degraded?: boolean;\n error?: string;\n reason?: string;\n}\n\n/** The raw `context_search` wire payload (success branch). */\ninterface ContextSearchResult {\n ok: true;\n results?: unknown;\n consumedTokens?: number;\n truncated?: boolean;\n degraded?: boolean;\n mode?: string;\n}\n\n/** The raw `context_index` wire payload (success branch). */\ninterface ContextIndexResult {\n ok: true;\n indexed?: number;\n skipped?: number;\n deleted?: number;\n failed?: number;\n totalChunks?: number;\n degraded?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Small shared helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Coerce a commander `--limit <n>` string into a positive int, or fail with\n * exit 2 (USAGE) naming the flag — mirrors the daemon's zod\n * `number().int().positive()` but at the CLI edge so a bad value never starts a\n * daemon round-trip.\n */\nfunction parseLimit(raw: string | undefined, label: string, opts: CliOptions): number | undefined {\n if (raw === undefined) return undefined;\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n fail(EXIT.USAGE, `${label}: --limit must be a positive integer (got '${raw}')`, opts);\n }\n return n;\n}\n\n/**\n * Normalize a daemon logical-failure envelope into a thrown exit-1 ERROR. The\n * envelope is data (it parsed cleanly), so this is NOT daemon-down; we surface\n * the daemon's own `error`/`reason` verbatim so the user sees the real cause\n * (e.g. \"store is read-only (daemon down) — context_index is unavailable\").\n */\nfunction failTool(label: string, envelope: ToolFailure, opts: CliOptions): never {\n const detail = envelope.error ?? envelope.reason ?? 'unknown failure';\n fail(EXIT.ERROR, `${label}: ${detail}`, opts);\n}\n\n/** Narrow a raw wire hit (RetrieverHit) into the rendered {@link ContextHit}. */\nfunction toHit(raw: unknown): ContextHit {\n const h = (raw ?? {}) as Record<string, unknown>;\n return {\n path: typeof h.path === 'string' ? h.path : '<unknown>',\n score: typeof h.score === 'number' ? h.score : 0,\n snippet: typeof h.snippet === 'string' ? h.snippet : '',\n source: typeof h.source === 'string' ? h.source : '',\n };\n}\n\n// ---------------------------------------------------------------------------\n// `noir context search <query> [--limit N]`\n// ---------------------------------------------------------------------------\nexport interface ContextSearchOptions extends ContextOptions {\n query: string;\n /** Raw `--limit` string from commander; parsed + validated here. */\n limit?: string;\n}\n\n/**\n * Run `context_search` against the daemon when it is up, or fall back to the\n * IN-PROCESS read-only engine when the daemon probe reports it down (S9 DS-5).\n *\n * A daemon-down probe is a READ degradation, not a write failure: the read-only\n * store keeps working, so instead of exit 4 the search runs against a fresh\n * in-process {@link withInProcessRead} ContextEngine over the same project\n * store (BM25-only when the embedder is unavailable — F8). Writes (`context\n * index`) keep the daemon-required exit-4 path. When the daemon IS up, the\n * probe result is ignored and the normal daemon round-trip proceeds unchanged\n * (the probe only ever short-circuits on a CONFIRMED down).\n *\n * The probe is best-effort and CONSERVATIVE: an unavailable probe (a host that\n * stubs the daemon-client without a `probeDaemon` export, or a probe transport\n * hiccup) is treated as \"daemon may be up\" so the command proceeds down the\n * normal daemon path (which maps a genuinely-down daemon to exit 4). The\n * fallback only engages on a CONFIRMED `{running:false}`.\n */\nexport async function contextSearch(opts: ContextSearchOptions): Promise<void> {\n const limit = parseLimit(opts.limit, 'context search', opts);\n let probe: DaemonProbe;\n try {\n probe = await probeDaemon(opts);\n } catch {\n // Conservative: can't confirm the daemon is down → use the daemon path.\n probe = { running: true };\n }\n if (!probe.running) {\n await withInProcessRead(opts, async (engines) => {\n const result = await engines.context.search(opts.query, {\n ...(limit === undefined ? {} : { limit }),\n });\n const data: ContextSearchData = {\n query: opts.query,\n hits: result.results.map((h) => ({\n path: h.path,\n score: h.score,\n snippet: h.snippet,\n source: h.source,\n })),\n consumedTokens: result.consumedTokens,\n truncated: result.truncated,\n degraded: result.degraded,\n mode: result.mode,\n };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSearch(data, opts);\n });\n return;\n }\n const res = await callDaemonTool<ContextSearchResult | ToolFailure>(opts, 'context_search', {\n query: opts.query,\n ...(limit === undefined ? {} : { limit }),\n });\n if (res.ok !== true) failTool('context search', res, opts);\n\n const data: ContextSearchData = {\n query: opts.query,\n hits: Array.isArray(res.results) ? (res.results as unknown[]).map(toHit) : [],\n consumedTokens: typeof res.consumedTokens === 'number' ? res.consumedTokens : 0,\n truncated: res.truncated === true,\n degraded: res.degraded === true,\n mode: typeof res.mode === 'string' ? res.mode : 'unknown',\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSearch(data, opts);\n}\n\nfunction renderSearch(data: ContextSearchData, opts: CliOptions): void {\n const flag = data.degraded ? ` ${badge('warn', 'degraded: BM25-only')}` : '';\n const trunc = data.truncated ? ' (budget hit — results truncated)' : '';\n log(\n `context search — ${data.hits.length} hit${data.hits.length === 1 ? '' : 's'} · ${data.mode} · ${data.consumedTokens} tokens${flag}${trunc}`,\n opts,\n );\n table(\n data.hits.map((h, i) => ({\n '#': i + 1,\n Path: h.path,\n Score: h.score.toFixed(4),\n Snippet: h.snippet,\n })),\n ['#', 'Path', 'Score', 'Snippet'],\n opts,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `noir context index [--path ...] [--force]`\n// ---------------------------------------------------------------------------\nexport interface ContextIndexOptions extends ContextOptions {\n /** Raw repeated `--path` values from commander (`undefined` ⇒ index root). */\n paths?: string[];\n /** Force a full reindex (drop all chunks+vectors, re-index from scratch). */\n force?: boolean;\n}\n\nexport async function contextIndex(opts: ContextIndexOptions): Promise<void> {\n // force:true → the daemon drops every indexed chunk + vector and re-indexes\n // the registered roots from scratch (spec F1); otherwise the content-hash\n // incremental walk. Omit the key entirely when not forced so the incremental\n // contract (and any daemon defaulting) stays unambiguous on the wire.\n const args: Record<string, unknown> =\n opts.paths && opts.paths.length > 0 ? { paths: opts.paths } : {};\n if (opts.force === true) args.force = true;\n const res = await callDaemonTool<ContextIndexResult | ToolFailure>(opts, 'context_index', args);\n if (res.ok !== true) failTool('context index', res, opts);\n\n const data: ContextIndexData = {\n indexed: typeof res.indexed === 'number' ? res.indexed : 0,\n skipped: typeof res.skipped === 'number' ? res.skipped : 0,\n deleted: typeof res.deleted === 'number' ? res.deleted : 0,\n failed: typeof res.failed === 'number' ? res.failed : 0,\n totalChunks: typeof res.totalChunks === 'number' ? res.totalChunks : 0,\n degraded: res.degraded === true,\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderIndex(data, opts.paths, opts);\n}\n\nfunction renderIndex(data: ContextIndexData, paths: string[] | undefined, opts: CliOptions): void {\n const scope = paths && paths.length > 0 ? paths.join(', ') : '.';\n const flag = data.degraded ? ` ${badge('warn', 'degraded: vectors skipped')}` : '';\n log(`context index — ${scope}${flag}`, opts);\n definitionList(\n [\n { label: 'Indexed (new)', value: data.indexed },\n { label: 'Skipped (unchanged)', value: data.skipped },\n { label: 'Deleted (removed)', value: data.deleted },\n { label: 'Failed', value: data.failed },\n { label: 'Total chunks', value: data.totalChunks },\n ],\n opts,\n );\n if (data.failed > 0) {\n info(`${data.failed} file(s) could not be indexed (binary / IO / encoding).`, opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir context status`\n// ---------------------------------------------------------------------------\nexport async function contextStatus(opts: ContextOptions): Promise<void> {\n const res = await callDaemonTool<ContextStatusData | ToolFailure>(opts, 'context_status');\n if (res.ok !== true) failTool('context status', res, opts);\n // `context_status` already carries its own `ok:true`; reuse it as the payload.\n const data = res;\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderStatus(data, opts);\n}\n\nfunction describeEmbedder(e: ContextStatusData['embedder']): string {\n if (!e || e.kind === 'none') return 'none (BM25-only)';\n const model = typeof e.model === 'string' && e.model.length > 0 ? e.model : '<unset>';\n return `${e.kind} · ${model} (${e.dim}-dim)`;\n}\n\nfunction renderStatus(data: ContextStatusData, opts: CliOptions): void {\n const flag = data.degraded ? ` ${badge('warn', 'degraded')}` : '';\n log(`context status — ${data.projectId}${flag}`, opts);\n definitionList(\n [\n { label: 'Project', value: data.projectId },\n { label: 'Docs', value: data.docCount },\n { label: 'Vectors', value: data.vecCount },\n { label: 'Indexed files', value: data.indexedFiles },\n { label: 'Embedder', value: describeEmbedder(data.embedder) },\n { label: 'Degraded', value: data.degraded ? 'yes' : 'no' },\n ],\n opts,\n );\n}\n","// `.mcp.json` rewrite/restore for workspace membership — the ONLY user-facing\n// file a join/leave touches. It rewrites just the `noir` server entry (preserving\n// every other server the user added) and refuses to clobber a config that does\n// not look Noir-emitted unless `--force` (never a silent overwrite).\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { atomicWriteFile, resolveNoirCommand } from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail } from './output.js';\n\nfunction mcpPathFor(root: string, host: HostId): string {\n return resolveAdapter(host).mcpConfigPath?.({ root }) ?? join(root, '.mcp.json');\n}\n\n/** Read + parse an existing host MCP config, or `null` when the file is absent. */\nfunction readJson(path: string): { mcpServers?: Record<string, unknown> } | null {\n if (!existsSync(path)) return null; // absent → fresh write is fine\n // Unparseable (JSONC/commented/trailing-comma) THROWS so the caller can refuse —\n // never silently clobber a user-edited file that happens to not be strict JSON.\n return JSON.parse(readFileSync(path, 'utf8')) as { mcpServers?: Record<string, unknown> };\n}\n\n/** Load an existing config, refusing (exit 1) on an unparseable file unless --force. */\nfunction loadExisting(\n path: string,\n opts: CliOptions & { force?: boolean },\n): { mcpServers?: Record<string, unknown> } | null {\n let existing: { mcpServers?: Record<string, unknown> } | null;\n try {\n existing = readJson(path);\n } catch {\n if (opts.force !== true) {\n fail(\n EXIT.ERROR,\n `Refusing to rewrite ${path}: it is not valid JSON (add --force to overwrite).`,\n opts,\n );\n }\n existing = null; // --force: overwrite the unparseable file\n }\n if (existing !== null && !isNoirEmitted(existing.mcpServers) && opts.force !== true) {\n fail(\n EXIT.ERROR,\n `Refusing to rewrite ${path}: it does not look like a Noir-emitted config (add --force to overwrite).`,\n opts,\n );\n }\n return existing;\n}\n\nfunction isNoirEmitted(mcpServers: Record<string, unknown> | undefined): boolean {\n const noir = mcpServers?.noir as { command?: unknown; type?: unknown } | undefined;\n if (!noir) return false;\n if (typeof noir.command === 'string') return true;\n return noir.type === 'http';\n}\n\n/** Point the repo's `noir` MCP entry at the workspace daemon (with the member `?p=` identity). */\nexport function writeWorkspaceHttpEntry(\n root: string,\n host: HostId,\n url: string,\n projectId: string,\n opts: CliOptions & { force?: boolean },\n): void {\n const path = mcpPathFor(root, host);\n const existing = loadExisting(path, opts);\n const next = {\n ...(existing ?? {}),\n mcpServers: {\n ...(existing?.mcpServers ?? {}),\n noir: { type: 'http', url: `${url}?p=${projectId}` },\n },\n };\n atomicWriteFile(path, `${JSON.stringify(next, null, 2)}\\n`);\n}\n\n/** Restore the repo's `noir` MCP entry to stdio (used by `workspace leave`). */\nexport function writeStdioEntry(\n root: string,\n host: HostId,\n opts: CliOptions & { force?: boolean },\n): void {\n const path = mcpPathFor(root, host);\n const existing = loadExisting(path, opts);\n const noir = { command: resolveNoirCommand(), args: ['mcp', 'serve', '--stdio'] };\n const next = { ...(existing ?? {}), mcpServers: { ...(existing?.mcpServers ?? {}), noir } };\n atomicWriteFile(path, `${JSON.stringify(next, null, 2)}\\n`);\n}\n","// `noir workspace *` + the `--workspace` branch of `noir daemon start` + `noir daemon join`.\n//\n// A workspace is a named cross-repo sharing unit. `daemon start --workspace <name>`\n// founds/joins it from the current repo and starts the detached workspace daemon;\n// `daemon join <name>` joins it from another repo. `workspace list/status/leave/stop`\n// manage membership. Joining writes a `.noir/workspace.json` marker + rewrites the\n// repo's `.mcp.json` `noir` entry to the workspace daemon URL (`?p=<projectId>`);\n// leaving reverses both. Default transport (stdio) and project behavior are\n// untouched unless a repo explicitly joins.\nimport { readdirSync } from 'node:fs';\nimport {\n clearWorkspaceMarker,\n ensureWorkspaceRegistry,\n isValidWorkspaceName,\n isWorkspaceMember,\n loadProjectInfo,\n type ProjectInfo,\n readWorkspaceMarker,\n readWorkspaceRegistry,\n removeWorkspaceMember,\n upsertWorkspaceMember,\n workspaceHomeDir,\n writeWorkspaceMarker,\n} from '@noir-ai/core';\nimport {\n clearWorkspaceDaemonRecord,\n ensureWorkspaceDaemonRunning,\n pidAlive,\n readWorkspaceDaemonRecord,\n spawnDetachedWorkspaceDaemon,\n} from '@noir-ai/daemon';\nimport { type CliOptions, EXIT, fail, info, log } from '../output.js';\nimport { writeStdioEntry, writeWorkspaceHttpEntry } from '../workspace-mcp.js';\n\nexport interface WorkspaceStartOptions extends CliOptions {\n name: string;\n detach?: boolean;\n detachChild?: boolean;\n force?: boolean;\n}\nexport interface WorkspaceJoinOptions extends CliOptions {\n name: string;\n force?: boolean;\n}\nexport interface WorkspaceStatusOptions extends CliOptions {\n name?: string;\n}\nexport interface WorkspaceLeaveOptions extends CliOptions {\n force?: boolean;\n}\n\nfunction loadProjectOrFail(opts: CliOptions): ProjectInfo {\n try {\n return loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n}\n\nfunction finishJoin(\n project: ProjectInfo,\n name: string,\n url: string,\n opts: CliOptions & { force?: boolean },\n): void {\n writeWorkspaceMarker(project.root, name);\n writeWorkspaceHttpEntry(project.root, project.config.host, url, project.id, opts);\n}\n\n/** `noir daemon start --workspace <name>` — found a workspace + start its daemon. */\nexport async function daemonStartWorkspace(opts: WorkspaceStartOptions): Promise<void> {\n const project = loadProjectOrFail(opts);\n if (!isValidWorkspaceName(opts.name)) {\n fail(\n EXIT.USAGE,\n `invalid workspace name ${JSON.stringify(opts.name)} (lowercase letters, digits, and dashes).`,\n opts,\n );\n }\n // Found/join: register THIS repo as a member (idempotent by projectId).\n upsertWorkspaceMember(ensureWorkspaceRegistry(opts.name), {\n projectId: project.id,\n root: project.root,\n joinedAt: Date.now(),\n });\n\n // Detached-child path (set by spawnDetachedWorkspaceDaemon): run in-process.\n if (opts.detachChild === true) {\n const ensured = await ensureWorkspaceDaemonRunning({\n name: opts.name,\n project,\n idleTimeoutSec: project.config.workspace.idleTimeoutSec,\n });\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { mode: 'detached' } })}\\n`);\n return;\n }\n info('noir workspace daemon: detached foreground. Ctrl+C to stop.', opts);\n log(`workspace daemon listening at ${ensured.url}`, opts);\n return;\n }\n\n // --detach: fork a detached child; the parent writes the marker + .mcp.json.\n if (opts.detach === true) {\n const spawned = await spawnDetachedWorkspaceDaemon({ name: opts.name, project });\n finishJoin(project, opts.name, `http://127.0.0.1:${spawned.port}/mcp`, opts);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { mode: 'detached', pid: spawned.pid, port: spawned.port } })}\\n`,\n );\n return;\n }\n info(\n `workspace \"${opts.name}\" daemon started (pid ${spawned.pid}, port ${spawned.port}).`,\n opts,\n );\n return;\n }\n\n // Foreground: the workspace server keeps this process alive.\n const ensured = await ensureWorkspaceDaemonRunning({\n name: opts.name,\n project,\n idleTimeoutSec: project.config.workspace.idleTimeoutSec,\n });\n finishJoin(project, opts.name, ensured.url, opts);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { url: ensured.url, port: ensured.port, reused: !ensured.started } })}\\n`,\n );\n return;\n }\n info('noir workspace daemon: foreground mode. Ctrl+C to stop.', opts);\n log(`workspace daemon listening at ${ensured.url}`, opts);\n}\n\n/** `noir daemon join <name>` — join an existing workspace from this repo. */\nexport async function daemonJoin(opts: WorkspaceJoinOptions): Promise<void> {\n const project = loadProjectOrFail(opts);\n if (!isValidWorkspaceName(opts.name)) {\n fail(EXIT.USAGE, `invalid workspace name ${JSON.stringify(opts.name)}.`, opts);\n }\n const reg = readWorkspaceRegistry(opts.name);\n if (!reg) {\n fail(\n EXIT.ERROR,\n `workspace ${JSON.stringify(opts.name)} not found — start it from a member repo with \\`noir daemon start --workspace ${opts.name}\\`.`,\n opts,\n );\n }\n const ensured = await ensureWorkspaceDaemonRunning({\n name: opts.name,\n project,\n idleTimeoutSec: project.config.workspace.idleTimeoutSec,\n });\n upsertWorkspaceMember(reg, { projectId: project.id, root: project.root, joinedAt: Date.now() });\n finishJoin(project, opts.name, ensured.url, opts);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { joined: true, url: ensured.url, port: ensured.port } })}\\n`,\n );\n return;\n }\n info(`joined workspace ${JSON.stringify(opts.name)} (${ensured.url}).`, opts);\n}\n\n/** `noir workspace list` — every workspace on this machine + member count. */\nexport async function workspaceList(opts: CliOptions): Promise<void> {\n let names: string[] = [];\n try {\n names = readdirSync(workspaceHomeDir(), { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n } catch {\n names = [];\n }\n const rows = names.map((n) => {\n const reg = readWorkspaceRegistry(n);\n return {\n name: n,\n members: reg?.members.length ?? 0,\n // A record file whose pid is dead (daemon killed without cleanup) must not\n // read as \"running\" — mirror the pidAlive liveness `workspace status` uses.\n running: (() => {\n const rec = readWorkspaceDaemonRecord(n);\n return rec !== null && pidAlive(rec.pid);\n })(),\n };\n });\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { workspaces: rows } })}\\n`);\n return;\n }\n if (rows.length === 0) {\n info('No workspaces yet.', opts);\n return;\n }\n for (const r of rows) {\n log(`${r.name} — ${r.members} member(s)${r.running ? ', running' : ''}`, opts);\n }\n}\n\n/** `noir workspace status [name]` — members + daemon liveness. */\nexport async function workspaceStatus(opts: WorkspaceStatusOptions): Promise<void> {\n const name = opts.name ?? readWorkspaceMarker(process.cwd()) ?? undefined;\n if (name === undefined) {\n fail(EXIT.USAGE, 'workspace status requires a name (or run from a joined repo).', opts);\n }\n if (!isValidWorkspaceName(name)) {\n fail(EXIT.USAGE, `invalid workspace name ${JSON.stringify(name)}.`, opts);\n }\n const reg = readWorkspaceRegistry(name);\n const rec = readWorkspaceDaemonRecord(name);\n const running = rec !== null && pidAlive(rec.pid);\n const data = {\n name,\n members: reg?.members.map((m) => m.projectId) ?? [],\n running,\n ...(rec ? { pid: rec.pid, port: rec.port } : {}),\n };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(\n `workspace ${name}: ${running ? `running (pid ${rec?.pid}, port ${rec?.port})` : 'not running'}`,\n opts,\n );\n for (const m of data.members) log(` member: ${m}`, opts);\n}\n\n/** `noir workspace leave` — remove this repo from membership + restore stdio. */\nexport async function workspaceLeave(opts: WorkspaceLeaveOptions): Promise<void> {\n const project = loadProjectOrFail(opts);\n const name = readWorkspaceMarker(project.root);\n if (name === null) {\n fail(EXIT.USAGE, 'this repo is not joined to a workspace.', opts);\n }\n // Restore stdio FIRST — it is the one step that can fail (a non-Noir .mcp.json\n // is refused without `--force`). Doing it last would strand the repo mid-leave:\n // membership removed + marker cleared, but .mcp.json still pointed at a\n // workspace the repo no longer belongs to.\n writeStdioEntry(project.root, project.config.host, opts);\n const reg = readWorkspaceRegistry(name);\n if (reg && isWorkspaceMember(reg, project.id)) {\n removeWorkspaceMember(reg, project.id);\n }\n clearWorkspaceMarker(project.root);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { left: name } })}\\n`);\n return;\n }\n info(`left workspace ${JSON.stringify(name)}.`, opts);\n}\n\n/** `noir workspace stop [name]` — stop the workspace daemon (membership retained). */\nexport async function workspaceStop(opts: WorkspaceStatusOptions): Promise<void> {\n const name = opts.name ?? readWorkspaceMarker(process.cwd()) ?? undefined;\n if (name === undefined) {\n fail(EXIT.USAGE, 'workspace stop requires a name (or run from a joined repo).', opts);\n }\n if (!isValidWorkspaceName(name)) {\n fail(EXIT.USAGE, `invalid workspace name ${JSON.stringify(name)}.`, opts);\n }\n const rec = readWorkspaceDaemonRecord(name);\n if (rec === null || !pidAlive(rec.pid)) {\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { running: false, stopped: false } })}\\n`,\n );\n return;\n }\n info('workspace daemon is not running.', opts);\n return;\n }\n try {\n process.kill(rec.pid, 'SIGTERM');\n } catch {\n // already exited between the probe and the signal\n }\n clearWorkspaceDaemonRecord(name);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { running: false, stopped: true, pid: rec.pid } })}\\n`,\n );\n return;\n }\n info(`stopped workspace daemon (pid ${rec.pid}).`, opts);\n}\n","// S9 — `noir daemon {start,stop,status,restart}` (+ `token`, spec 6.1).\n//\n// Honest daemon UX (S9 / spec §8). `noir daemon start` runs the daemon either\n// FOREGROUND or DETACHED, depending on flags:\n// - default (foreground): `ensureDaemonRunning` (from @noir-ai/daemon) starts\n// a fresh in-process HTTP server — whose listen handle + idle timer +\n// SIGINT/SIGTERM handlers (installed inside `startHttpServer`) keep this CLI\n// process alive until idle-stop or a signal — or reuses an already-healthy\n// one and exits.\n// - `--detach` (parent path, D1): `spawnDetachedDaemon` forks a detached child\n// `noir daemon start --_detached-child` (unref'd + silent stdio), waits for\n// the child's daemon record + `/health` to confirm it is serving, reports\n// `{mode:'detached', pid, port}`, and the PARENT exits — the child keeps the\n// daemon alive in the background.\n// - `--_detached-child` (child path, D2; hidden, set by the parent's spawn):\n// we ARE the detached child — run `ensureDaemonRunning` in-process (the HTTP\n// server keeps THIS process alive) and emit `{mode:'detached'}`. The child\n// is the SINGLE writer of the daemon record (its own pid), so the parent\n// discovers the port by polling that record.\n// Both detached paths guard double-spawn: an already-healthy daemon is reported\n// `{mode:'detached', reused:true}` and never spawned twice.\n//\n// Stream discipline (S9): `--json` emits the versioned `{ok,data}` envelope\n// to stdout (the only stdout write); every human diagnostic goes to stderr via\n// the centralized helpers. Exit codes follow the S9 contract: a missing/stale\n// daemon record on `status` → exit 4 (DAEMON_DOWN); an uninitialized project\n// (`loadProjectInfo` throws) → exit 1 with the hint.\n\nimport { loadProjectInfo, type ProjectInfo } from '@noir-ai/core';\nimport {\n clearProjectDaemonRecord,\n ensureDaemonRunning,\n pidAlive,\n readDaemonToken,\n readProjectDaemonRecord,\n spawnDetachedDaemon,\n} from '@noir-ai/daemon';\nimport { PROBE_TIMEOUT_MS } from '../daemon-client.js';\nimport { type CliOptions, EXIT, fail, info, log, spinner } from '../output.js';\nimport { daemonStartWorkspace } from './workspace.js';\n\n/** Options accepted by `daemon` sub-commands (the global flags only). */\nexport interface DaemonOptions extends CliOptions {}\n\n/** `daemon start` adds the real `--detach` flag (D1) plus the hidden\n * `--_detached-child` marker (D2) the detached child carries. */\nexport interface DaemonStartOptions extends DaemonOptions {\n /** `--detach`: parent path — fork a detached child and exit. */\n detach?: boolean;\n /** `--_detached-child` (hidden, reserved): we ARE the detached child. */\n detachChild?: boolean;\n /** `--workspace <name>`: start a shared cross-repo workspace daemon instead. */\n workspace?: string;\n /** `--force`: overwrite a non-Noir `.mcp.json` entry (the `--workspace` join). */\n force?: boolean;\n}\n\n/** Human label for the detached mode in `status` output. */\nconst MODE = 'foreground' as const;\n\n/** Shape of the `/health` body the daemon's HTTP server serves. */\ninterface HealthBody {\n ok?: boolean;\n pid?: number;\n projectId?: string;\n uptimeSec?: number;\n}\n\nfunction formatUptime(sec: number): string {\n if (!Number.isFinite(sec) || sec < 0) return 'unknown';\n if (sec < 60) return `${sec}s`;\n if (sec < 3600) return `${Math.floor(sec / 60)}m ${sec % 60}s`;\n return `${Math.floor(sec / 3600)}h ${Math.floor((sec % 3600) / 60)}m`;\n}\n\n/** Resolve the CALLER's project id — the lookup key for this project's daemon\n * record (the record is per-project). `undefined` when uninitialized: there is\n * no project whose daemon we could legitimately report or stop. */\nfunction resolveCallerProjectId(): string | undefined {\n try {\n return loadProjectInfo(process.cwd()).id;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort `/health` probe (never throws). Bounded — a blackhole socket that\n * accepts TCP but never answers must not hang `noir daemon start/status/stop`\n * indefinitely. When `expectedPid` is given, the probe ALSO requires the\n * responding daemon's own pid to match (the PID-reuse guard: a foreign process\n * that recycled the recorded pid must never be signalled as \"our daemon\"). */\nasync function isHealthy(\n port: number,\n expectedPid?: number,\n expectedProjectId?: string,\n): Promise<boolean> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n if (res.status !== 200) return false;\n if (expectedPid === undefined) return true;\n const body = (await res.json()) as HealthBody | undefined;\n if (body?.ok !== true || body.pid !== expectedPid) return false;\n // Cross-project isolation: only reuse a daemon that serves the expected\n // project (absent /health projectId — a pre-1.12 daemon — is not trusted).\n if (expectedProjectId !== undefined && body.projectId !== expectedProjectId) return false;\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon start` (foreground default; `--detach` + `--_detached-child`)\n// ---------------------------------------------------------------------------\n/**\n * Start the Noir daemon — foreground by default, or detached via `--detach`.\n *\n * Three modes, chosen by the flags (all three guard double-spawn: an\n * already-healthy daemon is reported `{mode:'detached', reused:true}` / the\n * foreground `reused:true` envelope and never started twice):\n *\n * 1. `--_detached-child` (D2; hidden, only ever set by `spawnDetachedDaemon`'s\n * child argv): we ARE the detached daemon. Run `ensureDaemonRunning`\n * in-process — the started HTTP server + idle timer + signal handlers keep\n * THIS process alive as the background daemon — emit `{mode:'detached'}`\n * (or `{mode:'detached', reused:true}` when a healthy daemon is reused, in\n * which case the redundant child exits) and return. The child is the SINGLE\n * writer of the daemon record (its own pid), which is how the parent learns\n * the port. We deliberately do NOT call `ensured.stop()` — that would undo\n * the daemon we just started (or tear down a reused one owned elsewhere).\n * 2. `--detach` (D1; parent path): `spawnDetachedDaemon` forks the detached\n * child, waits until the child's record + `/health` confirm it is serving,\n * emits `{mode:'detached', pid, port}`, and the PARENT returns (and thus\n * exits — it never blocks). Guard first: if a daemon is ALREADY healthy,\n * report `{mode:'detached', reused:true}` and return WITHOUT spawning.\n * 3. Foreground (default): `ensureDaemonRunning` in-process. When it STARTS a\n * daemon, the HTTP server keeps this process alive until\n * SIGINT/SIGTERM/idle-timeout (the function returns but the process does not\n * exit); when it REUSES a healthy daemon, this process reports and exits.\n *\n * `--json` emits the one envelope to stdout before the process blocks/returns.\n */\nexport async function daemonStart(opts: DaemonStartOptions): Promise<void> {\n // Workspace mode: `--workspace <name>` founds/joins a cross-repo workspace and\n // starts its daemon (a separate daemon kind — never the project daemon).\n if (opts.workspace !== undefined) {\n await daemonStartWorkspace({\n ...opts,\n name: opts.workspace,\n ...(opts.detach === true ? { detach: true } : {}),\n ...(opts.detachChild === true ? { detachChild: true } : {}),\n });\n return;\n }\n // A throw from loadProjectInfo on an uninitialized project must route through\n // fail() — otherwise under --json stdout stays EMPTY (the raw error only\n // reaches stderr), violating the S9 `{ok:false}` envelope contract.\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n\n // D2 — detached CHILD path. The parent forked us via `--_detached-child`;\n // run the daemon in-process (the HTTP server keeps THIS process alive).\n if (opts.detachChild === true) {\n const child = await ensureDaemonRunning({\n project,\n idleTimeoutSec: project.config.daemon.idleTimeoutSec,\n ...(project.config.daemon.port !== undefined ? { port: project.config.daemon.port } : {}),\n });\n if (child.started) {\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { mode: 'detached' } })}\\n`);\n return;\n }\n // The HTTP server + idle timer + signal handlers inside startHttpServer\n // keep this child process alive past `return` — it IS the daemon now.\n info('noir daemon: detached foreground. Ctrl+C to stop.', opts);\n log(`Noir daemon listening at ${child.url}`, opts);\n return;\n }\n // An already-healthy daemon was reused — this redundant child reports and\n // exits (the parent's double-spawn guard should have caught this, but the\n // child re-checks so two racing spawns can never start two daemons).\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { mode: 'detached', reused: true } })}\\n`,\n );\n return;\n }\n info('Noir daemon already running; detached child exiting.', opts);\n return;\n }\n\n // D1 — `--detach` PARENT path. Fork a detached child and let it own the\n // daemon; the parent reports the backgrounded daemon and exits.\n if (opts.detach === true) {\n // Double-spawn guard: only spawn a child if no daemon is already healthy\n // (the child would merely reuse it and exit — a wasted detached process).\n // Scoped read: THIS project's record, so a daemon serving another project\n // is neither adopted nor counted as ours.\n const existing = readProjectDaemonRecord(project.id);\n if (\n existing &&\n pidAlive(existing.pid) &&\n (await isHealthy(existing.port, existing.pid, project.id))\n ) {\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { mode: 'detached', reused: true } })}\\n`,\n );\n return;\n }\n info(\n `Noir daemon already running at http://127.0.0.1:${existing.port}/mcp (detach not needed).`,\n opts,\n );\n return;\n }\n let spawned: Awaited<ReturnType<typeof spawnDetachedDaemon>>;\n try {\n spawned = await spawnDetachedDaemon({ project });\n } catch (err) {\n fail(\n EXIT.ERROR,\n `noir daemon start --detach failed: ${err instanceof Error ? err.message : String(err)}`,\n opts,\n );\n }\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { mode: 'detached', pid: spawned.pid, port: spawned.port } })}\\n`,\n );\n return;\n }\n info(`Noir daemon started in the background (pid ${spawned.pid}, port ${spawned.port}).`, opts);\n // The parent returns (and exits); the detached child owns the daemon.\n return;\n }\n\n // Foreground (default) — run the daemon in-process.\n const ds = spinner('Starting Noir daemon...', opts).start();\n let ensured: Awaited<ReturnType<typeof ensureDaemonRunning>>;\n try {\n ensured = await ensureDaemonRunning({\n project,\n idleTimeoutSec: project.config.daemon.idleTimeoutSec,\n ...(project.config.daemon.port !== undefined ? { port: project.config.daemon.port } : {}),\n });\n } catch (err) {\n ds.fail('Daemon failed to start');\n fail(\n EXIT.ERROR,\n `noir daemon start failed: ${err instanceof Error ? err.message : String(err)}`,\n opts,\n );\n }\n\n if (ensured.started) {\n ds.succeed('Daemon started');\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n data: {\n url: ensured.url,\n port: ensured.port,\n pid: process.pid,\n mode: MODE,\n reused: false,\n },\n })}\\n`,\n );\n return;\n }\n info('noir daemon: foreground mode. Ctrl+C to stop.', opts);\n log(`Noir daemon listening at ${ensured.url}`, opts);\n // NOTE: the started HTTP server + idle timer + signal handlers installed\n // inside startHttpServer keep this process alive past `return`. We\n // intentionally do NOT call ensured.stop(): that would undo the start.\n return;\n }\n\n // Reused an already-running daemon — this process exits after reporting.\n ds.succeed('Daemon already running');\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { url: ensured.url, port: ensured.port, reused: true } })}\\n`,\n );\n return;\n }\n log(`Noir daemon already running at ${ensured.url}`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon stop`\n// ---------------------------------------------------------------------------\n/**\n * Stop the recorded daemon (SIGTERM its pid) and clear the record.\n *\n * Best-effort exit 0: a missing record (\"not running\") and a signal failure\n * both report and succeed, matching the pre-migration behavior. `--json` emits\n * `{ok:true, data:{running, stopped, pid?}}` to stdout.\n */\nexport async function daemonStop(opts: DaemonOptions): Promise<void> {\n // The record is keyed by ProjectId, so the caller's project IS the scope: a\n // daemon serving another project is simply not visible here (no\n // cross-project guard needed — the foreign record is a different file). When\n // the caller has no project there is no record to name, and guessing would\n // mean either stopping nothing or signalling a stranger's daemon.\n const callerProject = resolveCallerProjectId();\n if (callerProject === undefined) {\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { running: false, stopped: false } })}\\n`,\n );\n return;\n }\n info('No Noir daemon is running for this directory (not a Noir project).', opts);\n return;\n }\n\n const rec = readProjectDaemonRecord(callerProject);\n if (!rec) {\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { running: false, stopped: false } })}\\n`,\n );\n return;\n }\n info('No Noir daemon is running.', opts);\n return;\n }\n\n const ds = spinner(`Stopping daemon (pid ${rec.pid})...`, opts).start();\n let signalled = false;\n let errMsg: string | undefined;\n // Guard PID reuse: only SIGTERM the recorded pid if it still answers as OUR\n // daemon on the recorded port. If the daemon crashed and the pid was recycled\n // by an unrelated process, a blind `process.kill(pid)` would signal an\n // innocent process — so probe `/health` first and clear the stale record if it\n // does not answer.\n const healthy = pidAlive(rec.pid) && (await isHealthy(rec.port, rec.pid, callerProject));\n if (healthy) {\n try {\n process.kill(rec.pid, 'SIGTERM');\n signalled = true;\n } catch (err) {\n // Process may have already exited between the probe and the signal.\n errMsg = err instanceof Error ? err.message : String(err);\n }\n }\n // Our own record only — unconditionally, now that it is unambiguously ours.\n clearProjectDaemonRecord(callerProject);\n\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n data: {\n running: signalled,\n stopped: signalled,\n pid: rec.pid,\n ...(errMsg !== undefined ? { error: errMsg } : {}),\n },\n })}\\n`,\n );\n return;\n }\n if (signalled) {\n ds.succeed(`Stopped Noir daemon (pid ${rec.pid})`);\n } else {\n // Two non-signalled cases: a healthy probe whose signal failed (errMsg set),\n // or a pid that no longer answers as our daemon.\n const reason =\n errMsg !== undefined\n ? errMsg\n : 'the recorded pid did not answer as our daemon (stale record cleared)';\n ds.warn(`Daemon (pid ${rec.pid}) could not be signalled: ${reason}`);\n info(`Noir daemon (pid ${rec.pid}) could not be signalled: ${reason}`, opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon status`\n// ---------------------------------------------------------------------------\n/**\n * Report daemon liveness: read the record, confirm the pid is alive, and ping\n * `/health` for a live uptime. A missing/stale/unresponsive record maps to\n * exit 4 (DAEMON_DOWN) — the \"probed by script\" contract.\n *\n * `--json` emits `{ok:true, data:{running,pid,port,startedAt,uptimeSec,mode}}`\n * on success; failure goes through `fail()` (structured `{ok:false,error}` on\n * stdout under `--json`, plain message on stderr otherwise) with exit 4.\n */\nexport async function daemonStatus(opts: DaemonOptions): Promise<void> {\n // The record is keyed by ProjectId, so the caller's project IS the scope: a\n // daemon serving another project is a different file, invisible here (no\n // cross-project exit-4 block left to perform).\n const callerProject = resolveCallerProjectId();\n if (callerProject === undefined) {\n fail(EXIT.DAEMON_DOWN, 'Noir daemon is not running (not a Noir project).', opts);\n }\n const rec = readProjectDaemonRecord(callerProject);\n if (!rec) {\n fail(EXIT.DAEMON_DOWN, 'Noir daemon is not running (start with `noir daemon start`).', opts);\n }\n // `fail` returns `never` → TS narrows `rec` to ProjectDaemonRecord below.\n\n if (!pidAlive(rec.pid)) {\n clearProjectDaemonRecord(callerProject);\n fail(EXIT.DAEMON_DOWN, 'Noir daemon is not running (stale record removed).', opts);\n }\n\n // Live liveness probe: the daemon's HTTP server answers GET /health with\n // `{ok, pid, uptimeSec}`. A dead port / non-200 / unreachable host ⇒ stale.\n // Bounded like the other probes — a blackhole socket must not hang `noir\n // daemon status` (the \"probed by script\" exit-4 contract).\n const hs = spinner('Probing daemon health...', opts).start();\n let health: HealthBody | null = null;\n try {\n const res = await fetch(`http://127.0.0.1:${rec.port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n if (res.status === 200) {\n health = (await res.json()) as HealthBody;\n }\n } catch {\n // ECONNREFUSED / DNS / timeout — treat as not-running below.\n health = null;\n }\n // PID-reuse guard (same invariant as @noir-ai/daemon ensure.ts isHealthy +\n // this file's isHealthy): the responding /health must carry OUR recorded pid —\n // a MISSING or mismatched pid means a foreign process (or one that recycled\n // the pid) holds the port. Report stale, never \"running\" with the wrong\n // process's uptime. Distinguish \"no health body at all\" (port unresponsive)\n // from \"body present but pid missing/mismatched\" (foreign process).\n const pidOk = typeof health?.pid === 'number' && health.pid === rec.pid;\n if (health?.ok !== true || !pidOk) {\n hs.fail('Daemon not responding');\n clearProjectDaemonRecord(callerProject);\n fail(\n EXIT.DAEMON_DOWN,\n health !== null && !pidOk\n ? 'Noir daemon is not running (pid missing/mismatched — a foreign process holds the recorded port; stale record removed).'\n : 'Noir daemon is not running (port unresponsive; stale record removed).',\n opts,\n );\n }\n\n hs.succeed('Daemon healthy');\n\n // Prefer the daemon's own uptime count; fall back to the record's startedAt.\n const uptimeSec =\n typeof health?.uptimeSec === 'number'\n ? health.uptimeSec\n : Math.max(0, Math.floor((Date.now() - rec.startedAt) / 1000));\n const data = {\n running: true,\n pid: rec.pid,\n port: rec.port,\n startedAt: rec.startedAt,\n uptimeSec,\n // Honest ownership: the detached child writes `mode:'detached'`; anything\n // else (a record written before the field existed, foreground start)\n // reports 'foreground'.\n mode: rec.mode ?? MODE,\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(\n `Noir daemon: running (pid ${data.pid}, port ${data.port}, up ${formatUptime(uptimeSec)}, ${data.mode})`,\n opts,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon token`\n// ---------------------------------------------------------------------------\n/**\n * Print this project's daemon bearer token (spec 6.1) — the command a host's\n * MCP `headersHelper` runs at connect time so no secret lives in a config file.\n *\n * stdout carries the token and NOTHING else: no banner, no decoration, no\n * second line — the command's whole purpose is to be consumed by a program.\n * `--json` emits the S9 `{ok:true,data:{token}}` envelope instead (the same data\n * channel, self-describing). Every diagnostic goes to stderr.\n *\n * Every \"no usable token\" outcome is exit 4 (DAEMON_DOWN), like `daemon status`:\n * an uninitialized project, no daemon record, a record whose daemon is gone, or\n * a record with no token file beside it. A token is a credential for a LIVE\n * daemon — printing the token of a daemon that is not running would authenticate\n * nothing while looking like success, which is exactly the silent failure the\n * `headersHelper` contract must not have.\n *\n * The token is read from disk at CALL time (`~/.noir/daemons/<projectId>.token`)\n * and never cached: the daemon regenerates it on every start, so a remembered\n * copy is a 401.\n */\nexport async function daemonToken(opts: DaemonOptions): Promise<void> {\n // Same caller-scope resolution as status/stop: the project id IS the record\n // (and token) lookup key, so a daemon serving another project is invisible\n // here rather than mis-reported.\n const callerProject = resolveCallerProjectId();\n if (callerProject === undefined) {\n fail(EXIT.DAEMON_DOWN, 'Noir daemon is not running (not a Noir project).', opts);\n }\n const rec = readProjectDaemonRecord(callerProject);\n if (!rec) {\n fail(\n EXIT.DAEMON_DOWN,\n 'Noir daemon is not running (no daemon record — start it with `noir daemon start`).',\n opts,\n );\n }\n // A record whose process is gone is stale, and liveness alone is not enough:\n // an unrelated process can recycle the recorded pid. /health must answer with\n // THIS pid (and project) — the same ownership proof `daemon status` demands —\n // so a recycled pid cannot mint a token for a daemon that is not there.\n if (!pidAlive(rec.pid) || !(await isHealthy(rec.port, rec.pid, callerProject))) {\n fail(\n EXIT.DAEMON_DOWN,\n 'Noir daemon is not running (stale record — start it with `noir daemon start`).',\n opts,\n );\n }\n const token = readDaemonToken(callerProject);\n if (token === null) {\n fail(\n EXIT.DAEMON_DOWN,\n 'Noir daemon is not running (the record has no token beside it — restart it with `noir daemon start`).',\n opts,\n );\n }\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { token } })}\\n`);\n return;\n }\n // The ONLY stdout write on the human path — a headersHelper reads exactly this.\n process.stdout.write(`${token}\\n`);\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon restart`\n// ---------------------------------------------------------------------------\n/**\n * Stop any running daemon, then start a fresh one (foreground by default,\n * detached when `--detach` is forwarded through).\n *\n * The stop sub-step is silenced (no separate envelope / message) so that under\n * `--json` a restart emits exactly ONE stdout envelope (the start result), and\n * so the human output's headline is the new daemon, not the torn-down old one.\n * Foreground `start` blocks once it brings up a daemon, so a foreground restart\n * blocks too — the honest behavior; with `--detach` the parent exits after the\n * detached child is confirmed serving.\n */\nexport async function daemonRestart(opts: DaemonOptions & { detach?: boolean }): Promise<void> {\n // Force the stop to be quiet + non-JSON: its output would either duplicate\n // the start envelope (--json) or noise up the headline (human). The verbose\n // flag is forwarded so a `--verbose` caller still sees stop diagnostics.\n await daemonStop({ ...opts, json: false, quiet: true });\n await daemonStart(opts);\n}\n","// S9 — `noir doctor`.\n//\n// Environment + project health. Runs a fixed set of checks and renders a\n// results table (human, stderr) or the versioned `{ok,data}` envelope (--json,\n// stdout). Exit 1 if any CRITICAL (fail) check is unhealthy, else 0.\n//\n// Severity model (S9 spec F8):\n// - ok — healthy.\n// - warn — degraded but usable (daemon down, onnx missing, provider key\n// missing, project not initialized). NEVER triggers exit 1.\n// - fail — CRITICAL: the product is broken on this host. Only `config` (parse\n// error), `native deps` (better-sqlite3 / sqlite-vec will not load),\n// and `store` (open fails) can fail. Exit 1 if any fail.\n//\n// Honesty rules: provider status uses `resolveModelConfig` — a PURE projection\n// of the user's config + env-var NAME presence; it makes NO live call (blueprint\n// D5). Native-dep probes are best-effort try/catch (the store's native\n// binaries are probed via `vecAvailability`, the single cross-package surface;\n// onnxruntime-node is the context engine's own dep and is probed separately).\n//\n// Cold-start (NF6): the @noir-ai/store import (which loads better-sqlite3) is\n// DYNAMIC, inside the action, so merely running `noir status` / `noir --help`\n// never pays the native-bindings cost. @noir-ai/model is imported eagerly — its\n// provider self-registration is light (SDKs load lazily inside `complete()`).\n//\n// Stream discipline (S9): `--json` writes `{ok:true, data:{checks,summary}}`\n// to stdout (the only stdout write). Doctor always emits `ok:true` because the\n// COMMAND succeeded in producing a diagnosis; the system-health pass/fail is\n// signaled by the EXIT CODE (0 healthy / 1 critical failure) — the same shape\n// `npm audit --json` uses (valid JSON out, non-zero exit when issues found).\n\nimport { spawnSync } from 'node:child_process';\nimport { existsSync, readdirSync, readFileSync, type Stats, statSync } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport {\n detectActiveMethod,\n type InstallMethod,\n type LoadedEnv,\n latestVersionFromCache,\n loadNoirEnv,\n loadProjectInfo,\n NOIR_VERSION,\n type ProjectInfo,\n paths,\n readInstallRecord,\n readUpdateCache,\n} from '@noir-ai/core';\nimport { CURRENT_SCAFFOLD_VERSION, readScaffoldVersion } from '@noir-ai/create';\nimport { pidAlive, readProjectDaemonRecord } from '@noir-ai/daemon';\nimport { resolveModelConfig } from '@noir-ai/model';\nimport { PROBE_TIMEOUT_MS } from '../daemon-client.js';\nimport {\n type CliOptions,\n EXIT,\n error as err,\n log,\n NoirCliError,\n success,\n table,\n warn,\n} from '../output.js';\nimport { type BadgeState, badge } from '../theme.js';\n\n/** Options accepted by `doctor`: the global flags + the opt-in `--dedup`. */\nexport interface DoctorOptions extends CliOptions {\n /** `--dedup`: scan host-context + `.noir/` docs for semantic near-duplicates.\n * Opt-in (loads the local embedder) so a default `noir doctor` stays fast. */\n dedup?: boolean;\n}\n\ntype Severity = 'ok' | 'warn' | 'fail';\n\n/** One row of the doctor report (table cell + `--json` element). */\nexport interface CheckResult {\n name: string;\n status: Severity;\n detail: string;\n}\n\n/** The `data` payload of `noir doctor --json`. */\nexport interface DoctorPayload {\n noir: string;\n checks: CheckResult[];\n summary: { ok: number; warn: number; fail: number };\n /** Scaffold-version drift (slice S). `onDisk` is null when the stamp is\n * absent (uninitialized or pre-Slice-S project); `drift` is true only when\n * a stamp is present AND differs from the engine's current version. */\n scaffold: { onDisk: string | null; current: string; drift: boolean };\n /** RULES.md budget measurement. `null` when the project isn't\n * initialized OR `.noir/rules/RULES.md` is absent — in either case there\n * is nothing to measure and the check row stays informational (warn skip\n * or ok \"no RULES.md\"). Never drives `fail` — over-budget is a `warn`. */\n rules: {\n onDisk: { bytes: number; lines: number };\n budget: { kb: number; maxLines: number };\n over: boolean;\n } | null;\n /** S10 host report. `null` when the project isn't initialized (no resolved\n * host); otherwise carries the active `host` + the list of repo-relative\n * primary artifact paths the doctor verified (AGENTS.md for agents-md/\n * cursor/opencode; the host's native context file when distinct from\n * AGENTS.md (claude → CLAUDE.md, gemini → GEMINI.md); the host's MCP\n * config). The check row is `ok` when\n * all present, `warn` (NEVER `fail`) when any are missing — re-running\n * `noir sync` restores them. */\n host: { active: HostId; expected: string[]; missing: string[] } | null;\n /** S11 publish-readiness report (repo-developer-facing, advisory). `null`\n * when doctor is NOT running from a monorepo checkout (a global install has\n * no workspace package.json set to validate → the row reports `ok`\n * \"skipped\"). Otherwise carries the count of workspace `package.json` files\n * validated + the list of advisory issues found (bad semver, missing\n * `files`, cli missing `bin`, …). The row severity is `warn` when any issues\n * are present, `ok` otherwise — NEVER `fail` (a missing field does not break\n * the local install). */\n publish: { checked: number; issues: string[] } | null;\n /** C1 install-method report. `method` from ~/.noir/install.json (fallback\n * unknown); `latestKnown` from the update cache (never a live call). */\n install: {\n method: InstallMethod;\n installedVersion: string | null;\n latestKnown: string | null;\n } | null;\n}\n\n// ---------------------------------------------------------------------------\n// Small helpers\n// ---------------------------------------------------------------------------\n\n/** Best-effort probe of the `onnxruntime-node` native binding. */\nasync function probeOnnx(): Promise<{ ok: boolean; reason: string }> {\n try {\n // Variable specifier (not a string literal) so tsc does NOT try to resolve\n // a module the CLI package does not directly depend on — onnxruntime-node\n // is the context engine's dep and resolves through ITS node_modules at\n // runtime. Best-effort: hosts that hoist it resolve; otherwise not-loadable.\n const spec = 'onnxruntime-node';\n await import(spec);\n return { ok: true, reason: 'loadable' };\n } catch (e) {\n return { ok: false, reason: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/** Human label for the onnx probe outcome (hides confusing module-not-found noise). */\nfunction describeOnnx(o: { ok: boolean; reason: string }): string {\n if (o.ok) return 'loadable';\n if (/Cannot find (module|package)/i.test(o.reason)) {\n return 'not resolvable from CLI probe (best-effort)';\n }\n const r = o.reason.length > 60 ? `${o.reason.slice(0, 59)}…` : o.reason;\n return `probe failed: ${r}`;\n}\n\nfunction fmtUptime(sec: number): string {\n if (!Number.isFinite(sec) || sec < 0) return 'unknown';\n if (sec < 60) return `${sec}s`;\n if (sec < 3600) return `${Math.floor(sec / 60)}m ${sec % 60}s`;\n return `${Math.floor(sec / 3600)}h ${Math.floor((sec % 3600) / 60)}m`;\n}\n\nfunction msg(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n\n// ---------------------------------------------------------------------------\n// Checks. Each appends to `checks`; project-dependent ones short-circuit to a\n// `warn` \"skipped\" row when the project isn't initialized (so the table still\n// accounts for them, but a pre-init `noir doctor` never exit-1s on them).\n// ---------------------------------------------------------------------------\n\nasync function checkRuntime(checks: CheckResult[]): Promise<void> {\n checks.push({\n name: 'runtime',\n status: 'ok',\n detail: `noir ${NOIR_VERSION} · node ${process.version} · ${process.platform}`,\n });\n}\n\nfunction checkConfig(root: string): { project: ProjectInfo | undefined; result: CheckResult } {\n const configPath = paths.config(root);\n if (!existsSync(configPath)) {\n return {\n project: undefined,\n result: {\n name: 'config',\n status: 'warn',\n detail: `no .noir/config.yml in ${root} — run \\`noir init\\``,\n },\n };\n }\n try {\n const project = loadProjectInfo(root);\n return {\n project,\n result: {\n name: 'config',\n status: 'ok',\n detail: `host=${project.config.host} · mode=${project.config.mode}`,\n },\n };\n } catch (e) {\n return {\n project: undefined,\n result: { name: 'config', status: 'fail', detail: `config parse error: ${msg(e)}` },\n };\n }\n}\n\nasync function checkDaemon(checks: CheckResult[], project: ProjectInfo | undefined): Promise<void> {\n // The record is per-project; without a project id there is no record to look\n // up, so report honestly rather than reading (and implicitly claiming) a\n // daemon that belongs to some unknown project.\n if (project === undefined) {\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: 'skipped — project not initialized',\n });\n return;\n }\n const rec = readProjectDaemonRecord(project.id);\n if (!rec) {\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: 'not running (start with `noir daemon start`)',\n });\n return;\n }\n if (!pidAlive(rec.pid)) {\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: `stale record (pid ${rec.pid} not alive)`,\n });\n return;\n }\n try {\n // Bounded probe — a stale record pointing at a blackhole socket (TCP\n // accepted, HTTP never answered) must not hang `noir doctor` for minutes.\n const res = await fetch(`http://127.0.0.1:${rec.port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n const body =\n res.status === 200\n ? ((await res.json()) as { ok?: boolean; pid?: number; uptimeSec?: number })\n : null;\n // PID-reuse guard (mirrors daemonStatus): require the responding /health to\n // carry OUR recorded pid — a foreign process serving /health is not our daemon.\n const pidOk = typeof body?.pid === 'number' && body.pid === rec.pid;\n if (body && body.ok === true && pidOk) {\n checks.push({\n name: 'daemon',\n status: 'ok',\n detail: `running (pid ${rec.pid}, port ${rec.port}, up ${fmtUptime(body.uptimeSec ?? 0)})`,\n });\n return;\n }\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: `record present but /health unreachable on port ${rec.port}`,\n });\n } catch {\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: `record present but /health unreachable on port ${rec.port}`,\n });\n }\n}\n\nasync function checkNativeDeps(checks: CheckResult[]): Promise<{ vecOk: boolean }> {\n // vecAvailability probes better-sqlite3 + sqlite-vec together (the store opens\n // a Database and loads sqlite-vec). Dynamic import keeps the native bindings\n // out of cold-start for every other command.\n const { vecAvailability } = await import('@noir-ai/store');\n const vec = vecAvailability();\n const onnx = await probeOnnx();\n const vecOk = vec.ok === true;\n const onnxOk = onnx.ok === true;\n // The sqlite side is CRITICAL (the store cannot open without it). onnx only\n // backs local embeddings, so its absence is a warning (search → BM25-only).\n const status: Severity = vecOk ? (onnxOk ? 'ok' : 'warn') : 'fail';\n checks.push({\n name: 'native deps',\n status,\n detail: `better-sqlite3+sqlite-vec: ${vecOk ? 'loadable' : vec.ok === false ? vec.reason : 'unknown'} · onnxruntime-node: ${describeOnnx(onnx)}`,\n });\n return { vecOk };\n}\n\nasync function checkStore(\n checks: CheckResult[],\n project: ProjectInfo | undefined,\n root: string,\n): Promise<void> {\n if (!project) {\n checks.push({ name: 'store', status: 'warn', detail: 'skipped — not initialized' });\n return;\n }\n const dbPath = paths.storeDb(root, project.id);\n if (!existsSync(dbPath)) {\n // Fresh project: the store DB is created LAZILY when the daemon first runs\n // (or when content is indexed) — `noir init`/`create` does not pre-create it.\n // Its absence on an otherwise-initialized project is EXPECTED, not a failure:\n // surfacing it as `fail` would alarm every new user on their first `noir doctor`.\n // Warn (never exit-1) with the action that materializes the store.\n checks.push({\n name: 'store',\n status: 'warn',\n detail: `not created yet — the store DB is created when the daemon first runs (\\`noir daemon start\\` or index content); expected at ${dbPath}`,\n });\n return;\n }\n const { openStore } = await import('@noir-ai/store');\n let store: { close(): Promise<void> | void } | undefined;\n try {\n store = await openStore({ projectId: project.id, root, readonly: true });\n checks.push({\n name: 'store',\n status: 'ok',\n detail: `opens readonly (db: ${paths.storeDb(root, project.id)})`,\n });\n } catch (e) {\n checks.push({ name: 'store', status: 'fail', detail: `open failed: ${msg(e)}` });\n } finally {\n if (store) {\n try {\n await Promise.resolve(store.close());\n } catch {\n /* a close error must not mask the open result */\n }\n }\n }\n}\n\n/**\n * The tracked-file refusal line emitted by `loadNoirEnv` (spec 12.2). Matched by\n * its stable PREFIX: @noir-ai/core owns the wording, and this warning is the one\n * signal the loader returns that a `.noir/.env` was refused outright rather than\n * loaded (a refused file yields `overlay: {}` + `sources: {}` + this one line).\n */\nconst ENV_REFUSAL_PREFIX = '.noir/.env: refusing to load';\n\n/**\n * `.noir/.env` rows (names only, never values).\n *\n * Three concerns, in the order the user should read them:\n *\n * 1. REFUSAL (spec 12.2) — a git-tracked file contributes NOTHING. Reporting\n * only its permissions here would tell the user everything is fine while\n * every key in it is being ignored, which is precisely the \"I edited\n * `.noir/.env` and nothing changed\" confusion this check exists to kill.\n * The permissions advisory is skipped for a refused file, mirroring the\n * loader, which refuses before it even parses (so it emits no other\n * diagnostic either).\n * 2. PERMISSION advisory — a group/world-readable file can leak tokens\n * (ssh/aws-credentials convention). Warn, never fail.\n * 3. PROVENANCE — one `ok` row per key the file DEFINES, naming the side that\n * won. The file is the user's own short list, so this is not an\n * enumeration of the environment (see `noir env` for the curated view);\n * the value is never read, only the key name.\n */\nfunction checkNoirEnv(checks: CheckResult[], root: string, loaded: LoadedEnv): void {\n const path = join(paths.noirDir(root), '.env');\n let st: Stats;\n try {\n st = statSync(path);\n } catch {\n return; // no .env file — nothing to check\n }\n if (loaded.warnings.some((w) => w.startsWith(ENV_REFUSAL_PREFIX))) {\n checks.push({\n name: 'noir-env',\n status: 'warn',\n detail: `${path} is tracked by git — refused, so NONE of its keys are in effect. Fix: add \\`.noir/.env\\` to .gitignore and run \\`git rm --cached .noir/.env\\`.`,\n });\n return;\n }\n const mode = st.mode & 0o777;\n const tooOpen = (st.mode & 0o077) !== 0;\n checks.push(\n tooOpen\n ? {\n name: 'noir-env',\n status: 'warn',\n detail: `${path} is ${mode.toString(8)} — readable by others; run chmod 600`,\n }\n : { name: 'noir-env', status: 'ok', detail: `${path} permissions ok (${mode.toString(8)})` },\n );\n // Provenance rows. Named `noir-env:<KEY>` so they group under the permission\n // row in the table and stay uniquely addressable in the `--json` payload.\n for (const [key, source] of Object.entries(loaded.sources)) {\n if (source !== 'file') continue;\n checks.push({ name: `noir-env:${key}`, status: 'ok', detail: 'from .noir/.env' });\n }\n}\n\nfunction checkEmbedder(\n checks: CheckResult[],\n project: ProjectInfo | undefined,\n vecOk: boolean,\n): void {\n if (!project) {\n checks.push({ name: 'embedder', status: 'warn', detail: 'skipped — not initialized' });\n return;\n }\n const emb = project.config.context.embedder;\n const kind = emb.kind;\n const model = typeof emb.model === 'string' && emb.model.length > 0 ? emb.model : '<unset>';\n if (kind === 'none') {\n checks.push({\n name: 'embedder',\n status: 'ok',\n detail: `kind=none (BM25-only) · dim=${emb.dim}`,\n });\n return;\n }\n if (!vecOk) {\n // vec native layer is the embedder's hard prereq (vecs write into vec0).\n checks.push({\n name: 'embedder',\n status: 'warn',\n detail: `${kind}/${model} (${emb.dim}-dim) configured but vec native layer unavailable — search degrades to BM25`,\n });\n return;\n }\n checks.push({\n name: 'embedder',\n status: 'ok',\n detail: `${kind}/${model} (${emb.dim}-dim) · vec backend available`,\n });\n}\n\nfunction checkProvider(\n checks: CheckResult[],\n project: ProjectInfo | undefined,\n sources: LoadedEnv['sources'],\n): void {\n if (!project) {\n checks.push({ name: 'provider', status: 'warn', detail: 'skipped — not initialized' });\n return;\n }\n // PURE projection — resolveModelConfig reads config + env-var NAMES only; it\n // NEVER makes a live call and NEVER infers a provider from env-var presence.\n const resolved = resolveModelConfig(project.config.model);\n const names = Object.keys(resolved.providers);\n if (names.length === 0) {\n checks.push({\n name: 'provider',\n status: 'ok',\n detail: 'none configured (offline mode; drafting degrades to templates)',\n });\n return;\n }\n const parts: string[] = [];\n let missing = false;\n for (const name of names) {\n const p = resolved.providers[name];\n if (!p) continue;\n // Provenance (spec 12.3): when the winning source for the key is\n // `.noir/.env`, say so — \"key present\" alone leaves the user staring at an\n // empty shell export wondering where the key came from. The NAME only; the\n // value is never read here, let alone printed.\n const fromFile = p.apiKeyEnv !== undefined && sources[p.apiKeyEnv] === 'file';\n const key = p.hasKey\n ? fromFile\n ? 'key present (from .noir/.env)'\n : 'key present'\n : p.apiKeyEnv\n ? `missing ${p.apiKeyEnv}`\n : 'anonymous';\n if (!p.hasKey && p.apiKeyEnv) missing = true;\n parts.push(`${name}/${p.model ?? '?'} (${key})`);\n }\n // Provider readiness is never CRITICAL — the model layer degrades to `null`\n // (templates) when a key is missing, so this is an ok/warn signal only.\n checks.push({\n name: 'provider',\n status: missing ? 'warn' : 'ok',\n detail: parts.join(' · '),\n });\n}\n\n// ---------------------------------------------------------------------------\n// C1 — doctor install row (advisory; ok/warn only, never fail, cache-only).\n// ---------------------------------------------------------------------------\n\nexport interface InstallCheckOutcome {\n name: 'install';\n status: 'ok' | 'warn';\n detail: string;\n method: InstallMethod;\n installedVersion: string | null;\n latestKnown: string | null;\n}\n\n/** Pure: build the doctor `install` row. NEVER `fail` — an install method issue\n * is advisory, not a broken host. Reads the cache only; no network. */\nexport function buildInstallCheck(opts: {\n method: InstallMethod;\n version: string | null;\n latestKnown: string | null;\n}): InstallCheckOutcome {\n const { method, version, latestKnown } = opts;\n const parts: string[] = [`method=${method}`];\n if (version) parts.push(`v${version}`);\n const advisory: string[] = [];\n if (method !== 'native') advisory.push('native recommended');\n if (latestKnown && version && latestKnown !== version) advisory.push('update available');\n const status: 'ok' | 'warn' = advisory.length > 0 ? 'warn' : 'ok';\n if (advisory.length > 0) parts.push(advisory.join(' + '));\n return {\n name: 'install',\n status,\n detail: parts.join(' · '),\n method,\n installedVersion: version,\n latestKnown,\n };\n}\n\nfunction checkScaffoldVersion(\n checks: CheckResult[],\n root: string,\n): {\n onDisk: string | null;\n current: string;\n drift: boolean;\n} {\n // readScaffoldVersion never throws — it returns null on an absent/malformed\n // stamp, so doctor keeps reporting even on a partially-initialized project.\n const onDisk = readScaffoldVersion(root);\n const current = CURRENT_SCAFFOLD_VERSION;\n const drift = onDisk !== null && onDisk !== current;\n // Drift is NEVER critical: a stale scaffold still works (the manifest entries\n // are forward-compatible), it just means `noir init --upgrade` will run\n // pending migrations. Absent stamp on an uninitialized project → warn (parity\n // with the other project-dependent checks that skip-with-warn pre-init).\n const status: Severity = onDisk === null || drift ? 'warn' : 'ok';\n const detail =\n onDisk === null\n ? 'no .noir/scaffold-version stamp (run `noir init`)'\n : drift\n ? `on-disk ${onDisk} ≠ current ${current} (run \\`noir init --upgrade\\`)`\n : `${onDisk} (up to date)`;\n checks.push({ name: 'scaffold version', status, detail });\n return { onDisk, current, drift };\n}\n\n/**\n * Soft RULES.md budget cap. Mirrors the Failure-Backed Rules doctrine:\n * an over-budget RULES.md tends to accumulate stale / speculative clauses, so\n * doctor warns (NEVER fails — purely a hygiene nudge) when the file exceeds\n * the configured `lengthBudgetKb` (default 6 KB) OR a 150-line ceiling. The\n * check is honest about the three \"nothing to measure\" cases:\n *\n * • project not initialized → skip-warn (parity with store/embedder/provider)\n * • RULES.md absent → ok informational (no file = no budget problem)\n * • RULES.md unreadable → warn (treat like a malformed-stamp: surface it)\n *\n * Returns the structured measurement for the `--json` envelope's `data.rules`,\n * or `null` when there is nothing to measure (absent / skipped).\n */\nfunction checkRulesMdBudget(\n checks: CheckResult[],\n root: string,\n project: ProjectInfo | undefined,\n): {\n onDisk: { bytes: number; lines: number };\n budget: { kb: number; maxLines: number };\n over: boolean;\n} | null {\n if (!project) {\n checks.push({ name: 'rules budget', status: 'warn', detail: 'skipped — not initialized' });\n return null;\n }\n const rulesPath = paths.rulesMd(root);\n if (!existsSync(rulesPath)) {\n // No RULES.md is fine — most projects don't have one yet. Informational ok\n // so a green run stays green without forcing a file creation.\n checks.push({\n name: 'rules budget',\n status: 'ok',\n detail: 'no .noir/rules/RULES.md',\n });\n return null;\n }\n // Read + measure. A read/decode failure surfaces as a warn (parallel to the\n // malformed-stamp handling in checkScaffoldVersion): the doctor keeps\n // reporting rather than crashing, and the user sees a clear hint.\n let content: string;\n try {\n content = readFileSync(rulesPath, 'utf8');\n } catch (e) {\n checks.push({\n name: 'rules budget',\n status: 'warn',\n detail: `RULES.md unreadable: ${msg(e)}`,\n });\n return null;\n }\n // bytes = UTF-8 file size on disk (the natural unit for a KB budget). lines\n // uses wc-style counting: count of '\\n' chars, +1 if the last line has no\n // trailing newline; 0 only for an empty file.\n const bytes = Buffer.byteLength(content, 'utf8');\n const lineCount =\n content.length === 0 ? 0 : content.split('\\n').length - (content.endsWith('\\n') ? 1 : 0);\n const lengthBudgetKb = project.config.rules.lengthBudgetKb;\n const budgetBytes = lengthBudgetKb * 1024;\n const maxLines = 150;\n const overBytes = bytes > budgetBytes;\n const overLines = lineCount > maxLines;\n const over = overBytes || overLines;\n const status: Severity = over ? 'warn' : 'ok';\n const kb = (n: number): string => (n / 1024).toFixed(1);\n const detail = over\n ? `OVER: ${kb(bytes)} KB / ${lengthBudgetKb} KB · ${lineCount}/${maxLines} lines — trim RULES.md (failure-backed clauses only) or raise rules.lengthBudgetKb`\n : `${kb(bytes)} KB / ${lengthBudgetKb} KB · ${lineCount}/${maxLines} lines (within budget)`;\n checks.push({ name: 'rules budget', status, detail });\n return {\n onDisk: { bytes, lines: lineCount },\n budget: { kb: lengthBudgetKb, maxLines },\n over,\n };\n}\n\nfunction summarize(checks: CheckResult[]): DoctorPayload['summary'] {\n let ok = 0;\n let warnN = 0;\n let fail = 0;\n for (const c of checks) {\n if (c.status === 'ok') ok++;\n else if (c.status === 'warn') warnN++;\n else fail++;\n }\n return { ok, warn: warnN, fail };\n}\n\n/**\n * S10 — host-artifacts presence check. Reports the ACTIVE host (read from\n * `project.config.host`) + verifies the host's primary emission paths exist on\n * disk. The expected set mirrors `buildHostArtifacts` in @noir-ai/create:\n *\n * - AGENTS.md for the hosts that emit it (agents-md/cursor/opencode — their\n * native context surface IS AGENTS.md). claude/gemini do NOT emit AGENTS.md\n * (their CLAUDE.md/GEMINI.md @-import the canonical .noir/ sources; a root\n * AGENTS.md would double-import them — fix-wave I1 removed that delta).\n * - The host's native context file when distinct from AGENTS.md\n * (claude → CLAUDE.md; gemini → GEMINI.md; agents-md/cursor/opencode →\n * none, AGENTS.md IS the context). Cursor's working-rules ride AGENTS.md's\n * `@.noir/rules/RULES.md` import — NO separate `.cursor/rules/noir-contract.mdc`\n * host-rules pointer (it was `noir-`-prefixed and the C3 cursor flat-skill\n * prune deleted it on every sync).\n * - The host's MCP config (.mcp.json / .gemini/mcp.json / .cursor/mcp.json /\n * opencode.json).\n *\n * Severity is `ok` when every expected path exists, `warn` when any are\n * missing (NEVER `fail` — a missing host artifact is restored by `noir sync`,\n * not a critical product failure). Mirrors the policy of the other\n * presence/projection checks (scaffold-version drift, RULES.md budget): warn,\n * surface the remediation, never block.\n *\n * Returns the structured payload for the `--json` envelope's `data.host`, or\n * `null` when the project isn't initialized (no resolved host).\n */\nfunction checkHostArtifacts(\n checks: CheckResult[],\n root: string,\n project: ProjectInfo | undefined,\n): { active: HostId; expected: string[]; missing: string[] } | null {\n if (!project) {\n checks.push({ name: 'host artifacts', status: 'warn', detail: 'skipped — not initialized' });\n return null;\n }\n const host = project.config.host;\n const adapter = resolveAdapter(host);\n const ectx = { root };\n\n // Build the expected repo-relative path list. Mirrors buildHostArtifacts in\n // @noir-ai/create so a missing row maps 1:1 to a manifest entry the user can\n // restore with `noir sync`. Kept inline (not imported) so doctor stays a\n // READ-ONLY health probe with zero write-side coupling.\n const expected: string[] = [];\n // AGENTS.md — only for hosts whose emitContext IS the AGENTS.md (I1: claude/\n // gemini skip it — their native context file already covers .noir/).\n const emitsAgentsMd = host === 'agents-md' || host === 'cursor' || host === 'opencode';\n if (emitsAgentsMd) {\n expected.push(relOr(adapter.agentsMdPath?.(ectx) ?? 'AGENTS.md', root));\n }\n // Host-native context file (when distinct from AGENTS.md).\n if (host === 'claude') expected.push('CLAUDE.md');\n else if (host === 'gemini') expected.push('GEMINI.md');\n // Cursor has NO separate host-rules .mdc — the prior `noir-contract.mdc`\n // pointer was REMOVED (it was `noir-`-prefixed, so the C3 cursor flat-skill\n // prune in emitSkillsToDir deleted it on every noir init/create/sync).\n // Cursor's rules ride AGENTS.md's `@.noir/rules/RULES.md` import instead.\n // Host MCP config (fallback .mcp.json for claude/agents-md).\n expected.push(relOr(adapter.mcpConfigPath?.(ectx) ?? '.mcp.json', root));\n\n const missing = expected.filter((p) => !existsSync(joinRoot(root, p)));\n const status: Severity = missing.length === 0 ? 'ok' : 'warn';\n const detail =\n missing.length === 0\n ? `host=${host} · all ${expected.length} primary artifacts present`\n : `host=${host} · missing ${missing.join(', ')} (run \\`noir sync\\` to restore)`;\n checks.push({ name: 'host artifacts', status, detail });\n return { active: host, expected, missing };\n}\n\n// ---------------------------------------------------------------------------\n// SP-A — nested-`.noir` detection (read-only).\n// ---------------------------------------------------------------------------\n\n/**\n * Detects the fingerprint of a `noir init`/`create` run from INSIDE `.noir/`\n * (now PREVENTED by `assertSafeRoot` in @noir-ai/create, but legacy/already-\n * nested projects still carry the damage): a nested `<root>/.noir/.noir/` store\n * and/or host artifacts emitted into `.noir/` as if it were a project root\n * (`.noir/CLAUDE.md`, `.noir/.mcp.json`, `.noir/.claude/`, and the ignore files\n * `.noir/.gitignore` / `.dockerignore` / `.npmignore` / `.prettierignore`).\n * Read-only `warn` —\n * doctor never mutates; remediation is manual removal (a future follow-up\n * slice can automate it). Never `fail`: a nested store wastes space + confuses\n * tooling but does not break the outer project.\n */\nexport function checkNestedNoir(\n checks: CheckResult[],\n root: string,\n): { detected: boolean; paths: string[] } {\n const candidates = [\n '.noir/.noir',\n '.noir/CLAUDE.md',\n '.noir/.mcp.json',\n '.noir/.claude',\n '.noir/.gitignore',\n '.noir/.dockerignore',\n '.noir/.npmignore',\n '.noir/.prettierignore',\n ];\n const found = candidates.filter((rel) => existsSync(join(root, rel)));\n const detected = found.length > 0;\n checks.push({\n name: 'nested .noir',\n status: detected ? 'warn' : 'ok',\n detail: detected\n ? `nested Noir artifacts inside .noir/ (${found.join(', ')}) — likely from running \\`noir init\\` inside .noir/. Remove them; the real project is at ${root}.`\n : 'no nested .noir/ artifacts',\n });\n return { detected, paths: found };\n}\n\n// ---------------------------------------------------------------------------\n// SP-C deferred — semantic duplicate detection (`--dedup`; loads the embedder).\n// ---------------------------------------------------------------------------\n\n/** Local embedder shape (@noir-ai/context's `EmbedFn`). */\ntype EmbedLike = (text: string) => Promise<Float32Array>;\n\n/**\n * Collect dedup candidates: the host context files that exist at the root\n * (CLAUDE.md / AGENTS.md / GEMINI.md — the hand-mirrored-overlap case) plus\n * `.noir/rules/RULES.md`. `.noir/NOIR.md` is EXCLUDED because host context\n * files `@import` it by design (their similarity is intentional, not a dup).\n */\nexport function collectDedupCandidates(root: string): { path: string; text: string }[] {\n const rels = ['CLAUDE.md', 'AGENTS.md', 'GEMINI.md', '.noir/rules/RULES.md'];\n const out: { path: string; text: string }[] = [];\n for (const rel of rels) {\n const abs = join(root, rel);\n if (!existsSync(abs)) continue;\n let text: string;\n try {\n text = readFileSync(abs, 'utf8');\n } catch {\n continue;\n }\n if (text.trim().length === 0) continue;\n out.push({ path: rel, text });\n }\n return out;\n}\n\n/**\n * `noir doctor --dedup`: embed the host-context + RULES.md candidates and\n * report near-duplicate pairs (cosine ≥ 0.90). The ONLY mechanism that catches\n * cross-file SEMANTIC overlap (e.g. a hand-mirrored CLAUDE.md ≈ AGENTS.md) —\n * exact content-hash cannot. Opt-in so a default `noir doctor` never pays the\n * embedder-load cost; degrades to a `warn` skip when the embedder is\n * unavailable (kind=none / native layer missing). `opts.embed` is a testability\n * seam — tests inject a deterministic fake; production omits it and the local\n * embedder is lazy-loaded via `@noir-ai/context`.\n */\nexport async function checkSemanticDupDoctor(\n checks: CheckResult[],\n root: string,\n project: ProjectInfo | undefined,\n opts: { embed?: EmbedLike } = {},\n): Promise<void> {\n if (!project) {\n checks.push({ name: 'semantic dup', status: 'warn', detail: 'skipped — not initialized' });\n return;\n }\n const candidates = collectDedupCandidates(root);\n if (candidates.length < 2) {\n checks.push({\n name: 'semantic dup',\n status: 'ok',\n detail: `${candidates.length} candidate file(s); nothing to compare`,\n });\n return;\n }\n const ctx = await import('@noir-ai/context');\n let embed: EmbedLike | undefined = opts.embed;\n if (!embed) {\n const cfg = ctx.resolveEmbedderConfig(project.config.context);\n if (cfg.kind === 'none') {\n checks.push({\n name: 'semantic dup',\n status: 'warn',\n detail: 'embedder kind=none — set context.embedder.kind=local to enable semantic dedup',\n });\n return;\n }\n try {\n embed = ctx.createEmbedFn(cfg).embed as EmbedLike;\n } catch (e) {\n checks.push({\n name: 'semantic dup',\n status: 'warn',\n detail: `embedder unavailable — skipped (${msg(e)})`,\n });\n return;\n }\n }\n if (!embed) return;\n const pairs = await ctx.findSemanticDuplicates(candidates, embed, ctx.DEFAULT_DUP_THRESHOLD);\n checks.push({\n name: 'semantic dup',\n status: pairs.length > 0 ? 'warn' : 'ok',\n detail:\n pairs.length === 0\n ? `no near-duplicates across ${candidates.length} file(s)`\n : `${pairs.length} near-duplicate pair${pairs.length === 1 ? '' : 's'}: ${pairs\n .map((p) => `${p.a}≈${p.b} (${p.similarity.toFixed(2)})`)\n .join('; ')}`,\n });\n}\n\n// ---------------------------------------------------------------------------\n// S11 — publish-readiness check (advisory, repo-developer-facing).\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the workspace `packages/` directory from the CLI's own location —\n * walk up from `import.meta.url` to the nearest `package.json` named\n * `@noir-ai/cli`, then return its PARENT dir (the `packages/` root) IF that\n * dir's parent carries the monorepo marker (`pnpm-workspace.yaml`). Returns\n * `null` when not running from a monorepo checkout (a global `npm install -g`\n * has no `packages/*` workspace to validate → the publish check skips).\n *\n * The walk is robust across layouts: source (`packages/cli/src/commands/`),\n * built (`packages/cli/dist/`), and bundled-chunk (`packages/cli/dist/`) all\n * walk up to `packages/cli/package.json`. In a global install the same walk\n * lands on `node_modules/@noir-ai/cli/package.json`, whose parent\n * (`node_modules/@noir-ai/`) has NO `pnpm-workspace.yaml` parent → null.\n */\nfunction resolveWorkspacePackagesDir(): string | null {\n let here = dirname(fileURLToPath(import.meta.url));\n // Bound the walk so a pathological layout never loops the filesystem.\n for (let i = 0; i < 8; i++) {\n const pkgPath = join(here, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { name?: string };\n if (pkg.name === '@noir-ai/cli') {\n // `here` is the cli package dir; `dirname(here)` is `packages/`.\n const packagesDir = dirname(here);\n // Monorepo gate: the repo root (`dirname(packagesDir)`) carries\n // `pnpm-workspace.yaml`. A global install fails this gate.\n if (existsSync(join(dirname(packagesDir), 'pnpm-workspace.yaml'))) {\n return packagesDir;\n }\n return null;\n }\n } catch {\n // Malformed package.json — keep walking.\n }\n }\n const parent = dirname(here);\n if (parent === here) break; // filesystem root\n here = parent;\n }\n return null;\n}\n\n/**\n * Permissive semver-ish regex for the advisory check. Accepts release and\n * pre-release/build forms (`1.0.0`, `1.1.0-beta.1`, `1.2.0+build.4`). Strict\n * enough to catch the obvious foot-guns (a git SHA, a literal \"latest\", a\n * missing patch) without pulling in the `semver` package for a warn-level\n * check. `npm publish` itself enforces rigor at pack time.\n */\nconst SEMVER_ISH = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/;\n\n/** Best-effort `npm pack --dry-run` on the cli package — read-only (it does\n * NOT write the tarball). Returns `{ fileCount, distIncluded }` parsed from\n * npm's notice output, or `{ error }` when npm is unavailable / the pack\n * fails. Gated on npm being on PATH (the spec's expensive-but-optional leg):\n * we probe `npm --version` first and skip-with-note if it isn't resolvable. */\nfunction npmPackDryRun(\n cliPkgDir: string,\n): { fileCount: number | null; distIncluded: boolean | null } | { error: string } {\n // Probe npm availability first (cheap). shell:true so Windows finds npm.cmd.\n const probe = spawnSync('npm', ['--version'], { encoding: 'utf8', shell: true });\n if (probe.status !== 0 || probe.error) {\n return { error: probe.error ? probe.error.message : 'npm not on PATH' };\n }\n // `npm pack --dry-run` lists the tarball contents without writing the file.\n // It IS read-only (npm's own docs: \"--dry-run ... does not write anything\").\n const res = spawnSync('npm', ['pack', '--dry-run'], {\n cwd: cliPkgDir,\n encoding: 'utf8',\n shell: true,\n });\n if (res.status !== 0 || res.error) {\n return { error: res.error ? res.error.message : `npm pack exited ${res.status ?? '?'}` };\n }\n const out = `${res.stdout ?? ''}\\n${res.stderr ?? ''}`;\n // `npm notice total files: 42` — the canonical summary line.\n const totalMatch = out.match(/total files:\\s*(\\d+)/i);\n const fileCount = totalMatch?.[1] ? Number.parseInt(totalMatch[1], 10) : null;\n // `dist/` appears in the tarball contents (`npm notice <size> dist/<file>`).\n const distIncluded = /\\bdist\\//.test(out);\n return { fileCount, distIncluded };\n}\n\n/**\n * S11 publish-readiness check. For every workspace `package.json` under\n * `packagesDir`, validate publishability:\n * - `name` starts with `@noir-ai/`\n * - `version` matches {@link SEMVER_ISH}\n * - `files` is a non-empty array\n * - the cli package has a `bin` field\n *\n * Optionally runs `npm pack --dry-run` on the cli package (best-effort, gated\n * on npm being available) to report the file count + confirm `dist/` ships.\n *\n * Severity is ALWAYS `ok` or `warn` — NEVER `fail`: a missing/odd package.json\n * field does not break the local install (this is an advisory nudge to repo\n * developers, not a project-facing health check). The check runs ALWAYS (it\n * produces a row in every doctor report) but returns `null` from\n * `data.publish` when not running from a monorepo (`packagesDir === null`).\n */\nexport function checkPublish(\n checks: CheckResult[],\n packagesDir: string | null,\n opts: { skipNpmPack?: boolean } = {},\n): { checked: number; issues: string[] } | null {\n if (packagesDir === null) {\n checks.push({\n name: 'publish',\n status: 'ok',\n detail: 'skipped — not a monorepo checkout (no packages/* workspace)',\n });\n return null;\n }\n // Discover workspace package.json files (depth-1 dirs only — no glob dep).\n let entries: string[] = [];\n try {\n entries = readdirSync(packagesDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch (e) {\n checks.push({\n name: 'publish',\n status: 'warn',\n detail: `could not read ${packagesDir}: ${msg(e)}`,\n });\n return { checked: 0, issues: [`unreadable packages dir: ${msg(e)}`] };\n }\n\n const issues: string[] = [];\n let checked = 0;\n let cliDir: string | null = null;\n for (const name of entries) {\n const pkgPath = join(packagesDir, name, 'package.json');\n if (!existsSync(pkgPath)) continue;\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as Record<string, unknown>;\n } catch (e) {\n issues.push(`${name}/package.json: unparseable (${msg(e)})`);\n checked++;\n continue;\n }\n checked++;\n const pkgName = typeof pkg.name === 'string' ? pkg.name : '';\n if (!pkgName.startsWith('@noir-ai/')) {\n issues.push(`${name}/package.json: name '${pkgName}' is not @noir-ai/*`);\n }\n const version = typeof pkg.version === 'string' ? pkg.version : '';\n if (!SEMVER_ISH.test(version)) {\n issues.push(`${name}/package.json: version '${version}' is not semver`);\n }\n if (!Array.isArray(pkg.files) || pkg.files.length === 0) {\n issues.push(`${name}/package.json: files missing or empty`);\n }\n if (pkgName === '@noir-ai/cli') {\n if (pkg.bin == null || (typeof pkg.bin === 'object' && Object.keys(pkg.bin).length === 0)) {\n issues.push(`${name}/package.json: bin missing (cli package must declare a bin)`);\n }\n cliDir = join(packagesDir, name);\n }\n }\n\n // Best-effort npm pack --dry-run on the cli package. Gated on npm being\n // available; a failure here is surfaced in the detail but does NOT add an\n // issue (it is environmental, not a package.json defect). `skipNpmPack` is a\n // testability seam — synthetic test fixtures have no real `dist/`, so the\n // dist-absent signal would otherwise pollute the package.json-validation\n // assertions. Production (`doctor()`) leaves it unset.\n let packNote = '';\n if (cliDir && opts.skipNpmPack !== true) {\n const pack = npmPackDryRun(cliDir);\n if ('error' in pack) {\n packNote = ` · npm pack skipped (${pack.error})`;\n } else {\n const count = pack.fileCount == null ? '?' : String(pack.fileCount);\n const dist = pack.distIncluded ? 'dist/ included' : 'dist/ NOT in tarball';\n packNote = ` · npm pack: ${count} files, ${dist}`;\n if (pack.distIncluded === false) {\n issues.push('cli npm pack --dry-run: dist/ absent from the tarball');\n }\n }\n }\n\n const status: Severity = issues.length > 0 ? 'warn' : 'ok';\n const detail =\n issues.length === 0\n ? `${checked}/${entries.length} packages publish-ready${packNote}`\n : `${issues.length} issue${issues.length === 1 ? '' : 's'} across ${checked} package${checked === 1 ? '' : 's'}${packNote}`;\n checks.push({ name: 'publish', status, detail });\n return { checked, issues };\n}\n\n/** Repo-relative POSIX form of `abs` under `root`. Defensive (N2): rejects\n * paths that escape `root` — mirrors `hostRel` in @noir-ai/create/manifest.ts.\n * A future adapter that returns a stray path fails loudly here instead of\n * producing a misleading \"missing\" row for a path doctor would never probe.\n * Relative literals (e.g. `'AGENTS.md'`, `'.mcp.json'`) pass through unchanged\n * AFTER the `..`-escape check so a buggy literal could not exfiltrate either. */\nfunction relOr(abs: string, root: string): string {\n const posix = abs.replace(/\\\\/g, '/');\n if (posix.startsWith('/')) {\n const rel = relative(root, abs).replace(/\\\\/g, '/');\n if (rel.length === 0 || rel.startsWith('..') || rel.startsWith('/')) {\n throw new Error(`doctor: host path '${abs}' is not under root '${root}'`);\n }\n return rel;\n }\n // Already-relative literal: still reject `..` segments (a `../etc/passwd`\n // literal from a buggy adapter must not silently pass through).\n if (posix.split('/').some((seg) => seg === '..')) {\n throw new Error(`doctor: host path '${abs}' escapes root '${root}'`);\n }\n return posix;\n}\n\n/** Join `root` with a repo-relative POSIX path for an existsSync probe. */\nfunction joinRoot(root: string, relPath: string): string {\n return `${root}/${relPath}`;\n}\n\nfunction severityBadge(s: Severity): string {\n // Doctor's vocabulary is ok/warn/fail; the badge palette is ok/warn/error/info.\n // `fail` is the CRITICAL state → red (`error`), so RED stays reserved for the\n // broken-host states. The TEXT label keeps doctor's own uppercase word so the\n // JSON envelope (`status: 'fail'`) and the human row agree on vocabulary.\n const state: BadgeState = s === 'fail' ? 'error' : s;\n return badge(state, s.toUpperCase());\n}\n\nfunction renderHuman(payload: DoctorPayload, opts: CliOptions): void {\n log(\n `noir doctor — ${payload.checks.length} check${payload.checks.length === 1 ? '' : 's'}`,\n opts,\n );\n table(\n payload.checks.map((c) => ({\n Check: c.name,\n Status: severityBadge(c.status),\n Detail: c.detail,\n })),\n ['Check', 'Status', 'Detail'],\n opts,\n );\n const { ok, warn: warnN, fail } = payload.summary;\n if (fail > 0) {\n err(\n `${fail} critical check${fail === 1 ? '' : 's'} failed (${warnN} warning${warnN === 1 ? '' : 's'}, ${ok} ok)`,\n opts,\n );\n } else if (warnN > 0) {\n warn(`all critical checks passed (${warnN} warning${warnN === 1 ? '' : 's'}, ${ok} ok)`, opts);\n } else {\n success(`all ${ok} check${ok === 1 ? '' : 's'} passed`, opts);\n }\n}\n\n/**\n * `noir doctor`: run all checks, render, and exit.\n *\n * Under `--json` writes `{ok:true, data: DoctorPayload}` to stdout (always —\n * the command produced a diagnosis). The exit code carries health: 0 when no\n * check failed, 1 when any CRITICAL (`fail`) check is unhealthy.\n */\nexport async function doctor(opts: DoctorOptions = {}): Promise<void> {\n const root = process.cwd();\n const checks: CheckResult[] = [];\n\n await checkRuntime(checks);\n const { project, result: configResult } = checkConfig(root);\n checks.push(configResult);\n await checkDaemon(checks, project);\n const { vecOk } = await checkNativeDeps(checks);\n await checkStore(checks, project, root);\n checkEmbedder(checks, project, vecOk);\n // `.noir/.env` is read ONCE and shared by the provider + noir-env rows — both\n // need \"which side won for this key\". Names only: the loader never opens a\n // value into anything doctor prints.\n const noirEnv = loadNoirEnv(root);\n checkProvider(checks, project, noirEnv.sources);\n checkNoirEnv(checks, root, noirEnv);\n const scaffold = checkScaffoldVersion(checks, root);\n const rules = checkRulesMdBudget(checks, root, project);\n const host = checkHostArtifacts(checks, root, project);\n checkNestedNoir(checks, root);\n if (opts.dedup === true) {\n await checkSemanticDupDoctor(checks, root, project);\n }\n const publish = checkPublish(checks, resolveWorkspacePackagesDir());\n\n // C1 — install-method check (advisory, cache-only, never fail).\n const installCheck = buildInstallCheck({\n method: detectActiveMethod(),\n version: readInstallRecord()?.version ?? null,\n latestKnown: latestVersionFromCache(readUpdateCache(), 'latest'),\n });\n checks.push(installCheck);\n\n const summary = summarize(checks);\n const payload: DoctorPayload = {\n noir: NOIR_VERSION,\n checks,\n summary,\n scaffold,\n rules,\n host,\n publish,\n install: {\n method: installCheck.method,\n installedVersion: installCheck.installedVersion,\n latestKnown: installCheck.latestKnown,\n },\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: payload })}\\n`);\n } else {\n renderHuman(payload, opts);\n }\n\n if (summary.fail > 0) {\n // Under --json the data envelope is already on stdout; throw with an empty\n // message so handleError sets exitCode=1 without a redundant stderr line.\n // Under human mode the summary line above already named the failures.\n throw new NoirCliError(\n EXIT.ERROR,\n opts.json === true ? '' : `noir doctor: ${summary.fail} critical check(s) failed`,\n );\n }\n}\n","// `noir env [--json]` — which configuration is in effect, and where each value\n// comes from.\n//\n// The command exists to answer the one question the maintainer hit live: \"I\n// edited `.noir/.env` and nothing changed — so which value is actually in\n// effect, and who supplied it?\". The resolution rule itself (spec 12.1) is that\n// a key `.noir/.env` DEFINES wins and the real environment is the fallback for\n// the keys the file omits; this command renders that decision per key instead of\n// leaving the user to infer it.\n//\n// HARD RULE (spec 12.4): NAMES AND SHAPES ONLY, NEVER A VALUE. Each row carries\n// the key, the side that won, and a REDACTED shape (`pk_…(42)` = the first three\n// characters + the length) so two tokens can be told apart without a secret ever\n// being printed. `--json` carries even less — `valueLength` only; the shape\n// belongs to the human table and never reaches stdout (see `env`, whose payload\n// is an EXPLICIT projection of the resolved rows).\n//\n// CURATION, NOT A DUMP (spec 12.3). `loadNoirEnv().sources` records every key\n// that resolves to something, including every ambient one as `'env'` — under the\n// default argument that is the whole of `process.env` (PATH, npm_config_*, …).\n// Rendering that inventory would bury the answer this command exists to give, so\n// the report is exactly:\n//\n// (a) every key `.noir/.env` defines — the user's own short list, and the\n// thing they just edited; plus\n// (b) ambient keys named by {@link CURATED_AMBIENT_KEYS} — the Noir-relevant\n// names a user may have exported from a shell rc; plus\n// (c) ambient keys named by the project's OWN config\n// (`model.providers.<name>.apiKeyEnv`), read through the shipped\n// `resolveModelConfig` mapper so the CLI never re-derives the schema.\n//\n// READ-ONLY: nothing here writes, spawns, or opens the store.\n\nimport { loadNoirEnv, loadProjectInfo } from '@noir-ai/core';\nimport { resolveModelConfig } from '@noir-ai/model';\nimport { type CliOptions, info, json, table } from '../output.js';\n\n/**\n * Ambient (environment-only) names worth reporting: the Noir-relevant keys a\n * user might reasonably have exported from a shell rc. Deliberately a SHORT\n * allowlist — see the curation note above. `CLICKUP_API_TOKEN` is the\n * integration token, the four `*_API_KEY` names are the remote embedder /\n * provider keys the docs name, `OLLAMA_BASE_URL` backs a local Ollama embedder,\n * and `NOIR_PROFILE` selects a `run.profiles` entry.\n *\n * An ambient key NOT in this list is still resolved (and still feeds the\n * precedence decision) — it is simply not reported, because \"every variable in\n * your shell\" is not an answer to \"which value is in effect\".\n */\nexport const CURATED_AMBIENT_KEYS: readonly string[] = [\n 'CLICKUP_API_TOKEN',\n 'OPENAI_API_KEY',\n 'VOYAGE_API_KEY',\n 'COHERE_API_KEY',\n 'OLLAMA_BASE_URL',\n 'ANTHROPIC_API_KEY',\n 'NOIR_PROFILE',\n];\n\n/**\n * The ambient environment as it was BEFORE `.noir/.env` was applied, captured by\n * the bin's `preAction` (see {@link captureAmbientEnv}).\n *\n * The loader APPLIES its overlay to `process.env` in place, and after that the\n * file's own values are indistinguishable from an inherited export for exactly\n * the keys where the question matters — so \"is my shell value being overridden?\"\n * (the `shadowed` column) can only be answered from a pre-overlay snapshot.\n *\n * A SHALLOW COPY of the environment, values included: the shadow comparison\n * needs the ambient VALUE to tell an override apart from an agreeing export.\n * The copy is in-memory and process-local for the lifetime of one invocation —\n * it is never persisted, never written back to `process.env`, and a value from\n * it reaches the user only as a redacted shape or a length (see\n * {@link redactShape} / {@link EnvVarRow.valueLength}).\n */\nlet ambientBaseline: Record<string, string | undefined> | undefined;\n\n/**\n * Snapshot the ambient environment before the bin applies `.noir/.env`. Called\n * from `bin.ts`'s `preAction` for `noir env` only — every other command just\n * applies the overlay and never needs the baseline.\n */\nexport function captureAmbientEnv(env: Record<string, string | undefined> = process.env): void {\n ambientBaseline = { ...env };\n}\n\n/** One resolved key: the name, the side that won, and a length — never a value. */\nexport interface EnvVarRow {\n /** The env-var NAME (safe to print; this is what the user types). */\n key: string;\n /** The side that won: `.noir/.env` (`'file'`) or the ambient environment. */\n source: 'file' | 'env';\n /** Present (true) only when `.noir/.env` defines the key AND the ambient\n * environment defined it with a DIFFERENT value — i.e. the file is\n * overriding something. Omitted otherwise (the JSON shape is additive). */\n shadowed?: boolean;\n /** Character count of the value in effect. A length is not a value. */\n valueLength: number;\n}\n\n/** The `data` of `noir env --json`. No value, and no fragment of one. */\nexport interface EnvPayload {\n vars: EnvVarRow[];\n /** Loader diagnostics (malformed lines, shadowed keys, a tracked-file\n * refusal). Names only — the loader never puts a value in a warning. */\n warnings: string[];\n}\n\n/** Resolved rows + the REDACTED display shapes for the human table. */\ninterface ResolvedEnv {\n vars: EnvVarRow[];\n warnings: string[];\n /** key → redacted shape (first three characters + length). Human-only: it is\n * never serialized (see the explicit projection in `env`). */\n shapes: Record<string, string>;\n}\n\n/**\n * A redacted DISPLAY shape for a value: the first three characters plus the\n * length (`pk_la…(42)`). Enough to tell two tokens apart in a table — the shape\n * a provider dashboard shows for a masked key — and never enough to use one.\n * The length is deliberate: it is what makes \"the file's value is in effect, not\n * the one I exported\" visible without printing either.\n *\n * SHORT VALUES ARE SHOWN AS LENGTH ONLY. For a value of `SHORT_VALUE_MAX` chars\n * or fewer the prefix would BE the whole value (`dev…(3)` for `NOIR_PROFILE=dev`\n * prints a complete value — and a short one is exactly the kind a user might\n * have picked, or a legacy token might be). The floor is 8 rather than the 3\n * the prefix needs, so a short-but-real value like a hostname or a base URL\n * cannot be reconstructed by guessing the last few characters.\n */\nconst SHORT_VALUE_MAX = 8;\n\nfunction redactShape(value: string): string {\n if (value.length <= SHORT_VALUE_MAX) return `…(${value.length})`;\n return `${value.slice(0, 3)}…(${value.length})`;\n}\n\n/**\n * The env-var NAMES the project's own model config requests\n * (`model.providers.<name>.apiKeyEnv`), read through the shipped\n * `resolveModelConfig` mapper rather than by reaching into the config shape —\n * so the CLI never re-derives the schema, and a config that names a key the\n * built-in allowlist would never guess still shows up.\n *\n * An uninitialized (or unparseable) project yields `[]`: `noir env` reports what\n * it can from the file + the environment instead of refusing to run — the file\n * is exactly what the user came here to inspect.\n */\nfunction configApiKeyEnvNames(root: string): string[] {\n try {\n const project = loadProjectInfo(root);\n const names = new Set<string>();\n for (const provider of Object.values(resolveModelConfig(project.config.model).providers)) {\n if (provider.apiKeyEnv !== undefined && provider.apiKeyEnv.length > 0) {\n names.add(provider.apiKeyEnv);\n }\n }\n return [...names];\n } catch {\n return [];\n }\n}\n\n/**\n * Resolve the reported keys: provenance from `loadNoirEnv` (spec 12.1's single\n * decision point — this command never re-implements the precedence), filtered\n * down to the curated report described at the top of this file.\n *\n * The resolved VALUE is used only for its length and its redacted shape, inside\n * the loop body below — it is never stored on a row, so it cannot leak into the\n * `--json` envelope.\n */\nfunction resolveEnv(root: string, ambientEnv: Record<string, string | undefined>): ResolvedEnv {\n const { overlay, warnings, sources } = loadNoirEnv(root, ambientEnv);\n const curated = new Set<string>(CURATED_AMBIENT_KEYS);\n for (const name of configApiKeyEnvNames(root)) curated.add(name);\n\n // Candidate keys: everything `sources` records (the loader's authority), plus\n // the curated names that are SET in the ambient environment. The second half\n // exists because `sources` is empty whenever the loader takes one of its two\n // no-op paths — no `.noir/.env` at all, or a git-tracked file refused outright\n // (spec 12.2) — and neither means \"nothing is in effect\": with no file\n // contributing, an exported `CLICKUP_API_TOKEN` IS the value in effect, and\n // reporting \"nothing to report\" while it sits in the user's shell would be a\n // lie. It cannot contradict the loader: those are exactly the keys the loader\n // would have recorded as `'env'` had it run its ambient pass.\n const candidates = new Set<string>(Object.keys(sources));\n for (const key of curated) {\n if (ambientEnv[key] !== undefined) candidates.add(key);\n }\n\n const vars: EnvVarRow[] = [];\n const shapes: Record<string, string> = {};\n // Sorted for a stable table (and a stable `--json` array) across runs.\n for (const key of [...candidates].sort()) {\n const source = sources[key] ?? 'env';\n // (b)/(c): an ambient key is reported ONLY when it is curated. `sources`\n // holds every ambient name, so this filter is what keeps the command from\n // enumerating the environment.\n if (source === 'env' && !curated.has(key)) continue;\n // The value in effect: the file's overlay when the file defined the key\n // (spec 12.1 — the file WINS), else the fallback the file fell through to.\n const value = source === 'file' ? (overlay[key] ?? '') : (ambientEnv[key] ?? '');\n // Shadowing is the same condition the loader warns on: the file defines a\n // key the ambient environment ALSO defines, with a different value. Equal\n // values are not a shadow — nothing the user exported is being overridden.\n const shadowed =\n source === 'file' && ambientEnv[key] !== undefined && ambientEnv[key] !== value;\n\n vars.push({\n key,\n source,\n ...(shadowed ? { shadowed: true } : {}),\n valueLength: value.length,\n });\n shapes[key] = redactShape(value);\n }\n return { vars, warnings, shapes };\n}\n\n/** Human label for the SOURCE column. */\nfunction describeSource(row: EnvVarRow): string {\n if (row.source !== 'file') return 'environment';\n return row.shadowed === true ? '.noir/.env (shadows environment)' : '.noir/.env';\n}\n\n/**\n * `noir env [--json]`: report every key that is in effect, its winning source,\n * and a redacted shape of its value.\n *\n * `--json` writes `{ok:true, data:{vars, warnings}}` to STDOUT — the explicit\n * projection below is the redaction boundary (`shapes` is deliberately dropped).\n * The human path renders a KEY/SOURCE/VALUE table to STDERR and writes nothing\n * to stdout.\n *\n * Loader warnings are NOT re-printed here: `bin.ts`'s `preAction` already\n * applies `.noir/.env` through `applyNoirEnv`, which writes every warning\n * (including the tracked-file refusal) to stderr before this action runs — so\n * they are visible in every real invocation, and printing them again would only\n * duplicate them. They are carried in the `--json` payload for consumers.\n *\n * Always exits 0: this is a report, and \"your file is not being loaded\" is\n * information, not a failure.\n */\nexport async function env(opts: CliOptions = {}): Promise<void> {\n const report = resolveEnv(process.cwd(), ambientBaseline ?? process.env);\n\n if (opts.json === true) {\n json({ ok: true, data: { vars: report.vars, warnings: report.warnings } });\n return;\n }\n\n if (report.vars.length === 0) {\n // \"no keys IN EFFECT\", not \"no keys\": a file can define keys that resolve to\n // nothing — a git-tracked file is refused outright (spec 12.2), and a\n // process-injection key (NODE_OPTIONS, npm_*) is skipped by the loader. Both\n // are reported by the loader's own stderr warnings above this line.\n info(\n 'nothing to report — .noir/.env defines no keys in effect and no Noir-relevant environment variable is set',\n opts,\n );\n return;\n }\n\n table(\n report.vars.map((row) => ({\n KEY: row.key,\n SOURCE: describeSource(row),\n VALUE: report.shapes[row.key] ?? '',\n })),\n ['KEY', 'SOURCE', 'VALUE'],\n opts,\n );\n}\n","import { chmodSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n atomicWriteFile,\n type DetectResult,\n detectActiveMethod,\n detectInstallMethods,\n ensureShimExecutable,\n loadProjectInfo,\n MANAGED_NODE_VERSION,\n NOIR_VERSION,\n noirHome,\n type ProvisionedNode,\n type ProvisionOptions,\n provisionManagedNode,\n readInstallRecord,\n runManagerCmd,\n writeInstallRecord,\n} from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail, info, spinner, success, warn } from '../output.js';\n\nexport interface InstallOptions extends CliOptions {\n list?: boolean;\n uninstallPrev?: boolean;\n spec?: string;\n /** Dismiss the migration banner for the current CLI version (persists in\n * install.json's `dismissedVersions`). Typically passed with `--list`. */\n dismiss?: boolean;\n}\n\nexport interface MigrationPlan {\n detected: DetectResult[];\n currentMethod: string;\n targetSpec: string;\n installedVersion: string | null;\n shouldMigrate: boolean;\n isDowngrade: boolean;\n /**\n * I2 — `update.minVersion` floor (config.ts:238). True when the resolved\n * target is a concrete version below the configured floor. Channel targets\n * (`latest`/`beta`) resolve to the newest at install time and never trip it.\n * Enforced in {@link install} with the same warn/refuse pattern as the\n * downgrade guard.\n */\n belowMinVersion: boolean;\n nativeVersion: string;\n prevUninstallCmd: string | null;\n autoUninstall: boolean; // always false unless --uninstall-prev\n}\n\n/** Per-segment numeric semver comparison. Returns true when `a < b`. */\nfunction semverLt(a: string, b: string): boolean {\n // Strip a leading `v` (npm-idiomatic exact-version spelling that\n // installManagedNode explicitly supports) — bare Number('v1.9.0') is NaN → 0.\n const seg = (s: string): number => Number(s.replace(/^v/, '')) || 0;\n const pa = a.split('.').map(seg);\n const pb = b.split('.').map(seg);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x < y;\n }\n return false;\n}\n\n/** Pure: build the migration plan from detection + target. No I/O. */\nexport function buildMigrationPlan(opts: {\n detected: DetectResult[];\n currentMethod: string;\n targetSpec: string;\n installedVersion: string | null;\n /**\n * The `update.minVersion` floor from config (default `'1.6.0'`). When\n * omitted, the config default applies. See I2.\n */\n minVersion?: string;\n}): MigrationPlan {\n const detected = opts.detected;\n const shouldMigrate = detected.length > 0 && opts.currentMethod !== 'native';\n const nativeVersion = opts.targetSpec;\n let isDowngrade = false;\n if (opts.installedVersion && nativeVersion !== 'latest' && nativeVersion !== 'beta') {\n isDowngrade = semverLt(nativeVersion, opts.installedVersion);\n }\n // I2 — minVersion floor: only a concrete version below the floor trips it;\n // 'latest'/'beta' resolve to the newest at install time (always >= floor).\n const floor = opts.minVersion ?? '1.6.0';\n const belowMinVersion =\n nativeVersion !== 'latest' && nativeVersion !== 'beta' && semverLt(nativeVersion, floor);\n return {\n detected,\n currentMethod: opts.currentMethod,\n targetSpec: opts.targetSpec,\n installedVersion: opts.installedVersion,\n shouldMigrate,\n isDowngrade,\n belowMinVersion,\n nativeVersion,\n prevUninstallCmd: detected[0]?.uninstallCmd ?? null,\n autoUninstall: false,\n };\n}\n\nexport interface InstallManagedNodeOptions {\n channel?: string;\n version?: string;\n env: NodeJS.ProcessEnv;\n /**\n * Forwarded into {@link provisionManagedNode} (P1). Carries the offline mock\n * seams (`fetch`, `exec`, `target`, `signal`) so the install pipeline is\n * unit-testable without network — same pattern as `provisionManagedNode`'s\n * own suite in `packages/core/test/node-provision.test.ts`.\n */\n provision?: Pick<ProvisionOptions, 'fetch' | 'exec' | 'target' | 'signal'>;\n}\n\n/** Normalized record of which runtime backed an install (P2). */\nexport interface InstallManagedNodeResult {\n ok: boolean;\n version: string | null;\n /** What runtime backed this install: `'managed'` (downloaded into ~/.noir)\n * or `'system'` (used the system-Node fallback). */\n runtimeSource?: ProvisionedNode['source'];\n error?: string;\n}\n\n/**\n * Provision the managed Node runtime (P1) and install `@noir-ai/cli` into an\n * isolated `~/.noir/cli` prefix, shimmed from `~/.noir/bin/noir`.\n *\n * Replaces the prior \"not provisioned\" fail branch: instead of requiring the\n * user to pre-run `install.sh`/`install.ps1`, this now delegates to\n * {@link provisionManagedNode} (download + verify + extract, fail-closed), then\n * installs the CLI with the provisioned npm. On a system-Node fallback the\n * fallback node/npm paths are used and a warning is emitted; the install\n * record's `managedRuntimeVersion` reflects which path was taken so `noir\n * doctor` can report it accurately.\n */\nexport async function installManagedNode(\n opts: InstallManagedNodeOptions,\n): Promise<InstallManagedNodeResult> {\n const spec = opts.version ?? opts.channel ?? 'latest';\n const home = noirHome();\n const cliDir = join(home, 'cli');\n const binDir = join(home, 'bin');\n mkdirSync(cliDir, { recursive: true });\n mkdirSync(binDir, { recursive: true });\n\n // Provision the managed Node runtime (P1). Idempotent — re-runs are no-ops.\n // On any failure, provisionManagedNode either falls back to a system Node\n // >=22 (returns { source: 'system' }) or throws — we surface the throw as an\n // {ok:false} envelope rather than letting it kill the CLI.\n let runtime: ProvisionedNode;\n try {\n runtime = await provisionManagedNode({\n ...(opts.provision ?? {}),\n env: opts.env,\n });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n return { ok: false, version: null, error: `managed-Node provision failed: ${reason}` };\n }\n if (runtime.source === 'system') {\n warn(\n `managed-Node download failed; using system Node ${runtime.version} at ${runtime.nodeBin}.`,\n );\n }\n\n const { nodeBin, npmBin } = runtime;\n\n // Install @noir-ai/cli into the isolated prefix using the provisioned npm.\n const { code, stderr } = await runManagerCmd(\n [npmBin, 'install', '-g', `@noir-ai/cli@${spec}`, `--prefix=${cliDir}`],\n { env: opts.env, timeoutMs: 120_000 },\n );\n if (code !== 0) {\n return { ok: false, version: null, error: `npm install failed: ${stderr.slice(0, 300)}` };\n }\n\n // Shim: ~/.noir/bin/noir -> provisioned node + isolated prefix.\n const shim = join(binDir, 'noir');\n const shimBody = `#!/usr/bin/env bash\\n\"${nodeBin}\" \"${join(cliDir, 'lib', 'node_modules', '@noir-ai', 'cli', 'dist', 'bin.js')}\" \"$@\"\\n`;\n atomicWriteFile(shim, shimBody);\n chmodSync(shim, 0o755); // must be executable — atomicWriteFile sets 0o644 by default\n ensureShimExecutable(); // defense-in-depth: re-assert 0o755 (idempotent, never throws)\n // (POSIX shim; Windows uses a .cmd wrapper -- install.sh/install.ps1, P3.)\n\n // Resolve installed version via the provisioned node.\n const ver = await runManagerCmd(\n [\n nodeBin,\n join(cliDir, 'lib', 'node_modules', '@noir-ai', 'cli', 'dist', 'bin.js'),\n '--version',\n ],\n { env: opts.env, timeoutMs: 30_000 },\n );\n const version = ver.code === 0 ? ver.stdout.trim() : null;\n writeInstallRecord({\n method: 'native',\n version: version ?? '0.0.0',\n channel: opts.channel ?? 'latest',\n installedAt: new Date().toISOString(),\n // Reflect which runtime backs this install: the pinned managed version\n // (e.g. '22.23.2') for managed, or 'system-<v>' for the fallback so doctor\n // can distinguish a fallback install from a real managed one.\n managedRuntimeVersion:\n runtime.source === 'managed' ? MANAGED_NODE_VERSION : `system-${runtime.version}`,\n });\n return { ok: true, version, runtimeSource: runtime.source };\n}\n\nexport async function install(opts: InstallOptions = {}): Promise<void> {\n // --dismiss: append the current CLI version to install.json's\n // `dismissedVersions` so the migration banner stops showing for this\n // version. Idempotent (no duplicate entries). Typically combined with\n // `--list`; works standalone too. Requires an existing install record —\n // if there's none, there's nothing to dismiss (no banner was shown).\n // Track the dismiss outcome so `--dismiss --list --json` folds it into the\n // SINGLE list envelope — never two JSON documents on stdout.\n let dismissOutcome: { dismissed: boolean; version: string | null } | undefined;\n if (opts.dismiss === true) {\n const rec = readInstallRecord();\n if (!rec) {\n dismissOutcome = { dismissed: false, version: null };\n if (!opts.json) info('No install record found; nothing to dismiss.');\n } else {\n const dismissed = new Set(rec.dismissedVersions ?? []);\n if (!dismissed.has(NOIR_VERSION)) dismissed.add(NOIR_VERSION);\n writeInstallRecord({ ...rec, dismissedVersions: [...dismissed] });\n dismissOutcome = { dismissed: true, version: NOIR_VERSION };\n if (!opts.json) success(`Migration banner dismissed for ${NOIR_VERSION}.`);\n }\n }\n\n // --dismiss alone (no --list): emit the single result now.\n if (opts.dismiss === true && opts.list !== true) {\n if (opts.json) process.stdout.write(`${JSON.stringify({ ok: true, data: dismissOutcome })}\\n`);\n return;\n }\n\n if (opts.list === true) {\n const detected = await detectInstallMethods(process.env);\n if (opts.json) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { ...(dismissOutcome ?? {}), detected } })}\\n`,\n );\n return;\n }\n info('Detected installs:');\n for (const d of detected) info(` ${d.method} (${d.version ?? 'unknown'})`);\n return;\n }\n\n const currentMethod = detectActiveMethod();\n const detected = await detectInstallMethods(process.env);\n const installedRecord = readInstallRecord();\n // I2 — read the `update.minVersion` floor from the project config (falls\n // back to the config default '1.6.0' when the project isn't initialized or\n // the block is absent). Same try/catch pattern as home.ts's update-config read.\n let minVersion = '1.6.0';\n try {\n minVersion = loadProjectInfo(process.cwd()).config.update.minVersion;\n } catch {\n // Not initialized yet — the config default applies.\n }\n const plan = buildMigrationPlan({\n detected,\n currentMethod,\n targetSpec: opts.spec ?? 'latest',\n installedVersion: installedRecord?.version ?? null,\n minVersion,\n });\n\n // I2 — refuse to install a version below the configured minVersion floor, and\n // refuse a downgrade, UNCONDITIONALLY. The floor/downgrade-guard exist as\n // safety invariants; gating them on `--no-input` meant the common interactive\n // path (and `--json`, which does not set input:false) silently proceeded with\n // only a stderr warning — the invariant was never actually enforced.\n if (plan.belowMinVersion) {\n fail(\n EXIT.USAGE,\n `refusing install of ${plan.nativeVersion} (below minVersion ${minVersion})`,\n opts,\n );\n }\n\n if (plan.isDowngrade) {\n fail(\n EXIT.USAGE,\n `refusing downgrade to ${plan.nativeVersion} (installed ${plan.installedVersion})`,\n opts,\n );\n }\n\n const installSpin = spinner('Installing Noir via native installer...', opts).start();\n const result = await installManagedNode({\n channel: plan.nativeVersion === 'beta' ? 'beta' : undefined,\n version: plan.nativeVersion.startsWith('v')\n ? plan.nativeVersion\n : plan.nativeVersion === 'latest' || plan.nativeVersion === 'beta'\n ? undefined\n : plan.nativeVersion,\n env: process.env,\n });\n if (!result.ok) {\n installSpin.fail('Native install failed');\n fail(EXIT.ERROR, result.error ?? 'native install failed', opts);\n }\n installSpin.succeed(`Installed noir ${result.version}`);\n\n // Migration step runs FIRST, in BOTH modes — the --json early-return below\n // must not skip it (a scripted/CI migration relying on --uninstall-prev would\n // otherwise silently leave the stale install in place while reporting success).\n // Its diagnostics go to stderr (warn/success), which --json leaves untouched.\n if (plan.prevUninstallCmd && opts.uninstallPrev !== true) {\n warn(`To finish the migration, uninstall the previous install: ${plan.prevUninstallCmd}`);\n warn(\n ' Or re-run with --uninstall-prev to do it now. Rollback: reinstall via the previous manager.',\n );\n } else if (plan.prevUninstallCmd && opts.uninstallPrev === true) {\n const { code } = await runManagerCmd(plan.prevUninstallCmd.split(' '), { env: process.env });\n if (code === 0) success('Previous install removed.');\n else warn('Previous install NOT removed (non-zero exit). You can remove it manually.');\n }\n\n // S9 --json: emit the canonical {ok:true,data} success envelope on stdout.\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { version: result.version, runtimeSource: result.runtimeSource } })}\\n`,\n );\n return;\n }\n\n info(`Installed native: ${result.version}.`);\n // Claude Code #41806/#27910 mitigation: the old npm bin may still resolve\n // first in $PATH after migration (the shell hashes command paths, and a\n // stale global install can shadow ~/.noir/bin). Hint the user to refresh.\n info('If `noir` still points at the old install, run: hash -r && which -a noir');\n success(`noir ${result.version} installed via native. Run \\`noir doctor\\` to verify.`);\n}\n","import {\n detectActiveMethod,\n ensureShimExecutable,\n fetchLatestVersion,\n loadProjectInfo,\n readInstallRecord,\n readUpdateCache,\n runManagerCmd,\n shouldCheckForUpdate,\n type UpdateConfigLike,\n writeUpdateCache,\n} from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail, info, spinner, success, warn } from '../output.js';\nimport { installManagedNode } from './install.js';\n\nexport const DEFAULT_UPDATE_CONFIG: UpdateConfigLike = {\n checkEnabled: true,\n checkIntervalHours: 24,\n channel: 'latest',\n minVersion: '1.6.0',\n display: 'notice',\n};\n\nexport interface UpdateOptions extends CliOptions {\n check?: boolean;\n spec?: string;\n}\n\nexport interface UpdateTarget {\n method: string;\n targetSpec: string;\n currentVersion: string | null;\n latestKnown: string | null;\n isUpgrade: boolean;\n /**\n * I2 — `update.minVersion` floor (config.ts:238). True when the latest-known\n * version the registry returned is below the configured floor (e.g. a\n * beta/prerelease channel lag, or a yanked dist-tag). Enforced in\n * {@link update} with the same warn/refuse pattern as the downgrade guard.\n */\n belowMinVersion: boolean;\n}\n\nexport function buildUpdateTarget(opts: {\n method: string;\n channel: string;\n spec?: string;\n currentVersion: string | null;\n latestKnown: string | null;\n /**\n * The `update.minVersion` floor from config (default `'1.6.0'`). When\n * omitted, the config default applies. See I2.\n */\n minVersion?: string;\n}): UpdateTarget {\n const targetSpec = opts.spec ?? opts.channel;\n // The guard target: a concrete `--spec` (an exact version) WINS over the\n // fetched registry version — the T6/I2 guards must evaluate what we will\n // ACTUALLY install, not what the registry happens to offer. A pinned\n // `--spec 1.9.0` against a 1.12.0 registry is a DOWNGRADE, and a pinned\n // `--spec 1.5.0` can trip the minVersion floor — both were previously missed\n // because the guards only ever looked at `latestKnown`.\n const concrete =\n opts.spec != null && opts.spec !== 'latest' && opts.spec !== 'beta'\n ? opts.spec\n : opts.latestKnown;\n // Semver downgrade guard (T6 hardening): only treat the target as an upgrade\n // when it is STRICTLY NEWER than the current one. The prior inequality check\n // (`latestKnown !== currentVersion`) would treat a registry that returned an\n // OLDER version (e.g. beta/prerelease channel lag) as an \"upgrade\" and\n // silently downgrade the install.\n const isUpgrade =\n concrete != null && opts.currentVersion != null && semverGt(concrete, opts.currentVersion);\n // I2 — minVersion floor: refuse a target version below the floor. null\n // latestKnown is handled separately (registry-unreachable branch).\n const floor = opts.minVersion ?? '1.6.0';\n const belowMinVersion = concrete != null && semverLt(concrete, floor);\n return {\n method: opts.method,\n targetSpec,\n currentVersion: opts.currentVersion,\n latestKnown: opts.latestKnown,\n isUpgrade,\n belowMinVersion,\n };\n}\n\n/**\n * Per-segment numeric semver comparison (major.minor.patch). Returns true when\n * `a` is strictly greater than `b`. Pre-release suffixes are ignored — Noir\n * versioning is `MAJOR.MINOR.PATCH` only, and the registry's dist-tag is the\n * source of truth for \"latest\". Non-numeric segments coerce to 0. Used here\n * instead of a full semver library to keep the CLI's dependency surface\n * unchanged (the same helper lives in install.ts for the downgrade guard).\n */\nfunction semverGt(a: string, b: string): boolean {\n // Strip a leading `v` (npm-idiomatic) — bare Number('v1') is NaN.\n const seg = (s: string): number => Number(s.replace(/^v/, '')) || 0;\n const pa = a.split('.').map(seg);\n const pb = b.split('.').map(seg);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x > y;\n }\n return false;\n}\n\n/**\n * Per-segment numeric semver `<` (I2 — minVersion floor). Same comparison\n * discipline as {@link semverGt} / install.ts's downgrade guard: numeric per\n * segment, non-numeric → 0. Returns true when `a` is strictly less than `b`.\n */\nfunction semverLt(a: string, b: string): boolean {\n // Strip a leading `v` (npm-idiomatic) — bare Number('v1') is NaN.\n const seg = (s: string): number => Number(s.replace(/^v/, '')) || 0;\n const pa = a.split('.').map(seg);\n const pb = b.split('.').map(seg);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x < y;\n }\n return false;\n}\n\nexport async function update(opts: UpdateOptions = {}): Promise<void> {\n // NOIR_DISABLE_UPDATES is the hard kill-switch for the SELF-UPDATE surface\n // (distinct from NOIR_DISABLE_UPDATE_CHECK, which only gags the background\n // version check). When set, `noir update` refuses to run at all — enterprises\n // can pin this in the environment to enforce \"updates flow only through the\n // package manager / image rebuild, never from inside the CLI\". `fail` is\n // `: never` (always throws) — the explicit `return` is defensive.\n if (process.env.NOIR_DISABLE_UPDATES !== undefined) {\n fail(\n EXIT.USAGE,\n 'self-update is disabled (NOIR_DISABLE_UPDATES is set); update via your package manager or image rebuild.',\n opts,\n );\n return;\n }\n\n const method = detectActiveMethod();\n const rec = readInstallRecord();\n const currentVersion = rec?.version ?? null;\n\n if (opts.check === true) {\n const checkSpin = spinner('Checking for updates...', opts).start();\n const latest = await fetchLatestVersion('latest');\n if (latest) {\n checkSpin.succeed(`Latest: ${latest} (you have ${currentVersion ?? 'unknown'})`);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { latest, currentVersion } })}\\n`);\n }\n } else {\n checkSpin.fail('Could not reach the registry.');\n // fail() emits the canonical {ok:false,error} envelope under --json AND\n // exits 1 (a manual envelope here would leave the process at exit 0 while\n // the payload claims code 1).\n fail(EXIT.ERROR, 'Could not reach the registry.', opts);\n }\n return;\n }\n\n // Fetch latest (network; timeout-bound).\n const fetchSpin = spinner('Checking for updates...', opts).start();\n const latest = await fetchLatestVersion('latest');\n if (latest === null) {\n fetchSpin.fail('Could not reach the registry.');\n // Registry-unreachable is a FAILURE: route through fail() so --json emits\n // {ok:false,error} on stdout AND the process exits 1 (a silent return would\n // leave stdout empty + exit 0 — indistinguishable from success for scripts).\n fail(EXIT.ERROR, 'Could not reach the registry.', opts);\n }\n fetchSpin.succeed(`Latest: ${latest}`);\n // I2 — read the `update.minVersion` floor from the project config (falls\n // back to the config default '1.6.0' when the project isn't initialized or\n // the block is absent). Same try/catch pattern as home.ts's update-config read.\n let minVersion = DEFAULT_UPDATE_CONFIG.minVersion;\n try {\n minVersion = loadProjectInfo(process.cwd()).config.update.minVersion;\n } catch {\n // Not initialized yet — the config default applies.\n }\n const target = buildUpdateTarget({\n method,\n channel: 'latest',\n spec: opts.spec,\n currentVersion,\n latestKnown: latest,\n minVersion,\n });\n\n // I2 — refuse to install a registry-offered version below the configured\n // minVersion floor (same warn/refuse pattern as install.ts's downgrade guard).\n // Checked before the isUpgrade branch: a below-floor offer is unsafe even if\n // it happens to be newer than the (very old) current install.\n if (target.belowMinVersion) {\n // Name the ACTUAL target (a concrete --spec), not the registry's latest.\n warn(`Target ${target.targetSpec} is below the minVersion floor (${minVersion}).`);\n fail(\n EXIT.USAGE,\n `refusing update to ${target.targetSpec} (below minVersion ${minVersion})`,\n opts,\n );\n }\n\n if (!target.isUpgrade) {\n fetchSpin.succeed(`noir ${currentVersion} is up to date`);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { version: currentVersion, upToDate: true } })}\\n`,\n );\n return;\n }\n info(`noir ${currentVersion} is up to date.`, opts);\n return;\n }\n\n if (method === 'native') {\n const updateSpin = spinner(\n `Updating to ${target.latestKnown} via native installer...`,\n opts,\n ).start();\n const res = await installManagedNode({\n version: target.targetSpec === 'latest' ? undefined : target.targetSpec,\n env: process.env,\n });\n if (!res.ok) {\n updateSpin.fail('Update failed');\n fail(EXIT.ERROR, res.error ?? 'update failed', opts);\n }\n // Chicken-egg safety net: the update was ORCHESTRATED by the OLD binary\n // (which may predate the chmod fix). Re-assert the freshly-written shim is\n // executable so the user's NEXT `noir …` doesn't fail with permission\n // denied. This is the ONLY line that closes the regression once 1.7.4+ is\n // installed — it runs in the NEW binary's process.\n ensureShimExecutable();\n updateSpin.succeed(`Updated to noir ${res.version}`);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { version: res.version } })}\\n`);\n return;\n }\n success(`Updated to ${res.version}.`);\n return;\n }\n\n // npm/pnpm/yarn/bun/homebrew/scoop -> reinstall via the same manager.\n const cmd = updateCmdFor(method, target.targetSpec);\n if (!cmd)\n fail(EXIT.USAGE, `cannot auto-update a ${method} install; use the manager directly`, opts);\n const mgrSpin = spinner(`Updating via ${method}...`, opts).start();\n const { code, stderr } = await runManagerCmd(cmd, { env: process.env });\n if (code !== 0) {\n mgrSpin.fail(`Update failed`);\n fail(EXIT.ERROR, `update failed: ${stderr.slice(0, 300)}`, opts);\n }\n mgrSpin.succeed('Updated');\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { version: target.targetSpec } })}\\n`);\n return;\n }\n success('Updated.');\n}\n\nfunction updateCmdFor(method: string, spec: string): string[] | null {\n switch (method) {\n case 'npm':\n return ['npm', 'install', '-g', `@noir-ai/cli@${spec}`];\n case 'pnpm':\n return ['pnpm', 'add', '-g', `@noir-ai/cli@${spec}`];\n case 'yarn':\n return ['yarn', 'global', 'add', `@noir-ai/cli@${spec}`];\n case 'bun':\n return ['bun', 'add', '-g', `@noir-ai/cli@${spec}`];\n case 'homebrew':\n return ['brew', 'upgrade', 'noir'];\n case 'scoop':\n return ['scoop', 'update', 'noir'];\n default:\n return null;\n }\n}\n\n/**\n * The human-readable update command for an install method. Mirrors\n * {@link updateCmdFor} (the machine command) with the manager the user should\n * actually run — `noir update` is the universal entry (it re-dispatches to the\n * right manager), but the package-manager spellings are shown for transparency.\n */\nexport function updateAdvice(method: string): string {\n switch (method) {\n case 'npm':\n return 'run `npm install -g @noir-ai/cli@latest`';\n case 'pnpm':\n return 'run `pnpm add -g @noir-ai/cli@latest`';\n case 'yarn':\n return 'run `yarn global add @noir-ai/cli@latest`';\n case 'bun':\n return 'run `bun add -g @noir-ai/cli@latest`';\n case 'homebrew':\n return 'run `brew upgrade noir`';\n case 'scoop':\n return 'run `scoop update noir`';\n case 'native':\n return 'run `noir update`';\n default:\n return 'run `noir update`';\n }\n}\n\n/** A \"new version available\" notice for the home menu (version + advice). */\nexport interface UpdateNotice {\n readonly latestVersion: string;\n readonly currentVersion: string;\n readonly method: string;\n readonly advice: string;\n}\n\n/**\n * Build the home-menu \"update available\" notice from the cached latest version.\n * Returns `null` when there is no cached latest, no current version, or the\n * latest is NOT strictly newer (the semver downgrade guard — a stale/older\n * cache entry is never advertised as an upgrade).\n */\nexport function buildUpdateNotice(opts: {\n method: string;\n currentVersion: string | null;\n latestKnown: string | null;\n}): UpdateNotice | null {\n const { method, currentVersion, latestKnown } = opts;\n if (currentVersion == null || latestKnown == null) return null;\n if (!semverGt(latestKnown, currentVersion)) return null;\n return { latestVersion: latestKnown, currentVersion, method, advice: updateAdvice(method) };\n}\n\n/** Non-blocking, time-boxed startup check. Writes the cache on success; silent on any failure. */\nexport async function runAsyncUpdateCheck(opts: {\n env: NodeJS.ProcessEnv;\n configUpdate: UpdateConfigLike;\n quiet: boolean;\n}): Promise<void> {\n try {\n const cache = readUpdateCache();\n if (!shouldCheckForUpdate({ env: opts.env, configUpdate: opts.configUpdate, cache })) return;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 2000);\n const latest = await fetchLatestVersion(opts.configUpdate.channel, controller.signal);\n clearTimeout(timer);\n if (latest != null) {\n writeUpdateCache({\n lastCheckAt: new Date().toISOString(),\n latestVersion: latest,\n channel: opts.configUpdate.channel,\n });\n }\n // Non-blocking: never print in quiet/CI/non-TTY; a mismatch only nudges.\n } catch {\n // silent on any failure — this is a non-blocking background check\n }\n}\n","// S9 t4 — `noir status [--json]`.\n//\n// Aggregates a project + daemon + store + context + workflow + memory snapshot.\n// Project info (id/name/host/version) is assembled IN-PROCESS from\n// `loadProjectInfo` + `NOIR_VERSION` — no daemon round-trip just to name the\n// project. Daemon state comes from a read-only {@link probeDaemon} (liveness\n// probe of `~/.noir/daemon.json` + pid + GET /health) that NEVER starts a daemon\n// (spec F2 amendment: `status` is probe-only and works daemon-down — a down\n// daemon is reported honestly + exit 0, never auto-started). When the probe finds\n// a running daemon, the optional count tools are fetched over one connection via\n// {@link withRunningDaemon} (which reuses the running daemon and also never\n// starts one); if the daemon is down, those sections are simply `null` and the\n// snapshot still renders.\n//\n// Graceful degradation (spec F2 / \"some engines optional\"): every count tool —\n// `host_status` (enriches `daemon.transport`), `store_status`, `context_status`,\n// `workflow_status`, `memory_sessions` — is wrapped in {@link tryTool} and\n// contributes `null` when absent or when its engine is not wired. One missing\n// engine never fails the snapshot; even the daemon being down doesn't (it is the\n// one piece of information `status` exists to report). A tool's own\n// logical-failure envelope (e.g. `workflow_status` returning\n// `{ok:false,error:'no active task'}`) is data, not a transport failure, and is\n// normalized to `null`.\n//\n// Active commands (`context *`, `memory *`, `task *`) do real work and KEEP using\n// `withDaemon` (auto-start acceptable for commands that perform writes/reads with\n// side-effects); their daemon-down path is the same clean exit-4 envelope. Only\n// `status` is probe-only — in-process read fallback for the active commands is\n// deferred to v1.x.\n//\n// Stream discipline (S9): `--json` emits the versioned `{ok,data}` envelope\n// to STDOUT (the only stdout write); the human table + banner go to STDERR via\n// the centralized `table()` / `log()` helpers (auto-stripped under NO_COLOR /\n// non-TTY / --json).\n\nimport { loadProjectInfo, NOIR_VERSION, type ProjectInfo } from '@noir-ai/core';\nimport {\n type DaemonClientOptions,\n type DaemonProbe,\n type DaemonToolCaller,\n probeDaemon,\n withRunningDaemon,\n} from '../daemon-client.js';\nimport { type CliOptions, definitionList, EXIT, fail, log, tip } from '../output.js';\nimport { badge } from '../theme.js';\nimport { DEFAULT_UPDATE_CONFIG, runAsyncUpdateCheck } from './update.js';\n\n/** Options accepted by `status` (global flags + daemon-client knobs). */\nexport interface StatusOptions extends CliOptions, DaemonClientOptions {}\n\n// ---------------------------------------------------------------------------\n// Tool result shapes (the relevant slices of the daemon's payloads). The daemon\n// returns these as JSON text; daemon-client parses them to `unknown`, so each\n// reader below treats a missing/foreign field as `undefined` and the section is\n// reported `null`. Keeping these local (rather than importing the daemon's\n// internal types) means the CLI depends only on the MCP wire contract.\n// ---------------------------------------------------------------------------\ninterface HostStatusResult {\n noir: string;\n project: { id: string; name: string };\n host: string;\n transport: string;\n daemon: boolean;\n pid?: number;\n uptimeSec?: number;\n}\n\ninterface StoreStatusResult {\n ok: boolean;\n projectId: string;\n docCount: number;\n vecCount: number;\n dbPath: string | null;\n degraded: boolean;\n}\n\ninterface ContextStatusResult {\n ok: boolean;\n projectId: string;\n docCount: number;\n vecCount: number;\n indexedFiles: number;\n embedder: { kind: string; model?: string; dim: number };\n degraded: boolean;\n}\n\ninterface WorkflowStatusResult {\n ok: boolean;\n taskId: string;\n phase: string;\n state: string;\n mode: string;\n nextGate: string | null;\n degraded: boolean;\n}\n\ninterface MemorySessionsResult {\n ok: boolean;\n sessions: Array<{ id: string; count: number; lastTs: number }>;\n}\n\n// ---------------------------------------------------------------------------\n// Normalized payload (the `data` of the `--json` envelope + the source for the\n// human table). Every optional section is `null` when its engine is absent, so\n// a `--json` consumer can branch with a simple null check.\n// ---------------------------------------------------------------------------\nexport interface StatusPayload {\n noir: string;\n project: { id: string; name: string };\n host: string;\n daemon: {\n running: boolean;\n transport?: string;\n pid?: number;\n uptimeSec?: number;\n };\n store: {\n docCount: number;\n vecCount: number;\n dbPath: string | null;\n degraded: boolean;\n } | null;\n context: {\n docCount: number;\n vecCount: number;\n indexedFiles: number;\n embedder: string;\n degraded: boolean;\n } | null;\n workflow: {\n taskId: string;\n phase: string;\n state: string;\n mode: string;\n nextGate: string | null;\n degraded: boolean;\n } | null;\n memory: { sessions: number; observations: number } | null;\n}\n\n/**\n * Call an optional daemon tool, returning `null` on ANY failure. The daemon\n * registers `store_status` / `context_status` / `workflow_status` /\n * `memory_sessions` only when it wired the matching engine, so a missing engine\n * surfaces as a tool-call failure that we fold to `null` rather than failing\n * the snapshot. A tool's own `{ok:false,…}` envelope is returned as data (the\n * caller normalizes it) — only transport / parse / not-registered failures map\n * to `null` here.\n *\n * Exported so `noir handoff` reuses the SAME fold-to-null pattern for its\n * bounded `context_search` / `memory_recall` extraction — a missing embedder or\n * a daemon-down degrades to `null` instead of failing the handoff artifact.\n */\nexport async function tryTool<T>(\n caller: DaemonToolCaller,\n name: string,\n args?: Record<string, unknown>,\n): Promise<T | null> {\n try {\n return await caller.callTool<T>(name, args);\n } catch {\n // Engine absent (tool not registered), transport hiccup, or non-JSON text.\n // All are non-fatal for an optional section; report it as unavailable.\n return null;\n }\n}\n\nfunction pickStore(s: StoreStatusResult | null): StatusPayload['store'] {\n if (s?.ok !== true) return null;\n return {\n docCount: s.docCount,\n vecCount: s.vecCount,\n dbPath: s.dbPath,\n degraded: s.degraded === true,\n };\n}\n\nfunction describeEmbedder(e: ContextStatusResult['embedder']): string {\n if (!e || e.kind === 'none') return 'none (BM25-only)';\n const model = typeof e.model === 'string' && e.model.length > 0 ? e.model : '<unset>';\n return `${e.kind} · ${model} (${e.dim}-dim)`;\n}\n\nfunction pickContext(c: ContextStatusResult | null): StatusPayload['context'] {\n if (c?.ok !== true) return null;\n return {\n docCount: c.docCount,\n vecCount: c.vecCount,\n indexedFiles: c.indexedFiles,\n embedder: describeEmbedder(c.embedder),\n degraded: c.degraded === true,\n };\n}\n\nfunction pickWorkflow(w: WorkflowStatusResult | null): StatusPayload['workflow'] {\n // `workflow_status` returns `{ok:false,error:'no active task'}` (data) when no\n // task is active — normalize that to `null` so the payload's `workflow` field\n // cleanly means \"active task snapshot present\".\n if (w?.ok !== true) return null;\n return {\n taskId: w.taskId,\n phase: w.phase,\n state: w.state,\n mode: w.mode,\n nextGate: w.nextGate,\n degraded: w.degraded === true,\n };\n}\n\nfunction pickMemory(m: MemorySessionsResult | null): StatusPayload['memory'] {\n if (m?.ok !== true || !Array.isArray(m.sessions)) return null;\n let observations = 0;\n for (const s of m.sessions) observations += s.count;\n return { sessions: m.sessions.length, observations };\n}\n\n/**\n * Build the normalized payload from in-process project info + the daemon probe\n * + the (all-optional) count-tool results. Daemon `running`/`pid`/`uptimeSec`\n * come from the probe (the honest liveness source); `transport` is enriched from\n * `host_status` when that tool is reachable, else omitted. Every other section is\n * `null` when its engine is absent OR the daemon is down.\n */\nfunction buildPayload(\n project: ProjectInfo,\n probe: DaemonProbe,\n host: HostStatusResult | null,\n store: StoreStatusResult | null,\n context: ContextStatusResult | null,\n workflow: WorkflowStatusResult | null,\n memory: MemorySessionsResult | null,\n): StatusPayload {\n return {\n noir: NOIR_VERSION,\n project: { id: project.id, name: project.name },\n host: project.config.host,\n daemon: {\n running: probe.running,\n ...(probe.pid !== undefined ? { pid: probe.pid } : {}),\n ...(probe.uptimeSec !== undefined ? { uptimeSec: probe.uptimeSec } : {}),\n ...(host && typeof host.transport === 'string' ? { transport: host.transport } : {}),\n },\n store: pickStore(store),\n context: pickContext(context),\n workflow: pickWorkflow(workflow),\n memory: pickMemory(memory),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Human rendering (stderr). A two-column Field/Value table is the leanest\n// readable shape for a status snapshot; cli-table3 is auto-stripped under\n// NO_COLOR / non-TTY and the whole call is a no-op under --json.\n// ---------------------------------------------------------------------------\nfunction formatDuration(sec?: number): string {\n if (typeof sec !== 'number' || sec < 0) return 'unknown';\n if (sec < 60) return `${sec}s`;\n if (sec < 3600) return `${Math.floor(sec / 60)}m ${sec % 60}s`;\n return `${Math.floor(sec / 3600)}h ${Math.floor((sec % 3600) / 60)}m`;\n}\n\nexport function describeDaemon(d: StatusPayload['daemon']): string {\n if (!d.running) return 'not running (start with `noir daemon start`)';\n const pid = typeof d.pid === 'number' ? `pid ${d.pid}` : 'pid unknown';\n const up = typeof d.uptimeSec === 'number' ? `, up ${formatDuration(d.uptimeSec)}` : '';\n const transport = typeof d.transport === 'string' ? `, ${d.transport}` : '';\n return `running (${pid}${up}${transport})`;\n}\n\nexport function describeStore(s: StatusPayload['store']): string {\n if (!s) return 'unavailable (store engine not wired)';\n const flag = s.degraded ? ` ${badge('warn', 'degraded: read-only')}` : '';\n return `${s.docCount} docs / ${s.vecCount} vecs${flag}`;\n}\n\nexport function describeContext(c: StatusPayload['context']): string {\n if (!c) return 'unavailable (context engine not wired)';\n const flag = c.degraded ? ` ${badge('warn', 'degraded')}` : '';\n return `${c.docCount} docs, ${c.vecCount} vecs, ${c.indexedFiles} files · embedder ${c.embedder}${flag}`;\n}\n\nexport function describeWorkflow(w: StatusPayload['workflow']): string {\n if (!w) return 'no active task';\n const gate = w.nextGate ? ` → next gate: ${w.nextGate}` : '';\n const flag = w.degraded ? ` ${badge('warn', 'degraded')}` : '';\n return `${w.phase} (${w.state}, ${w.mode}) · task ${w.taskId}${gate}${flag}`;\n}\n\nexport function describeMemory(m: StatusPayload['memory']): string {\n if (!m) return 'unavailable (memory engine not wired)';\n return `${m.observations} observations across ${m.sessions} session${m.sessions === 1 ? '' : 's'}`;\n}\n\nfunction renderHuman(p: StatusPayload, opts: CliOptions): void {\n // Banner + definition list both go to stderr (rendering is a no-op under\n // --json; we only render human when opts.json is false, but passing opts\n // keeps --quiet honest).\n log(`noir status — ${p.project.name} (${p.project.id})`, opts);\n definitionList(\n [\n { label: 'Project', value: `${p.project.name} (${p.project.id})` },\n { label: 'Host', value: p.host },\n { label: 'Noir', value: p.noir },\n { label: 'Daemon', value: describeDaemon(p.daemon) },\n { label: 'Store', value: describeStore(p.store) },\n { label: 'Context', value: describeContext(p.context) },\n { label: 'Workflow', value: describeWorkflow(p.workflow) },\n { label: 'Memory', value: describeMemory(p.memory) },\n ],\n opts,\n );\n // Resume hint (c4-surface-wiring S2): when a resumable (non-terminal) task is\n // present, surface the resume command git-status-style. `--no-tips`/`--json`\n // silence it (renderHuman is only called when !json; tip() honors --no-tips).\n if (p.workflow && p.workflow.state !== 'done' && p.workflow.state !== 'abandoned') {\n tip('resume: `noir task resume`', opts);\n }\n}\n\n/**\n * Gather the live snapshot into the normalized {@link StatusPayload}. Extracted\n * so `noir handoff` reuses the SAME aggregation path as `noir status`\n * (single source for the multi-engine snapshot). Project info is read in-process\n * (uninitialized → exit 1 propagated to the caller); daemon state comes from a\n * {@link probeDaemon} (NEVER auto-starts). When the daemon is up, the optional\n * count tools are fetched over one {@link withRunningDaemon} connection; when it\n * is down, those sections are `null` and `daemon:{running:false}` — this function\n * never throws on a down daemon (the handoff command degrades gracefully).\n */\nexport async function gatherStatusPayload(opts: StatusOptions): Promise<StatusPayload> {\n // In-process project info — no daemon round-trip. Uninitialized → exit 1\n // (both `status` and `handoff` treat an uninitialized project as a hard error\n // — there is no project to snapshot). Route through fail() so --json emits the\n // canonical {ok:false,error} envelope (a plain Error would leave stdout empty).\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n // Probe — never starts a daemon. A down daemon is reported honestly.\n const probe = await probeDaemon(opts);\n\n let host: HostStatusResult | null = null;\n let store: StoreStatusResult | null = null;\n let context: ContextStatusResult | null = null;\n let workflow: WorkflowStatusResult | null = null;\n let memory: MemorySessionsResult | null = null;\n\n if (probe.running) {\n // Reuse the already-running daemon — withRunningDaemon NEVER auto-starts\n // (probe-only). Pass the probe we already computed so it doesn't GET /health\n // a second time. Every tool is optional (tryTool folds a missing engine /\n // logical-failure envelope to null); host_status now only enriches\n // daemon.transport. If the daemon dies mid-snapshot (probe→connect race),\n // withRunningDaemon returns null and the sections stay null while the probe\n // data still populates the daemon section.\n await withRunningDaemon(\n opts,\n async (caller: DaemonToolCaller): Promise<void> => {\n host = await tryTool<HostStatusResult>(caller, 'host_status');\n store = await tryTool<StoreStatusResult>(caller, 'store_status');\n context = await tryTool<ContextStatusResult>(caller, 'context_status');\n workflow = await tryTool<WorkflowStatusResult>(caller, 'workflow_status');\n memory = await tryTool<MemorySessionsResult>(caller, 'memory_sessions');\n },\n probe,\n );\n }\n\n return buildPayload(project, probe, host, store, context, workflow, memory);\n}\n\n/**\n * `noir status`: aggregate the snapshot and render it.\n *\n * Project info is read in-process; daemon state comes from a {@link probeDaemon}\n * (NEVER auto-starts). When the daemon is up, the optional count tools are\n * fetched over one {@link withRunningDaemon} connection; when it is down, those\n * sections are `null` and `daemon:{running:false}` is rendered with exit 0\n * (status is informational — a down daemon is not an error).\n *\n * `--json` emits `{ok:true, data: StatusPayload}` to stdout. The human path\n * renders a two-column table on stderr. Uninitialized project → exit 1 from\n * `loadProjectInfo` (same as every other command).\n */\nexport async function status(opts: StatusOptions): Promise<void> {\n // fire-and-forget; never blocks, never prints under --json/--quiet/CI/non-TTY.\n void runAsyncUpdateCheck({\n env: process.env,\n configUpdate: (() => {\n try {\n const info = loadProjectInfo(process.cwd());\n return info.config.update ?? DEFAULT_UPDATE_CONFIG;\n } catch {\n return DEFAULT_UPDATE_CONFIG;\n }\n })(),\n quiet: opts.json === true || opts.quiet === true || !process.stdout.isTTY,\n });\n\n const payload = await gatherStatusPayload(opts);\n if (opts.json === true) {\n // Single stdout write — the versioned S9 F11 success envelope.\n process.stdout.write(`${JSON.stringify({ ok: true, data: payload })}\\n`);\n return;\n }\n renderHuman(payload, opts);\n}\n","// S9 — `noir task {new,status,advance,next}`.\n//\n// Thin MCP-client commands over the daemon's workflow surface. The daemon\n// registers four workflow tools (packages/daemon/src/server.ts): `workflow_status`\n// (read the active or named task), `checkpoint` (save/restore in-flight state),\n// `workflow_start` (start a task), and `workflow_advance` (advance/jump a task).\n// All four CLI sub-commands are wired: `status`/`next` → `workflow_status`;\n// `new` → `workflow_start`; `advance` → `workflow_advance`.\n//\n// `new`/`advance` are writes. A read-only (daemon-down) store refuses them with\n// a `{ok:false,degraded:true}` envelope (surfaced as exit 1 here); any other\n// logical failure (unknown task, no active task, illegal transition) is likewise\n// exit 1 for these writes. Daemon-unreachable ⇒ exit 4 (DAEMON_DOWN) from\n// `callDaemonTool`, so transport vs logical failures stay distinguishable.\n//\n// `status` / `next` (reads) map the daemon's logical-failure envelopes\n// (`{ok:false,error:'no active task'}` / `{ok:false,error:'unknown task'}`)\n// onto exit 3 (NOT_FOUND) — a focused task read reporting absence is a\n// not-found condition, not an error (the broader `noir status` folds the same\n// envelope to `null` and stays exit 0).\n\nimport { spawnSync } from 'node:child_process';\nimport { writeFileSync } from 'node:fs';\nimport { loadProjectInfo, nextArtifactSequence, type ProjectInfo, sha256Hex } from '@noir-ai/core';\nimport {\n PHASES,\n type Phase,\n TASK_CLASSES,\n type TaskClass,\n writeChangelogStub,\n writeDecisionStub,\n} from '@noir-ai/workflow';\nimport {\n callDaemonTool,\n type DaemonClientOptions,\n type DaemonProbe,\n probeDaemon,\n withInProcessRead,\n} from '../daemon-client.js';\nimport {\n type CliOptions,\n definitionList,\n EXIT,\n fail,\n info,\n isInteractive,\n log,\n success,\n tip,\n warn,\n} from '../output.js';\nimport { badge } from '../theme.js';\n\n/** Options accepted by every `task` sub-command (globals + daemon knobs). */\nexport interface TaskOptions extends CliOptions, DaemonClientOptions {}\n\n// ---------------------------------------------------------------------------\n// Tool result shapes (slices of the daemon wire payloads; local types).\n// ---------------------------------------------------------------------------\n\n/** `workflow_status` success payload (WorkflowStatus). */\ninterface WorkflowStatusResult {\n ok: true;\n taskId: string;\n phase: string;\n state: string;\n mode: string;\n nextGate: string | null;\n history?: unknown;\n updatedAt?: number;\n degraded?: boolean;\n /** Task class (drives the soft PRD gate); absent on legacy tasks. */\n taskClass?: string;\n /** Block reason captured by `setBlocked` (absent unless blocked). */\n blockReason?: string;\n}\n\n/** `workflow_status` logical-failure envelopes (no active / unknown task). */\ninterface WorkflowNotFound {\n ok: false;\n error?: string;\n taskId?: string;\n}\n\n/** `workflow_resume` payload — a WorkflowStatus + a resumable flag. */\ninterface WorkflowResumeResult {\n ok: boolean;\n resumable?: boolean;\n taskId?: string;\n phase?: string;\n state?: string;\n mode?: string;\n nextGate?: string | null;\n history?: unknown;\n updatedAt?: number;\n degraded?: boolean;\n taskClass?: string;\n blockReason?: string;\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Phase → skill suggestion (grounded in the real @noir-ai/skills builtin pack;\n// each phase maps to a shipped noir-* skill the host can invoke next).\n// Exported so `noir handoff` reuses the SAME phase→skill map when naming\n// the next gate's skill in the handoff artifact — single source.\n// ---------------------------------------------------------------------------\nexport const PHASE_SKILL: Readonly<Record<string, string>> = {\n // C3 curation (2026-08-10): intake+clarify merged into brainstorming;\n // execute → executing-plans; verify → verifying; document → wrap.\n intake: 'noir-brainstorming',\n clarify: 'noir-brainstorming',\n spec: 'noir-spec',\n plan: 'noir-planning',\n execute: 'noir-executing-plans',\n verify: 'noir-verifying',\n document: 'noir-wrap',\n};\n\n/** Map a phase string to its shipped skill id (`null` if unknown / absent).\n * Exported for `noir handoff`. */\nexport function skillFor(phase: string | null | undefined): string | null {\n if (typeof phase !== 'string') return null;\n return PHASE_SKILL[phase] ?? null;\n}\n\n/**\n * c4-verify-gate-recovery S8 — write the document-phase artifacts when a task\n * lands at `done`: a changelog entry + a pending decision-record stub. Uses the\n * artifact conflict seam with `preserve` policy (never clobber a user's edit).\n * The decision-record number is the next after the highest existing ADR.\n */\nfunction writeDoneArtifacts(opts: CliOptions, taskId: string): void {\n try {\n const project = loadProjectInfo(process.cwd());\n const conflict = { conflictPolicy: 'preserve' as const, interactive: false };\n writeChangelogStub(project.root, `- ${taskId}: completed (verify gate passed)`, conflict);\n // Decision-record numbering: the canonical per-type sequence over\n // `.noir/decisions/` (`ADR-<NNNN>-<slug>.md`), max+1. The CLI collapses\n // taskId == slug (`task new --slug`), so the task id doubles as the ADR slug.\n const nextN = nextArtifactSequence(project.root, 'adr');\n writeDecisionStub(project.root, nextN, taskId, undefined, conflict);\n } catch {\n // Artifact writes are best-effort — a failure must never mask the advance\n // result. Surface a tip so the user knows the artifacts didn't land.\n tip('document-phase artifacts could not be written (use --no-artifacts to skip)', opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared fetch + render\n// ---------------------------------------------------------------------------\n\n/**\n * Call `workflow_status` (active task when `taskId` is absent). Returns the\n * success payload or throws exit 3 (NOT_FOUND) for a logical-failure envelope\n * (no active task / unknown task) — a focused task read's \"absent\" is a\n * not-found condition.\n */\nasync function fetchStatus(opts: TaskOptions, taskId?: string): Promise<WorkflowStatusResult> {\n const args: Record<string, unknown> =\n typeof taskId === 'string' && taskId.length > 0 ? { taskId } : {};\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_status',\n args,\n );\n if (res.ok === true) return res;\n const detail = res.error ?? 'task not found';\n // exit 3 (NOT_FOUND): the daemon was reachable; there's just no such task.\n fail(EXIT.NOT_FOUND, `task: ${detail}`, opts);\n}\n\n/** Format a `WorkflowStatus.updatedAt` epoch-ms stamp as a readable UTC string. */\nfunction formatStamp(ms?: number): string {\n if (typeof ms !== 'number' || ms <= 0) return '-';\n return new Date(ms).toISOString().replace('T', ' ').replace(/\\..*$/, 'Z');\n}\n\nfunction renderStatusRow(s: WorkflowStatusResult, opts: CliOptions): void {\n const flag = s.degraded === true ? ` ${badge('warn', 'degraded')}` : '';\n log(`task status — ${s.taskId}${flag}`, opts);\n const rows: { label: string; value: string }[] = [\n { label: 'Task', value: s.taskId },\n { label: 'Phase', value: s.phase },\n { label: 'State', value: s.state },\n { label: 'Mode', value: s.mode },\n ];\n if (typeof s.taskClass === 'string' && s.taskClass.length > 0) {\n rows.push({ label: 'Class', value: s.taskClass });\n }\n if (typeof s.blockReason === 'string' && s.blockReason.length > 0) {\n rows.push({ label: 'Block reason', value: s.blockReason });\n }\n rows.push(\n { label: 'Next gate', value: s.nextGate ?? '—' },\n { label: 'Updated', value: formatStamp(s.updatedAt) },\n );\n definitionList(rows, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task status [<id>]`\n// ---------------------------------------------------------------------------\nexport interface TaskStatusOptions extends TaskOptions {\n /** Positional task id; omitted ⇒ the active task. */\n id?: string;\n}\n\n/** The gate phases in lifecycle order (mirrors the daemon's `nextGateAfter`). */\nconst GATE_PHASES = ['spec', 'plan', 'verify'] as const satisfies readonly Phase[];\n\n/** The next gate-phase strictly ahead of `phase`, or `null` (past verify). */\nfunction nextGateAfter(phase: string): Phase | null {\n // The verify gate is ASYMMETRIC: it fires on entering DONE, so a task AT the\n // verify phase still has it ahead (mirrors the daemon's nextGateAfter).\n if (phase === 'verify') return 'verify';\n const cur = PHASES.indexOf(phase as Phase);\n for (const p of GATE_PHASES) {\n if (PHASES.indexOf(p) > cur) return p;\n }\n return null;\n}\n\n/**\n * Run `task status` against the daemon when it is up, or fall back to the\n * IN-PROCESS read-only workflow engine when the daemon probe reports it down\n * (S9 DS-5). A status read on a read-only store is a pure KV read, so instead\n * of exit 4 it resolves against the in-process engine (still exit 3 NOT_FOUND\n * for an unknown / absent task). Writes (`task new` / `advance`) keep the\n * daemon-required exit-4 path.\n */\nexport async function taskStatus(opts: TaskStatusOptions): Promise<void> {\n // Conservative probe (mirrors contextSearch/memoryRecall): only a CONFIRMED\n // `{running:false}` engages the in-process fallback; an unavailable probe\n // defaults to the daemon path.\n let probe: DaemonProbe;\n try {\n probe = await probeDaemon(opts);\n } catch {\n probe = { running: true };\n }\n if (!probe.running) {\n const s = await withInProcessRead(opts, async (engines) => {\n const id = typeof opts.id === 'string' && opts.id.length > 0 ? opts.id : null;\n const taskId = id ?? engines.workflow.activeTaskId();\n if (!taskId) fail(EXIT.NOT_FOUND, 'task: no active task', opts);\n const task = engines.workflow.status(taskId);\n if (!task) fail(EXIT.NOT_FOUND, `task: unknown task '${taskId}'`, opts);\n const stopped = task.state === 'blocked' || task.state === 'abandoned';\n return {\n ok: true as const,\n taskId: task.taskId,\n phase: task.phase,\n state: task.state,\n mode: task.mode,\n nextGate: stopped ? null : nextGateAfter(task.phase),\n history: task.history,\n updatedAt: task.updatedAt,\n degraded: true as const,\n };\n });\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: s })}\\n`);\n return;\n }\n renderStatusRow(s, opts);\n return;\n }\n const s = await fetchStatus(opts, opts.id);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: s })}\\n`);\n return;\n }\n renderStatusRow(s, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task next`\n// ---------------------------------------------------------------------------\nexport async function taskNext(opts: TaskOptions): Promise<void> {\n const s = await fetchStatus(opts);\n const suggestion = skillFor(s.phase);\n const nextSkill = skillFor(s.nextGate);\n const data = {\n taskId: s.taskId,\n phase: s.phase,\n state: s.state,\n mode: s.mode,\n nextGate: s.nextGate,\n suggestion,\n nextSkill,\n degraded: s.degraded === true,\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n const flag = data.degraded ? ` ${badge('warn', 'degraded')}` : '';\n log(`task next — ${s.taskId} · phase ${s.phase} (${s.state}, ${s.mode})${flag}`, opts);\n if (s.nextGate) {\n info(`next gate: ${s.nextGate}`, opts);\n } else {\n info('no further gate ahead (past verify, or stopped).', opts);\n }\n if (suggestion) info(`skill for current phase (${s.phase}): ${suggestion}`, opts);\n if (nextSkill) info(`skill for next gate (${s.nextGate}): ${nextSkill}`, opts);\n if (!suggestion && !nextSkill) {\n info('run `noir skills list` to see applicable skills.', opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir task new --slug <slug> [--mode full|quick]` → workflow_start\n// ---------------------------------------------------------------------------\nexport interface TaskNewOptions extends TaskOptions {\n slug: string;\n mode?: string;\n /** Task class — drives the soft PRD gate (prd.mandatoryFor). */\n taskClass?: string;\n}\n\nexport async function taskNew(opts: TaskNewOptions): Promise<void> {\n // Validate mode client-side so a typo is a clean usage error (exit 2) rather\n // than a server-side zod rejection that would mis-map onto exit 4.\n let mode: 'full' | 'quick' | undefined;\n if (opts.mode !== undefined) {\n if (opts.mode !== 'full' && opts.mode !== 'quick') {\n fail(EXIT.USAGE, `task new: invalid mode '${opts.mode}' (expected 'full' or 'quick')`, opts);\n }\n mode = opts.mode;\n }\n // Validate taskClass client-side (exit 2 on a typo) — c4-surface-wiring S1.\n let taskClass: TaskClass | undefined;\n if (opts.taskClass !== undefined) {\n if (!TASK_CLASSES.includes(opts.taskClass as TaskClass)) {\n fail(\n EXIT.USAGE,\n `task new: invalid class '${opts.taskClass}' (expected one of: ${TASK_CLASSES.join(', ')})`,\n opts,\n );\n }\n taskClass = opts.taskClass as TaskClass;\n }\n // The CLI surface only takes a slug, so it doubles as the stable taskId — the\n // engine's taskId/slug distinction collapses to the slug here. Re-starting the\n // same slug overwrites (the KV is the source of truth, not a journal).\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_start',\n {\n taskId: opts.slug,\n slug: opts.slug,\n ...(mode === undefined ? {} : { mode }),\n ...(taskClass === undefined ? {} : { taskClass }),\n },\n );\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'start failed';\n fail(EXIT.ERROR, `task new --slug ${opts.slug}: ${detail}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderStatusRow(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task advance [--to <phase>] [--force <reason>]` → workflow_advance\n// ---------------------------------------------------------------------------\nexport interface TaskAdvanceOptions extends TaskOptions {\n to?: string;\n force?: string;\n /** Record the landing gate as `skipped` (advance continues). */\n skip?: boolean;\n /** Skip the document-phase artifact writes (changelog + decision stubs). */\n noArtifacts?: boolean;\n}\n\nexport async function taskAdvance(opts: TaskAdvanceOptions): Promise<void> {\n const args: Record<string, unknown> = {};\n if (typeof opts.to === 'string' && opts.to.length > 0) {\n // Validate the target phase client-side (exit 2 on a typo) — PHASE_SKILL's\n // keys ARE the valid phases, so reuse them rather than a second literal.\n if (!Object.keys(PHASE_SKILL).includes(opts.to)) {\n fail(EXIT.USAGE, `task advance: invalid phase '${opts.to}'`, opts);\n }\n args.to = opts.to;\n }\n if (typeof opts.force === 'string' && opts.force.length > 0) {\n args.force = { reason: opts.force };\n }\n if (opts.skip === true) {\n args.skip = true;\n }\n // Omit taskId → the daemon targets the active task (workflow:active).\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_advance',\n args,\n );\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'advance failed';\n fail(EXIT.ERROR, `task advance: ${detail}`, opts);\n }\n // c4-verify-gate-recovery S8 — document-phase artifact wiring: when the task\n // lands at `done`, write a changelog entry + a pending decision-record stub\n // via the artifact conflict seam (preserve on conflict). `--no-artifacts`\n // skips; the memory consolidation hook is provider-gated and fires via the\n // daemon's existing memory engine when configured (not re-implemented here).\n if (res.state === 'done' && opts.noArtifacts !== true) {\n writeDoneArtifacts(opts, res.taskId);\n }\n // Surface (never auto-emit) the handoff command at the verify gate, the\n // natural handoff point (work moves from Noir's planning into the host's\n // execution). One-line stderr hint via `tip()` so `--no-tips` / `--json`\n // silence it in CI / pipes. Never blocks; never writes to stdout.\n if (typeof opts.to === 'string' && opts.to === 'verify') {\n tip('run `noir handoff` for a ready-to-paste host prompt', opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderStatusRow(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task resume [<id>] [--prompt '<continue instruction>']`\n// → workflow_resume (c4-surface-wiring S2)\n// ---------------------------------------------------------------------------\nexport interface TaskResumeOptions extends TaskOptions {\n id?: string;\n prompt?: string;\n}\n\n/** Render a resume briefing: state, phase, next action + skill hint + artifacts. */\nfunction renderResumeBriefing(r: WorkflowResumeResult, opts: CliOptions): void {\n const flag = r.degraded === true ? ` ${badge('warn', 'degraded')}` : '';\n log(`resume — ${r.taskId} (${r.state}, ${r.mode})${flag}`, opts);\n const rows: { label: string; value: string }[] = [\n { label: 'Task', value: String(r.taskId) },\n { label: 'Phase', value: String(r.phase) },\n { label: 'State', value: String(r.state) },\n { label: 'Mode', value: String(r.mode) },\n ];\n if (typeof r.taskClass === 'string' && r.taskClass.length > 0) {\n rows.push({ label: 'Class', value: r.taskClass });\n }\n if (typeof r.blockReason === 'string' && r.blockReason.length > 0) {\n rows.push({ label: 'Block reason', value: r.blockReason });\n }\n rows.push({ label: 'Next gate', value: r.nextGate ?? '—' });\n definitionList(rows, opts);\n const suggestion = skillFor(r.phase ?? null);\n if (suggestion) info(`skill for current phase (${r.phase}): ${suggestion}`, opts);\n // A blocked task resumes via jump-to-phase (FSM edges from blocked → any in-flight).\n if (r.state === 'blocked') {\n info('resume a blocked task with: noir task advance --to <phase>', opts);\n }\n}\n\nexport async function taskResume(opts: TaskResumeOptions): Promise<void> {\n const args: Record<string, unknown> = {};\n // A positional id targets a specific task; omit it → the active task.\n if (typeof opts.id === 'string' && opts.id.length > 0) {\n args.taskId = opts.id;\n }\n const res = await callDaemonTool<WorkflowResumeResult>(opts, 'workflow_resume', args);\n // resume is a read; the \"nothing to resume\" envelope is exit 1 (not a crash).\n // Route through fail() in BOTH branches so --json also exits 1 (fail emits the\n // canonical {ok:false,error} envelope on stdout) — previously --json wrote a\n // non-canonical shape and exited 0.\n if (res.resumable !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'nothing to resume';\n fail(EXIT.ERROR, `task resume: ${detail}`, opts);\n }\n // `--prompt` records a resume intent (additive KV, observable — not an FSM\n // state change). Surface it in the briefing so the host sees the continue note.\n if (typeof opts.prompt === 'string' && opts.prompt.length > 0) {\n info(`resume prompt: ${opts.prompt}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderResumeBriefing(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task block <reason> [--task <id>]` → workflow_block (S4)\n// ---------------------------------------------------------------------------\nexport interface TaskBlockOptions extends TaskOptions {\n reason: string;\n task?: string;\n}\n\nexport async function taskBlock(opts: TaskBlockOptions): Promise<void> {\n if (typeof opts.reason !== 'string' || opts.reason.trim().length === 0) {\n fail(EXIT.USAGE, 'task block: a non-empty <reason> is required', opts);\n }\n const args: Record<string, unknown> = { reason: opts.reason };\n if (typeof opts.task === 'string' && opts.task.length > 0) args.taskId = opts.task;\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_block',\n args,\n );\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'block failed';\n fail(EXIT.ERROR, `task block: ${detail}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderStatusRow(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task abandon [--task <id>]` → workflow_abandon (S4, destructive confirm)\n// ---------------------------------------------------------------------------\nexport interface TaskAbandonOptions extends TaskOptions {\n task?: string;\n}\n\nexport async function taskAbandon(opts: TaskAbandonOptions): Promise<void> {\n // Abandonment is terminal — confirm in interactive mode (mirrors the in-TUI\n // destructive-confirm pattern). --no-input / --json / CI skip the prompt.\n if (isInteractive(opts)) {\n const clack = await import('@clack/prompts');\n const confirm = await clack.confirm({\n message: 'Abandon this task? This is terminal and cannot be undone.',\n initialValue: false,\n });\n if (clack.isCancel(confirm)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n if (confirm !== true) {\n fail(EXIT.CANCELLED, 'abandon cancelled', opts);\n }\n }\n const args: Record<string, unknown> = {};\n if (typeof opts.task === 'string' && opts.task.length > 0) args.taskId = opts.task;\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_abandon',\n args,\n );\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'abandon failed';\n fail(EXIT.ERROR, `task abandon: ${detail}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderStatusRow(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task verify [--check <name> ...]` → runs checks + submits evidence\n// to workflow_advance (c4-verify-gate-recovery S3).\n// ---------------------------------------------------------------------------\nexport interface TaskVerifyOptions extends TaskOptions {\n /** Restrict to a named subset of checks; defaults to all configured checks. */\n check?: string[];\n}\n\n/** A check definition resolved from the project config. */\ninterface VerifyCheck {\n name: string;\n command: string;\n tier: 'hard' | 'soft';\n}\n\n/** `workflow_advance` result with optional verify-gate pending/recovery. */\ninterface WorkflowAdvanceResult {\n ok: boolean;\n taskId?: string;\n phase?: string;\n state?: string;\n mode?: string;\n nextGate?: string | null;\n history?: unknown;\n updatedAt?: number;\n degraded?: boolean;\n error?: string;\n /** c4-verify-gate-recovery: present when the verify gate did not admit `done`. */\n pendingGate?: { gate: string; reason: string };\n recovery?: string[];\n}\n\n/** Run one shell command and capture {exitCode, outputDigest}. */\nfunction runCheck(cmd: string): { exitCode: number; digest: string } {\n // The CLI (user-invoked) owns shell access — the engine never shells out.\n // Synchronous spawn keeps `noir task verify` a single atomic step; failures\n // surface as non-zero exit codes (the evidence), never a thrown crash.\n try {\n const buf = spawnSync('sh', ['-c', cmd], {\n stdio: ['ignore', 'pipe', 'pipe'],\n encoding: 'utf8',\n });\n let out = '';\n if (buf.stdout) out += buf.stdout.toString();\n if (buf.stderr) out += buf.stderr.toString();\n const status = buf.status;\n return {\n exitCode: typeof status === 'number' ? status : 1,\n digest: sha256Hex(out),\n };\n } catch {\n return { exitCode: 1, digest: sha256Hex('') };\n }\n}\n\nexport async function taskVerify(opts: TaskVerifyOptions): Promise<void> {\n // Resolve the check set from the project config (workflow.gate.verify.checks).\n // No checks resolvable → exit 2 (USAGE) rather than inventing commands.\n // A throw from loadProjectInfo on an uninitialized project must route through\n // fail() — otherwise under --json stdout stays EMPTY (the raw error only\n // reaches stderr), violating the S9 `{ok:false}` envelope contract.\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n const cfgChecks = project.config.workflow?.gate?.verify?.checks ?? [];\n const all: VerifyCheck[] = cfgChecks.map((c) => ({\n name: c.name,\n command: c.command,\n tier: (c.tier ?? 'hard') as 'hard' | 'soft',\n }));\n let checks = all;\n if (opts.check && opts.check.length > 0) {\n const want = new Set(opts.check);\n checks = all.filter((c) => want.has(c.name));\n if (checks.length === 0) {\n fail(\n EXIT.USAGE,\n `task verify: no configured checks match --check ${[...want].join(',')}`,\n opts,\n );\n }\n }\n if (checks.length === 0) {\n fail(\n EXIT.USAGE,\n 'task verify: no verify checks configured (set workflow.gate.verify.checks in .noir/config.yml)',\n opts,\n );\n }\n\n // Run each check, capture evidence.\n const ranAt = Date.now();\n const evidence = {\n ranAt,\n checks: checks.map((c) => {\n const r = runCheck(c.command);\n return {\n name: c.name,\n exitCode: r.exitCode,\n outputDigest: r.digest,\n command: c.command,\n tier: c.tier,\n };\n }),\n summary: '',\n };\n const passed = evidence.checks.filter((c) => c.exitCode === 0).length;\n const failed = evidence.checks.length - passed;\n evidence.summary = `${passed} passed, ${failed} failed`;\n\n // Submit evidence to workflow_advance (targets the active task → verify gate).\n const res = await callDaemonTool<WorkflowAdvanceResult>(opts, 'workflow_advance', { evidence });\n if (res.ok === true) {\n if (opts.json === true) {\n // S9 `{ok:true, data}` — evidence nests INSIDE the payload (no ad-hoc\n // top-level sibling keys), matching every other command's envelope shape.\n process.stdout.write(`${JSON.stringify({ ok: true, data: { ...res, evidence } })}\\n`);\n return;\n }\n // Only a landing at `done` means the verify gate FIRED. If the advance\n // landed elsewhere (e.g. from `executing` → `verifying` before the engine's\n // evidence guard was added), be honest about it rather than claiming success.\n const landed = (res as WorkflowStatusResult).state;\n if (landed === 'done') {\n success(`verify — ${evidence.summary} → gate approved`, opts);\n } else {\n info(\n `verify — ${evidence.summary} → advanced to ${landed} (the verify gate fires at \\`done\\`; run \\`noir task verify\\` again when there)`,\n opts,\n );\n }\n renderStatusRow(res as WorkflowStatusResult, opts);\n return;\n }\n // Pending / failed: a failing verify gate is a FAILURE (exit non-zero), never\n // a silent exit-0 \"success\". Under --json emit the canonical {ok:false,error}\n // envelope (not a flattened `{evidence, ...res}`), preserving pendingGate +\n // recovery + evidence for consumers.\n const detail = res.error ?? res.pendingGate?.reason ?? 'verify failed';\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({\n ok: false,\n error: { code: EXIT.ERROR, message: `task verify: ${detail}` },\n // S9 `{ok:false, error}` + a payload — recovery context + evidence nest\n // under `data`, not as top-level siblings (same envelope shape as the\n // success path; consumers read `data` for the full picture).\n data: {\n ...(res.pendingGate !== undefined ? { pendingGate: res.pendingGate } : {}),\n ...(res.recovery !== undefined ? { recovery: res.recovery } : {}),\n evidence,\n },\n })}\\n`,\n );\n process.exitCode = EXIT.ERROR;\n return;\n }\n if (res.pendingGate) {\n warn(`verify gate: ${res.pendingGate.reason} (${evidence.summary})`, opts);\n const recovery = res.recovery ?? ['retry', 'force', 'skip', 'block'];\n info(\n `recovery: ${recovery\n .map((r) =>\n r === 'retry'\n ? '`noir task verify`'\n : r === 'force'\n ? '`noir task advance --force <reason>`'\n : r === 'skip'\n ? '`noir task advance --skip`'\n : r === 'block'\n ? '`noir task block <reason>`'\n : `\\`${r}\\``,\n )\n .join(' | ')}`,\n opts,\n );\n }\n fail(EXIT.ERROR, `task verify: ${detail}`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task research [<id>]` — list research findings (c4-research-grounding)\n// `noir task research record --type <t> --text \"...\" [--source <ref>] [--task <id>]`\n// → workflow_research_record\n// ---------------------------------------------------------------------------\nexport interface TaskResearchOptions extends TaskOptions {\n id?: string;\n}\n\nexport async function taskResearch(opts: TaskResearchOptions): Promise<void> {\n // List findings by reading the active task's status (which carries history\n // — the research records live in the engine's readResearch KV). For now,\n // surface through the daemon: call workflow_status and report the task's\n // Show status, then note that `noir task research record` writes findings.\n const s = await fetchStatus(opts, opts.id);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: s })}\\n`);\n return;\n }\n renderStatusRow(s, opts);\n info('research findings are recorded via `noir task research record` (list + record)', opts);\n}\n\nexport interface TaskResearchRecordOptions extends TaskOptions {\n type: string;\n text: string;\n source?: string;\n task?: string;\n}\n\nexport async function taskResearchRecord(opts: TaskResearchRecordOptions): Promise<void> {\n const TYPES = ['assumption', 'discovery', 'decision', 'grounding-fact'];\n if (!TYPES.includes(opts.type)) {\n fail(\n EXIT.USAGE,\n `task research record: invalid type '${opts.type}' (expected one of: ${TYPES.join(', ')})`,\n opts,\n );\n }\n const args: Record<string, unknown> = { type: opts.type, text: opts.text };\n if (typeof opts.source === 'string' && opts.source.length > 0) args.source = opts.source;\n if (typeof opts.task === 'string' && opts.task.length > 0) args.taskId = opts.task;\n const res = await callDaemonTool<{\n ok: boolean;\n taskId?: string;\n entry?: unknown;\n error?: string;\n }>(opts, 'workflow_research_record', args);\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'record failed';\n fail(EXIT.ERROR, `task research record: ${detail}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n success(\n `research recorded → ${opts.type}: ${opts.text.slice(0, 80)}${opts.text.length > 80 ? '…' : ''}`,\n opts,\n );\n}\n// ---------------------------------------------------------------------------\n// `noir task decompose <capability-id>` → draft a SlicePlan (c4-decomposition)\n// ---------------------------------------------------------------------------\nexport interface TaskDecomposeOptions extends TaskOptions {\n capability: string;\n out?: string;\n}\n\nexport async function taskDecompose(opts: TaskDecomposeOptions): Promise<void> {\n const cap = opts.capability.trim();\n if (cap.length === 0) fail(EXIT.USAGE, 'task decompose: <capability-id> is required', opts);\n\n // Template SlicePlan (offline — mirrors draftPrd P3: single-shot, no provider\n // needed for a template, null/empty on no-provider). A provider-backed drafting\n // pass would fill richer fields; the offline template gives a valid skeleton.\n const plan = {\n capabilityId: cap,\n intent: `Capability ${cap} — offline template (run with a provider for an LLM-drafted plan)`,\n slices: [\n {\n id: 's1-walking-skeleton',\n title: 'Walking skeleton — thinnest end-to-end',\n type: 'feature' as const,\n rationale: `The first slice for capability ${cap}.`,\n scopeIn: 'End-to-end path linking the main architecture.',\n scopeOut: 'Polish, edge cases, full coverage.',\n dependsOn: [] as { id: string; mode: 'sequential' | 'parallel' }[],\n files: { create: [], modify: [], preserve: [] },\n acceptance: [`verify acceptance criteria for capability ${cap}`],\n doD: ['pnpm test', 'pnpm typecheck'],\n rollbackPlan: { procedure: 'revert the slice commit', verifyCommand: 'pnpm test' },\n },\n ],\n status: { 's1-walking-skeleton': 'planned' as const },\n };\n\n // `--out <path>`: persist the plan JSON (a silent no-op before — the flag was\n // declared + forwarded but never acted on).\n if (typeof opts.out === 'string' && opts.out.length > 0) {\n try {\n writeFileSync(opts.out, `${JSON.stringify(plan, null, 2)}\\n`, 'utf8');\n } catch (err) {\n fail(\n EXIT.ERROR,\n `task decompose: could not write ${opts.out}: ${err instanceof Error ? err.message : String(err)}`,\n opts,\n );\n }\n if (opts.json !== true) info(`wrote plan to ${opts.out}`, opts);\n }\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: plan })}\\n`);\n return;\n }\n log(`decompose — ${cap}`, opts);\n for (const s of plan.slices) {\n info(`${s.id}: ${s.title} (${s.type}) — rationale: ${s.rationale}`, opts);\n }\n tip('each piece enters the existing clarify→spec→plan→execute→verify→document workflow', opts);\n}\n","// `noir handoff` (alias `noir wrap`).\n//\n// The on-demand, pasteable HOST HANDOFF artifact that closes Noir's internal\n// loop and points the user at the right host CLI. Reuses the SAME aggregation\n// path as `noir status` (`gatherStatusPayload`) and the SAME phase→skill map as\n// `noir task` (`PHASE_SKILL` / `skillFor`), then does a BOUNDED in-process\n// `context_search` + `memory_recall` for the active task's domain to seed the\n// host prompt, and renders a structured MARKDOWN block to STDOUT for pasteability.\n//\n// DOCTRINE (hard constraint): Noir NEVER launches the host and NEVER writes the\n// artifact into CLAUDE.md/AGENTS.md (claude byte-parity risk + the double-`@`-\n// import hazard the manifest already fixed). The host-launch directive is TEXT\n// ONLY — `hostLaunchDirective(host)` / `HostAdapter.emitHandoff` produce the\n// \"Open `<host>` …\" wording; `noir handoff` never spawns anything.\n//\n// Graceful degradation (mirrors `status.ts`): a down daemon is reported with an\n// honest note (\"start `noir daemon start` for live task/memory\") and the\n// artifact still renders from in-process `.noir/` reads (project info + a\n// workflow snapshot if locally available) — exit 0. A missing embedder during\n// the bounded extraction folds to `null` (via `tryTool`); the seed section\n// renders a \"degraded\" note instead of failing.\n//\n// Stream discipline: the markdown artifact → STDOUT ONLY (the single\n// stdout write under the default path); `--json` emits the structured payload\n// via the versioned `{ok,data}` envelope instead; ALL diagnostics (the\n// \"wrote <path>\" confirmation, the daemon-down note) → STDERR via `info()`.\n// `--write` persists to `.noir/handoff/HO-<NNNN>-<id>.md` (gitignored — see\n// `@noir-ai/core` `syncIgnores`); the path is reported on stderr, never stdout.\n\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n defaultHandoffBlock,\n type EmitContext,\n type HandoffPayload,\n type HostId,\n hostLaunchDirective,\n resolveAdapter,\n} from '@noir-ai/adapters';\nimport { loadProjectInfo, NOIR_DIR, type ProjectInfo, resolveArtifactPath } from '@noir-ai/core';\nimport {\n type DaemonClientOptions,\n type DaemonProbe,\n probeDaemon,\n withRunningDaemon,\n} from '../daemon-client.js';\nimport { type CliOptions, EXIT, fail, info } from '../output.js';\nimport { gatherStatusPayload, tryTool } from './status.js';\nimport { PHASE_SKILL, skillFor } from './task.js';\n\n/** Options accepted by `noir handoff` (globals + daemon-client knobs). */\nexport interface HandoffOptions extends CliOptions, DaemonClientOptions {\n /** `--write`: persist the artifact to `.noir/handoff/HO-<NNNN>-<id>.md` (gitignored). */\n write?: boolean;\n}\n\n/** Bounded extraction limits — small enough to seed a prompt, never a dump. */\nconst SEED_LIMIT = 5;\n\n// ---------------------------------------------------------------------------\n// Wire result shapes (slices of the daemon payloads; local types — the CLI\n// depends only on the MCP wire contract, mirrors status.ts / context.ts).\n// ---------------------------------------------------------------------------\ninterface ContextSearchHit {\n path?: string;\n score?: number;\n snippet?: string;\n source?: string;\n}\ninterface ContextSearchResult {\n ok?: boolean;\n results?: unknown;\n consumedTokens?: number;\n truncated?: boolean;\n degraded?: boolean;\n mode?: string;\n}\ninterface MemoryRecallHit {\n id?: string;\n observation?: Record<string, unknown>;\n score?: number;\n}\ninterface MemoryRecallResult {\n ok?: boolean;\n results?: unknown;\n degraded?: boolean;\n}\n\n/** A normalized context_search seed entry rendered in the artifact. */\nexport interface HandoffContextHit {\n path: string;\n score: number;\n snippet: string;\n}\n/** A normalized memory_recall seed entry rendered in the artifact. */\nexport interface HandoffMemoryHit {\n id: string;\n summary: string;\n}\n\n/** The structured payload emitted by `--json` (and the source for the markdown\n * render). Every nullable field degrades cleanly (daemon-down / no active task\n * / missing embedder → null + a note), so the artifact NEVER hard-fails. */\nexport interface HandoffPayloadFull {\n project: { id: string; name: string };\n host: HostId;\n /** Host-launch directive text (single source: `hostLaunchDirective` or the\n * adapter's `emitHandoff` override). TEXT ONLY — never a spawn. */\n directive: string;\n task: {\n taskId: string;\n phase: string;\n nextGate: string | null;\n nextSkill: string | null;\n } | null;\n /** Bounded context_search seed (top hits for the task domain). Null when the\n * daemon is down OR the embedder is missing (degraded note rendered). */\n contextSeed: HandoffContextHit[] | null;\n /** Bounded memory_recall seed. Null when the daemon is down. */\n memorySeed: HandoffMemoryHit[] | null;\n /** Honest degradation flags so a `--json` consumer can branch. */\n degraded: {\n daemonDown: boolean;\n embedderMissing: boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Rendering — the markdown artifact (STDOUT) + the structured payload (--json).\n// ---------------------------------------------------------------------------\n\n/** Heading line: `# Noir handoff — <name> (<id>)`. */\nfunction heading(name: string, id: string): string {\n return `# Noir handoff — ${name} (${id})`;\n}\n\n/** Render the markdown artifact from the structured payload. STDOUT-safe (no\n * ANSI — the artifact is pasteable text; `--no-color` honored by virtue of\n * never coloring). */\nfunction renderMarkdown(p: HandoffPayloadFull): string {\n const lines: string[] = [];\n lines.push(heading(p.project.name, p.project.id));\n lines.push('');\n\n // Phase / gate line.\n if (p.task) {\n const gate = p.task.nextGate ?? '—';\n lines.push(`**Phase:** ${p.task.phase} — next gate: ${gate}`);\n } else {\n lines.push('**Phase:** no active task');\n }\n\n // Host directive (the pasteable \"open your host\" line).\n lines.push('');\n lines.push('## Open host');\n lines.push(p.directive);\n\n // Next step.\n lines.push('');\n lines.push('## Next step');\n if (p.task?.nextSkill) {\n lines.push(`Run \\`${p.task.nextSkill}\\` — the host's next gate skill.`);\n } else {\n lines.push('No further gate ahead (past verify, or no active task).');\n }\n\n // Extracted context seed.\n lines.push('');\n lines.push('## Extracted context (seed)');\n if (p.contextSeed && p.contextSeed.length > 0) {\n for (const h of p.contextSeed) {\n lines.push(`- \\`${h.path}\\` (score ${h.score.toFixed(4)})`);\n const snip = h.snippet.trim();\n if (snip.length > 0) lines.push(` > ${snip.replace(/\\n/g, '\\n > ')}`);\n }\n } else if (p.degraded.embedderMissing) {\n lines.push(\n '_Embedder unavailable — degraded to BM25-only or no hits. Start `noir daemon start` for live context._',\n );\n } else {\n lines.push('_No context hits (or daemon down). Start `noir daemon start` for live context._');\n }\n\n // Extracted memory seed.\n lines.push('');\n lines.push('## Extracted memory (seed)');\n if (p.memorySeed && p.memorySeed.length > 0) {\n for (const m of p.memorySeed) {\n lines.push(`- \\`${m.id}\\` — ${m.summary}`);\n }\n } else {\n lines.push(\n '_No memory observations (or daemon down). Start `noir daemon start` for live memory._',\n );\n }\n\n // Live data pointer.\n lines.push('');\n lines.push('## Live data — call these from the host (already wired via the host MCP config)');\n lines.push('- `noir.workflow_status` · `noir.context_search` · `noir.memory_recall`');\n\n // Daemon-down note (honest degradation, mirrors status.ts).\n if (p.degraded.daemonDown) {\n lines.push('');\n lines.push('> **Note:** daemon not running — start `noir daemon start` for live task/memory.');\n }\n\n lines.push('');\n return lines.join('\\n');\n}\n\n/** Normalize a raw context_search hit into the render shape. */\nfunction toContextHit(raw: unknown): HandoffContextHit {\n const h = (raw ?? {}) as ContextSearchHit;\n return {\n path: typeof h.path === 'string' ? h.path : '<unknown>',\n score: typeof h.score === 'number' ? h.score : 0,\n snippet: typeof h.snippet === 'string' ? h.snippet : '',\n };\n}\n\n/** Coerce a memory_recall observation's display summary from a raw hit. */\nfunction observationSummary(raw: MemoryRecallHit): string {\n const obs = raw.observation;\n if (obs && typeof obs === 'object') {\n const content = obs.content;\n if (typeof content === 'string' && content.length > 0) {\n return content.length > 160 ? `${content.slice(0, 157)}…` : content;\n }\n const type = obs.type;\n if (typeof type === 'string') return `(${type})`;\n }\n return '';\n}\n\n/** Normalize a raw memory_recall hit into the render shape. */\nfunction toMemoryHit(raw: unknown): HandoffMemoryHit {\n const h = (raw ?? {}) as MemoryRecallHit;\n return {\n id: typeof h.id === 'string' ? h.id : '<unknown>',\n summary: observationSummary(h),\n };\n}\n\n/** Build the host-facing {@link HandoffPayload} (the adapter hook's input) from\n * the full snapshot. Narrowed so the adapter never sees CLI-internal fields. */\nfunction toHostPayload(p: HandoffPayloadFull): HandoffPayload {\n return {\n project: p.project,\n host: p.host,\n task: p.task,\n };\n}\n\n/** Resolve the directive text: the adapter's `emitHandoff` override if present,\n * else the shared `defaultHandoffBlock` (which composes `hostLaunchDirective`).\n * Routes via `resolveAdapter` (no CLI branching — adding a host needs no CLI\n * edit, mirroring the host-adapter seam). */\nfunction resolveDirective(root: string, p: HandoffPayloadFull): string {\n const ctx: EmitContext = { root };\n const adapter = resolveAdapter(p.host);\n const hostPayload = toHostPayload(p);\n if (typeof adapter.emitHandoff === 'function') {\n return adapter.emitHandoff(ctx, hostPayload);\n }\n return defaultHandoffBlock(ctx, hostPayload);\n}\n\n/** Derive the on-disk filename for `--write`: `.noir/handoff/HO-<NNNN>-<id>.md`.\n * The id is the active task id when available, else the project id (stable +\n * unique per project). Reuses the existing artifact on rewrite. The path is\n * gitignored via `syncIgnores`. */\nfunction handoffFilePath(root: string, taskId: string | undefined, projectId: string): string {\n const id = typeof taskId === 'string' && taskId.length > 0 ? taskId : projectId;\n return resolveArtifactPath(root, 'handoff', { taskId: id });\n}\n\n// ---------------------------------------------------------------------------\n// Bounded extraction — `context_search` + `memory_recall` for the active task's\n// domain. Uses `withRunningDaemon` (probe-only — NEVER starts a daemon) so a\n// down daemon degrades to `null` (exit 0) instead of exit 4. A missing embedder\n// throws inside the tool call → `tryTool` folds to `null` → \"degraded\" note.\n// ---------------------------------------------------------------------------\n\n/** Run the bounded extraction over one daemon connection (if the daemon is up).\n * Returns `{context, memory}` where each is `null` on any failure path. The\n * `query` is the active task id (the task's domain); when no task is active,\n * the project name is used as a stable fallback. */\nasync function extractSeeds(\n opts: HandoffOptions,\n probe: DaemonProbe,\n query: string,\n): Promise<{ context: HandoffContextHit[] | null; memory: HandoffMemoryHit[] | null }> {\n // Guard: if the probe already said down, skip the connection entirely (the\n // artifact renders with a \"start daemon\" note — exit 0).\n if (!probe.running) return { context: null, memory: null };\n\n let context: HandoffContextHit[] | null = null;\n let memory: HandoffMemoryHit[] | null = null;\n\n await withRunningDaemon(\n opts,\n async (caller): Promise<void> => {\n // BOUNDED: small `limit` + a bounded `query` (the task id / project name)\n // on both tools — a seed, never a dump. tryTool folds ANY failure (missing\n // engine, embedder throw, transport hiccup) to null so the artifact never\n // hard-fails on extraction.\n const ctxRes = await tryTool<ContextSearchResult>(caller, 'context_search', {\n query,\n limit: SEED_LIMIT,\n });\n if (ctxRes && ctxRes.ok !== false && Array.isArray(ctxRes.results)) {\n context = (ctxRes.results as unknown[])\n .slice(0, SEED_LIMIT)\n .map(toContextHit)\n .filter((h) => h.path !== '<unknown>' || h.snippet.length > 0);\n }\n const memRes = await tryTool<MemoryRecallResult>(caller, 'memory_recall', {\n query,\n limit: SEED_LIMIT,\n });\n if (memRes && memRes.ok !== false && Array.isArray(memRes.results)) {\n memory = (memRes.results as unknown[])\n .slice(0, SEED_LIMIT)\n .map(toMemoryHit)\n .filter((h) => h.summary.length > 0);\n }\n },\n probe,\n );\n\n return { context, memory };\n}\n\n/** Probe helper — re-probe (gatherStatusPayload already probed once, but we need\n * the probe object here for the bounded extraction). Mirrors status.ts: never\n * starts a daemon, returns `{running:false}` on any miss. Static import matches\n * status.ts — vi.mock hoisting makes the mock apply to static imports too, so a\n * dynamic import is unnecessary (and it would trigger tsup code-splitting, which\n * re-chunks the CLI bin entry and breaks its `isMainModule` guard). */\nasync function probeOnly(opts: HandoffOptions): Promise<DaemonProbe> {\n return probeDaemon(opts);\n}\n\n// ---------------------------------------------------------------------------\n// The command.\n// ---------------------------------------------------------------------------\n\n/**\n * `noir handoff` (alias `noir wrap`): gather the live snapshot + a bounded\n * context/memory seed and render a pasteable host-handoff artifact.\n *\n * - Default: markdown → STDOUT (the single stdout write).\n * - `--write`: persist to `.noir/handoff/HO-<NNNN>-<id>.md` (gitignored); confirm on stderr.\n * - `--json`: emit the structured `{ok:true, data: HandoffPayloadFull}` envelope.\n *\n * Never hard-fails on a down daemon or a missing embedder — degrades to a note\n * (mirrors `status.ts`). Uninitialized project → exit 1 from `loadProjectInfo`.\n */\nexport async function handoff(opts: HandoffOptions): Promise<void> {\n // In-process project info — no daemon round-trip. Uninitialized → exit 1\n // (same as status / every other command). Route through fail() so --json\n // emits the canonical {ok:false,error} envelope (not empty stdout).\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n const host = project.config.host;\n\n // Snapshot — the SAME path `noir status` uses (single source). gatherStatusPayload\n // probes + folds every optional engine to null. We re-probe here for the extraction\n // step (the probe is cheap — one GET /health).\n const snapshot = await gatherStatusPayload(opts);\n const probe = await probeOnly(opts);\n\n // Active task + next-skill (reuse task.ts's PHASE_SKILL — single source).\n const task = snapshot.workflow\n ? {\n taskId: snapshot.workflow.taskId,\n phase: snapshot.workflow.phase,\n nextGate: snapshot.workflow.nextGate,\n nextSkill: skillFor(snapshot.workflow.nextGate) ?? skillFor(snapshot.workflow.phase),\n }\n : null;\n\n // Bounded extraction — the query is the task id (the task's domain), else the\n // project name as a stable fallback.\n const query = task?.taskId ?? project.name;\n const { context, memory } = await extractSeeds(opts, probe, query);\n\n const full: HandoffPayloadFull = {\n project: { id: project.id, name: project.name },\n host,\n directive: '', // filled after the host payload is assembled (below)\n task,\n contextSeed: context,\n memorySeed: memory,\n degraded: {\n daemonDown: !probe.running,\n embedderMissing: context === null && probe.running,\n },\n };\n full.directive = resolveDirective(project.root, full);\n\n // --write persists the markdown artifact BEFORE the --json early-return so\n // `--write --json` writes the file too (previously --json silently skipped it).\n if (opts.write === true) {\n const md = renderMarkdown(full);\n const path = handoffFilePath(project.root, task?.taskId, project.id);\n mkdirSync(join(project.root, NOIR_DIR, 'handoff'), { recursive: true });\n writeFileSync(path, md, 'utf8');\n // Diagnostic → STDERR (stream discipline: stdout is reserved for the\n // artifact/envelope; --write reports its path here, never on stdout).\n info(`handoff written to ${path}`, opts);\n }\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: full })}\\n`);\n return;\n }\n\n // Default: markdown → STDOUT ONLY (pasteability — the user pipes this into a\n // file or straight into the host). No trailing diagnostics on stdout. When\n // --write was set (and not --json), the file is already persisted and its\n // path reported to stderr — do not ALSO dump the artifact to stdout.\n if (opts.write !== true) process.stdout.write(renderMarkdown(full));\n}\n\n// Re-export so bin.ts can wire `noir wrap` as a sibling alias dispatching the\n// same handler, and so PHASE_SKILL stays usable from the test surface.\nexport { hostLaunchDirective, PHASE_SKILL };\n","// SP-B — Noir banner.\n//\n// Pre-rendered \"noir\" ASCII wordmark (figlet \"ANSI Shadow\", generated offline\n// and VERIFIED letter-by-letter to read N-O-I-R — an earlier hand-rolled\n// version had a malformed R that rendered as \"NOHA\") with a SMOOTH gradient via\n// gradient-string. (A per-row rainbow was garish; a smooth vertical gradient is\n// the modern look — cf. the GitHub Copilot CLI banner engineering post.)\n//\n// Guardrails: only the interactive home arm calls this (isInteractive already\n// gates TTY/CI/NO_COLOR/--json/--no-input); shouldShowBanner additionally\n// skips --quiet and NOIR_NO_BANNER. color:false → zero ANSI for snapshots/CI.\n// Responsive: ≥50 cols → block wordmark; <50 → compact `◆ noir` mark.\nimport gradient from 'gradient-string';\nimport type { CliOptions } from './output.js';\nimport { accessibleMode, c, useColor } from './theme.js';\n\n// figlet \"ANSI Shadow\" — regenerated 2026-07-26 from the standard per-letter\n// glyphs and verified (N, O, I, R) to read \"NOIR\". Do NOT hand-edit — regenerate\n// via `figlet -f \"ANSI Shadow\" NOIR` if a glyph changes.\nconst NOIR_BLOCK = [\n '███╗ ██╗ ██████╗ ██╗ ██████╗ ',\n '████╗ ██║ ██╔═══██╗ ██║ ██╔══██╗',\n '██╔██╗ ██║ ██║ ██║ ██║ ██████╔╝',\n '██║╚██╗██║ ██║ ██║ ██║ ██╔══██║',\n '██║ ╚████║ ╚██████╔╝ ██║ ██║ ██║',\n '╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝',\n];\n\n// \"noir\" aesthetic — Midnight Cobalt: a smooth gradient-string gradient (dark\n// cobalt → bright blue → sky) applied per-character across the wordmark\n// (vertical, top → bottom). All stops are bright enough to read on a dark\n// terminal; the deep-blue start evokes \"midnight\" without disappearing.\nconst NOIR_GRADIENT = gradient('#2c5282', '#3b82f6', '#7dd3fc');\n\n/** One-line product tagline (shown under the wordmark). */\nexport const NOIR_TAGLINE = 'discipline, context, and memory layer for agentic CLIs';\n\ninterface BannerOptions {\n /** Terminal width in columns (defaults to process.stdout.columns, else 80). */\n width?: number;\n /** Apply the gradient. Default true. */\n color?: boolean;\n}\n\n/**\n * Render the Noir banner. Wide terminals (≥50 cols) get the full block\n * wordmark; narrow terminals get a compact `◆ noir` mark.\n *\n * Color gating: decoration is on only when the caller did not pass\n * `color:false` AND `useColor()` agrees (honoring NO_COLOR / CLICOLOR_FORCE /\n * TTY uniformly via the theme — the single color authority). Under\n * `NOIR_ACCESSIBLE` the gradient is replaced with a solid accent render\n * (maximum legibility); the wordmark stays recognizable either way.\n */\nexport function renderBanner(opts: BannerOptions = {}): string {\n const width = opts.width ?? process.stdout.columns ?? 80;\n const colorOn = opts.color !== false && useColor();\n if (width < 50) {\n return colorOn ? `${c.accent('◆')} noir` : '◆ noir';\n }\n const block = NOIR_BLOCK.join('\\n');\n if (!colorOn) return block;\n // Accessible mode: skip the multi-stop gradient in favor of a single solid\n // accent color (still the Midnight Cobalt blue role, just without the\n // per-character hue drift that can be harder to read).\n if (accessibleMode()) {\n return block\n .split('\\n')\n .map((line) => c.accent(line))\n .join('\\n');\n }\n return NOIR_GRADIENT.multiline(block);\n}\n\n/**\n * Gate: skip the banner under `--quiet` / `--json` or `NOIR_NO_BANNER`. The TTY\n * / CI / NO_COLOR guards are already handled by `isInteractive` (the only path\n * that calls the banner is the interactive home arm); this adds the\n * banner-specific opt-outs a user can set even inside an interactive session.\n */\nexport function shouldShowBanner(opts: CliOptions): boolean {\n if (opts.quiet === true || opts.json === true) return false;\n const v = process.env.NOIR_NO_BANNER;\n if (v !== undefined && v !== '') return false;\n return true;\n}\n","// A1 — derive {@link PaletteCommand} entries from a commander program.\n//\n// The TUI command palette needs a flat, searchable list of the `noir`\n// subcommands a user can dispatch. Rather than maintain a second hand-curated\n// command table (which would drift from the real tree in `bin.ts`), this module\n// walks the commander tree at palette-build time and projects each LEAF command\n// into a {@link PaletteCommand}.\n//\n// Leaf contract (mirrors the palette's intent — only dispatchable commands\n// surface, never bare groups):\n// - has an action handler (`.action()` was called), AND\n// - has no child commands.\n// Groups — which own children and carry a usage-throwing fallback action in the\n// real CLI — are excluded by the \"no children\" clause regardless of their action\n// state. The program root name ('noir' / '') is never part of the argv path.\n\nimport type { Command } from 'commander';\nimport type { PaletteCommand } from '../palette/types.js';\n\n/**\n * argv-prefix table for commands that mutate the store / workflow / host\n * artifacts. The palette uses this to require confirmation before dispatch.\n * A prefix matches when `commandArgv` starts with every token of the entry\n * (e.g. `['context', 'index']` matches the `context index` leaf).\n */\nconst DESTRUCTIVE_PREFIXES: readonly (readonly string[])[] = [\n ['context', 'index'],\n ['memory', 'save'],\n ['memory', 'forget'],\n ['memory', 'consolidate'],\n ['task', 'new'],\n ['task', 'advance'],\n ['skills', 'sync'],\n ['daemon', 'start'],\n ['daemon', 'stop'],\n ['daemon', 'restart'],\n ['init'],\n ['create'],\n ['sync'],\n ['install'],\n ['update'],\n ['run'],\n];\n\n/**\n * True when `argv` matches one of the {@link DESTRUCTIVE_PREFIXES}. A prefix\n * matches iff `argv` is at least as long as the prefix and every prefix token\n * equals the corresponding argv token — so `['context']` does NOT match the\n * `['context', 'index']` prefix (a bare group isn't destructive), but\n * `['context', 'index']` does.\n */\nexport function isDestructive(argv: readonly string[]): boolean {\n return DESTRUCTIVE_PREFIXES.some((prefix) => {\n if (argv.length < prefix.length) return false;\n return prefix.every((tok, i) => argv[i] === tok);\n });\n}\n\n/** Capitalize the first character of `s`, leaving the rest untouched. */\nfunction capitalizeFirst(s: string): string {\n if (s.length === 0) return s;\n return `${s[0]?.toUpperCase() ?? ''}${s.slice(1)}`;\n}\n\n/**\n * Commander keeps the registered action handler on a private `_actionHandler`\n * field (no public accessor exists). This narrow cast reads only that field so\n * we can tell \"a dispatchable command\" apart from \"a bare group container\".\n */\ntype CommandWithAction = Command & { _actionHandler: ((...args: unknown[]) => unknown) | null };\n\n/** True when commander has an `.action()` handler registered on `cmd`. */\nfunction hasAction(cmd: Command): boolean {\n return (cmd as CommandWithAction)._actionHandler != null;\n}\n\n/**\n * Walk `cmd`'s subtree depth-first, appending one {@link PaletteCommand} per\n * leaf (has an action handler AND no child commands). `path` accumulates the\n * argv tokens excluding the program root name; the caller seeds it as `[]` so\n * the root contributes nothing.\n */\nfunction collectLeaves(cmd: Command, path: string[], out: PaletteCommand[]): void {\n const children = cmd.commands;\n const isLeaf = hasAction(cmd) && children.length === 0;\n\n if (isLeaf) {\n const argv = path;\n const id = argv.join(' ');\n const description = cmd.description() || argv.join(' ');\n out.push({\n id,\n label: argv.map(capitalizeFirst).join(': '),\n argv,\n description,\n category: argv[0] ?? '',\n keywords: argv,\n destructive: isDestructive(argv),\n });\n return;\n }\n\n // Not a leaf — recurse into each child, extending the path with its name.\n // A child `name()` is always a non-empty token (commander rejects empty\n // command names), so `path` only grows with real argv segments.\n for (const child of children) {\n collectLeaves(child, [...path, child.name()], out);\n }\n}\n\n/**\n * Derive the palette entries for `program`: one {@link PaletteCommand} per leaf\n * subcommand, in commander's registration order. The program root name\n * ('noir' / '') is never part of any entry's argv.\n */\nexport function buildPaletteCommands(program: Command): PaletteCommand[] {\n const out: PaletteCommand[] = [];\n // Seed the path empty so the root's own name is excluded; its children become\n // the top-level argv segments (e.g. `context`, `doctor`, `init`).\n collectLeaves(program, [], out);\n return out;\n}\n","// Home-consolidation (S1) — the shared, React-free curated-section module.\n//\n// The bare `noir` home menu (packages/cli/src/commands/home.ts) and the Ink TUI\n// palette's `commands` corpus (App.tsx → palette/rows.ts) both render a curated\n// set of quick actions. This module is the SINGLE source of that curation so\n// the two renderers cannot drift — the same one-table-two-renderers idea that\n// keeps the TUI palette aligned with the commander tree.\n//\n// No-drift contract: every {@link HomeAction} references a palette-registry\n// `id` (e.g. 'context search'), NOT a hand-written argv. {@link resolveSections}\n// re-derives the live palette commands and drops any action whose id no longer\n// exists — so a future commander change degrades the menu to what exists\n// instead of crashing it.\n//\n// This module is deliberately React-free (no ink import) so `home.ts` (the lean\n// @clack path) can consume it without pulling React/Ink into the main CLI graph\n// (NF6 lazy-import constraint). Callers pass the live palette commands\n// explicitly (bin.ts injects them via HomeDeps / TuiDeps), so this module has\n// no import edge back to bin.ts (home.ts must stay circular-import-free).\n\nimport type { PaletteCommand } from '../palette/types.js';\n\n/** A single curated quick action: friendly copy + a palette-registry id. */\nexport interface HomeAction {\n /** The palette-registry id this action runs (e.g. `context search`). */\n readonly id: string;\n /** Friendly title-cased label for the menu (may differ from the leaf). */\n readonly label: string;\n /** One-line dim hint shown under/near the label (exact subcommand + purpose). */\n readonly hint: string;\n /**\n * True when this action mutates the store / workflow / host artifacts (the\n * same prefix table the TUI palette uses). The menu gates it behind a confirm\n * before dispatch.\n */\n readonly destructive?: boolean;\n /**\n * When set, the action collects this argument inline (via clack.text in the\n * menu, or a small text prompt in the TUI home) before dispatch. Mirrors the\n * existing recall-query pattern in home.ts.\n */\n readonly needsArg?: {\n readonly prompt: string;\n readonly placeholder: string;\n };\n /**\n * The exact argv to dispatch. Defaults to the palette entry's `argv`; a\n * `needsArg` action appends the collected value. Always resolves through the\n * registry so it cannot drift.\n */\n readonly dispatch?: readonly string[];\n}\n\n/** A named group of quick actions shown as one home-menu section. */\nexport interface HomeSection {\n /** Stable id (used as a section header + the select value). */\n readonly id: string;\n /** Section title, e.g. 'Status & context'. */\n readonly label: string;\n /** One-line dim hint for the section (its subcommands). */\n readonly hint: string;\n /** Ordered quick actions in this section. */\n readonly items: readonly HomeAction[];\n}\n\n/**\n * The curated home sections, in display order. Each action's `id` must exist in\n * the palette registry (verified by {@link resolveSections}); the friendly copy\n * is authored here once and rendered by both surfaces.\n */\nexport const HOME_SECTIONS: readonly HomeSection[] = [\n {\n id: 'status',\n label: 'Status & context',\n hint: 'snapshot · index · search · doctor',\n items: [\n {\n id: 'status',\n label: 'Status',\n hint: 'project + daemon + workflow + store snapshot (noir status)',\n },\n {\n id: 'context index',\n label: 'Index project',\n hint: '(re)index files into context · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'context search',\n label: 'Search context',\n hint: 'hybrid search over indexed files',\n needsArg: { prompt: 'Context search query:', placeholder: 'e.g. auth flow, deploy steps' },\n },\n {\n id: 'doctor',\n label: 'Doctor',\n hint: 'environment + project health checks',\n },\n {\n id: 'context status',\n label: 'Context status',\n hint: 'index freshness + counts',\n },\n ],\n },\n {\n id: 'memory',\n label: 'Memory',\n hint: 'recall · save · sessions · forget · consolidate',\n items: [\n {\n id: 'memory recall',\n label: 'Recall memory',\n hint: 'search cross-session memory',\n needsArg: { prompt: 'Recall query:', placeholder: 'e.g. auth flow, deploy steps' },\n },\n {\n id: 'memory save',\n label: 'Save memory',\n hint: 'save an observation · ⚠ destructive',\n destructive: true,\n needsArg: { prompt: 'Memory content:', placeholder: 'what you want to remember' },\n },\n {\n id: 'memory sessions',\n label: 'Sessions',\n hint: 'list recent memory sessions',\n },\n {\n id: 'memory forget',\n label: 'Forget memory',\n hint: 'forget observations by id · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'memory consolidate',\n label: 'Consolidate',\n hint: 'fold observations into lessons · ⚠ destructive',\n destructive: true,\n },\n ],\n },\n {\n id: 'workflow',\n label: 'Workflow',\n hint: 'next · status · advance · handoff · wrap',\n items: [\n {\n id: 'task next',\n label: 'Next task',\n hint: 'suggest next phase + applicable skill',\n },\n {\n id: 'task status',\n label: 'Task status',\n hint: 'active task status',\n },\n {\n id: 'task advance',\n label: 'Advance task',\n hint: 'advance to next phase · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'handoff',\n label: 'Handoff',\n hint: 'ready-to-paste host prompt',\n },\n {\n id: 'wrap',\n label: 'Wrap (end session)',\n hint: 'session-end handoff',\n dispatch: ['wrap', '--write'],\n },\n ],\n },\n {\n id: 'setup',\n label: 'Setup & maintenance',\n hint: 'init · create · sync · skills · install · update',\n items: [\n {\n id: 'init',\n label: 'Init',\n hint: 'scaffold Noir in this project · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'create',\n label: 'Create',\n hint: 'bootstrap in a new/empty dir · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'sync',\n label: 'Sync',\n hint: 're-emit managed files + skills · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'skills list',\n label: 'Skills list',\n hint: 'list installed skills',\n },\n {\n id: 'skills sync',\n label: 'Sync skills',\n hint: 're-emit builtin skills · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'update',\n label: 'Update',\n hint: 'check for a new version · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'install',\n label: 'Install / migrate',\n hint: 'native install / migrate · ⚠ destructive',\n destructive: true,\n },\n ],\n },\n {\n id: 'dashboard',\n label: 'Dashboard (full-screen)',\n hint: 'noir tui · noir palette · home',\n items: [\n {\n id: 'tui',\n label: 'Dashboard (full-screen)',\n hint: 'live status · /command · Ctrl+K palette (noir tui)',\n },\n {\n id: 'palette',\n label: 'All commands (fuzzy palette)',\n hint: 'fuzzy search over every command (noir palette)',\n },\n ],\n },\n];\n\n/**\n * Resolve the curated sections against a LIVE palette command list. Drops any\n * action whose `id` does not exist (graceful degradation) and any section left\n * empty. `dispatch` defaults to the palette entry's argv.\n *\n * @param commands The live palette commands (from\n * `buildPaletteCommands(createProgram())`). bin.ts supplies them to both\n * home.ts and the TUI — this module stays free of a bin.ts import edge (and\n * of the load-time cycle a dynamic `import('../../bin.js')` would create,\n * since bin.ts imports home.ts which imports this module).\n */\nexport async function resolveSections(commands: readonly PaletteCommand[]): Promise<HomeSection[]> {\n const byId = new Map(commands.map((c) => [c.id, c]));\n const out: HomeSection[] = [];\n for (const section of HOME_SECTIONS) {\n const items: HomeAction[] = [];\n for (const action of section.items) {\n const entry = byId.get(action.id);\n if (!entry) continue; // id removed from the tree — degrade, don't crash\n items.push({\n ...action,\n // Resolve the dispatch argv from the registry (never hand-written).\n dispatch: action.dispatch ?? [...entry.argv],\n });\n }\n if (items.length === 0) continue; // section went empty — drop it\n out.push({ ...section, items });\n }\n return out;\n}\n","// S9 t4 + home-consolidation — `noir` (bare) home menu.\n//\n// The interactive entry point. Dispatch rules (spec F1 / AC1 / task t4):\n// • interactive (TTY && !CI && !NO_COLOR && !--json && !--no-input) → the\n// @clack/prompts grouped home menu (intro + sections + quick actions),\n// dispatching the chosen action through the SAME commander program the bin\n// uses, so the menu automatically inherits those implementations as they\n// land.\n// • non-interactive + --json → run `status --json` (machine snapshot).\n// • non-interactive + no --json → run `status` (human snapshot). Safe now that\n// `status` is probe-only: it NEVER auto-starts a daemon and exits 0\n// even when the daemon is down, so bare `noir` in CI / a pipe is a useful\n// no-op snapshot instead of a noisy failure or a help dump.\n//\n// Home-consolidation (S2): the interactive arm is a two-level grouped menu —\n// a section picker (select, arrow-navigable) then per-section action lists\n// (select with per-option hints). Navigation is smooth: Esc / backspace /\n// ← return to the section picker, → jumps to the next section, ← to the\n// previous, so the user can move across sections without re-entering level 1\n// each time. The section content comes from the SHARED React-free\n// {@link resolveSections} module (tui/commands/sections.ts), which references\n// palette-registry ids — so the menu cannot drift from the commander tree.\n// `deps.commands` is injected by bin.ts (the same list the TUI palette uses).\n//\n// Both levels use `clack.select` (NOT `selectKey`): `selectKey` is a\n// select-by-typed-letter prompt with no arrow/enter/esc handling, and its\n// `_track=false` value stays `undefined` until a letter is pressed — Enter\n// crashes with `Cannot read properties of undefined (reading 'label')` (the\n// 1.9.0 home-menu bug, fixed by `select` on @clack ≥1.7).\n//\n// `@clack/prompts` is imported lazily inside the interactive branch so the\n// non-interactive paths (the common CI/script case) never pay for it and never\n// touch stdin/stdout rendering (NF6). The menu never blocks when input is\n// disabled — `isInteractive` already gated that — and a Ctrl+C at any prompt\n// maps to exit 5 (CANCELLED) via `fail`.\n//\n// Decoupling: `home` takes its `dispatch` + `commands` as injected callbacks\n// (provided by bin.ts) so this module has NO import edge back to bin.ts (which\n// would be circular: bin imports home). That also makes the menu unit-testable\n// without commander — tests pass a fake callback + mock @clack.\n\nimport { type HostId, hostLaunchDirective } from '@noir-ai/adapters';\nimport {\n detectActiveMethod,\n type InstallRecord,\n latestVersionFromCache,\n loadProjectInfo,\n NOIR_VERSION,\n readInstallRecord,\n readUpdateCache,\n} from '@noir-ai/core';\nimport { NOIR_TAGLINE, renderBanner, shouldShowBanner } from '../banner.js';\nimport { type CliOptions, EXIT, fail, isInteractive } from '../output.js';\nimport { c } from '../theme.js';\nimport { isDestructive } from '../tui/commands/registry.js';\nimport { type HomeAction, type HomeSection, resolveSections } from '../tui/commands/sections.js';\nimport type { PaletteCommand } from '../tui/palette/types.js';\nimport { buildUpdateNotice, DEFAULT_UPDATE_CONFIG, runAsyncUpdateCheck } from './update.js';\n\n/** Callbacks home needs from the bin (injected → no circular import). */\nexport interface HomeDeps {\n /**\n * Run a sub-command by user-form argv (e.g. `['status','--json']`) on a fresh\n * commander program. Must NOT throw — it owns its own error → exit-code\n * mapping (bin's `handleError`) and leaves the outcome on `process.exitCode`\n * so it becomes the final exit code of the whole `noir` invocation.\n */\n dispatch: (argv: readonly string[]) => Promise<void>;\n /**\n * The live palette commands (derived from `createProgram()` by bin.ts) — the\n * same source the TUI palette uses. The grouped home menu resolves its\n * sections against this so it cannot drift. Optional for backward-compat\n * (older tests omit it); when absent, sections fall back to the internal\n * curated ids that resolve against a fresh registry.\n */\n readonly commands?: readonly PaletteCommand[];\n}\n\n/** Try to read the project for a friendlier intro banner; never throws. */\nfunction tryProject(): { id: string; name: string; host: HostId } | null {\n try {\n const info = loadProjectInfo(process.cwd());\n return { id: info.id, name: info.name, host: info.config.host };\n } catch {\n // Not initialized yet (no `.noir/project-id`) — the menu still works; it\n // just shows the generic intro. `Status` will surface the real error.\n return null;\n }\n}\n\n/**\n * One-time nudge: shows only for non-native installs, once per version. The\n * \"dismissed for this version\" flag is stored in install.json's record\n * (`dismissedVersions`); absence ⇒ show. Once the user dismisses (via\n * `noir install --dismiss`), the current CLI version is appended and the\n * banner stays silent until they next upgrade Noir.\n */\nexport function shouldShowMigrationBanner(rec: InstallRecord, currentVersion: string): boolean {\n if (rec.method === 'native') return false;\n if (rec.dismissedVersions?.includes(currentVersion)) return false;\n return true;\n}\n\n/** One-line summary of the CLI command surface (shown under the banner). */\nconst COMMANDS_HINT =\n 'Commands: status · context · memory · task · skills · daemon · doctor · install · update · tui · palette';\n\n/**\n * Bare-`noir` router. See module header for the three dispatch arms. Never\n * blocks when input is disabled (the interactive branch is unreachable then).\n *\n * TUI policy: `--no-tui` (`opts.tui === false`) forces the non-interactive\n * `status` path even when {@link isInteractive} would be true (a TTY). `--tui`\n * (`opts.tui === true`) is advisory only — it still requires a TTY, so the\n * `isInteractive(opts)` gate already encodes that. Auto (no flag) preserves the\n * prior behavior. Advisory only — `home` never hard-gates any subcommand.\n */\nexport async function home(opts: CliOptions, deps: HomeDeps): Promise<void> {\n if (isInteractive(opts) && opts.tui !== false) {\n await runMenu(opts, deps);\n return;\n }\n if (opts.json === true) {\n // Machine snapshot — same data `noir status --json` produces.\n await deps.dispatch(['status', '--json']);\n return;\n }\n // Non-interactive, human: run `status` (human snapshot). status is probe-only\n // — it never auto-starts a daemon and exits 0 even when down, so this is\n // a useful bare-`noir` snapshot in CI / pipes instead of a help dump.\n await deps.dispatch(['status']);\n}\n\n// ---------------------------------------------------------------------------\n// The grouped home menu (interactive arm).\n//\n// Two levels driven by a small state machine so navigation is smooth:\n// LEVEL 1 — section picker (clack.select): 1-6 sections + Exit.\n// LEVEL 2 — per-section action list (clack.select with hints).\n//\n// Navigation keys are handled by @clack itself at each level (Esc cancels that\n// prompt). We return a sentinel from each level and branch on it, so \"back\"\n// from level 2 loops back to level 1, and \"next\"/\"previous\" from level 2 jump\n// to the adjacent section's action list. Cancel anywhere → exit 5 (CANCELLED).\n// ---------------------------------------------------------------------------\n\n/** @clack's `select` result is a value | cancel-symbol. */\ntype ClackChoice = string | symbol;\n\n/** The @clack/prompts module (lazy). */\ntype Clack = typeof import('@clack/prompts');\n\n/**\n * Level 1 — the section picker. Returns which section to open, or a control\n * outcome. Uses `select` (NOT `selectKey`) so arrow navigation, Enter-to-submit,\n * and Esc/Ctrl+C cancel all work. `selectKey` is a select-by-typed-letter prompt\n * with no arrow/enter/esc handling and a `_track=false` value that stays\n * `undefined` until a matching letter is pressed — pressing Enter there crashes\n * with `Cannot read properties of undefined (reading 'label')` (the 1.9.0 home\n * menu bug, fixed by switching to `select` on @clack ≥1.7).\n */\nasync function pickSection(\n clack: Clack,\n sections: readonly HomeSection[],\n opts: CliOptions,\n): Promise<ClackChoice> {\n const result = await clack.select({\n message: 'What would you like to do?',\n options: [\n ...sections.map((s) => ({\n value: s.id,\n label: s.label,\n hint: s.hint,\n })),\n { value: 'exit', label: 'Exit', hint: 'leave the home menu' },\n ],\n });\n if (clack.isCancel(result)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n return result;\n}\n\n/**\n * Level 2 — the action list for `section`. Returns which action to run, or a\n * control outcome. `select` renders each action with a one-line `hint`; the\n * final \"Back\" option returns to the section picker. Cancel → back too.\n */\nasync function pickAction(\n clack: Clack,\n section: HomeSection,\n sections: readonly HomeSection[],\n opts: CliOptions,\n): Promise<ClackChoice> {\n const idx = sections.findIndex((s) => s.id === section.id);\n const hasPrev = idx > 0;\n const hasNext = idx !== -1 && idx < sections.length - 1;\n const result = await clack.select({\n message: section.label,\n initialValue: section.items[0]?.id,\n options: [\n ...section.items.map((a) => ({\n value: a.id,\n label: a.label,\n hint: a.hint,\n })),\n // Navigation options so the user can move across sections without\n // re-entering the section picker (the \"lancar\" navigation requirement).\n ...(hasPrev\n ? [{ value: 'prev' as const, label: '← Prev section', hint: sections[idx - 1]?.label }]\n : []),\n ...(hasNext\n ? [{ value: 'next' as const, label: '→ Next section', hint: sections[idx + 1]?.label }]\n : []),\n { value: 'back', label: '↑ Back to sections', hint: 'return to the section picker' },\n ],\n });\n if (clack.isCancel(result)) {\n // Esc / Ctrl+C at the action list → back to the section picker (not exit).\n clack.cancel('Back to the home menu.');\n return 'back';\n }\n if (opts.verbose) {\n process.stderr.write(`noir: home: section '${section.id}' selected\\n`);\n }\n return result;\n}\n\n/**\n * Collect an inline argument for an action that needs one (generalizes the\n * existing recall-query pattern). Cancel → exit 5.\n */\nasync function collectArg(\n clack: Clack,\n action: HomeAction,\n opts: CliOptions,\n): Promise<string | null> {\n if (!action.needsArg) return null;\n const value = await clack.text({\n message: action.needsArg.prompt,\n placeholder: action.needsArg.placeholder,\n validate: (v: string | undefined) =>\n !v || v.trim().length === 0 ? 'Please enter a value.' : undefined,\n });\n if (clack.isCancel(value)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n return String(value);\n}\n\n/**\n * Resolve the argv an action dispatches: the registry-derived `dispatch`,\n * plus the collected inline arg (when the action needs one). A `destructive`\n * action gates behind a clack.confirm first (reusing the palette registry's\n * destructive table). Returns null when the user declined a destructive action.\n */\nasync function argvForAction(\n clack: Clack,\n action: HomeAction,\n opts: CliOptions,\n): Promise<string[] | null> {\n const base = action.dispatch ?? [action.id];\n let argv = [...base];\n // Collect an inline arg ONLY when the action needs one; a non-arg action\n // skips the prompt entirely (this is what distinguishes \"no arg needed\"\n // from \"cancelled\" — both used to collide on `null` and infinite-loop).\n if (action.needsArg) {\n const arg = await collectArg(clack, action, opts);\n if (arg === null) return null; // cancelled → back to the section\n argv = [...base, arg];\n }\n\n // Gate destructive actions behind an explicit confirm (matches the TUI\n // palette's destructive-confirm overlay — same table, isDestructive()).\n if (action.destructive || isDestructive(argv)) {\n const confirmed = await clack.confirm({\n message: `Run \\`noir ${argv.join(' ')}\\`? This may modify project files / the store.`,\n initialValue: false,\n });\n if (clack.isCancel(confirmed) || !confirmed) return null;\n }\n return argv;\n}\n\n/**\n * The grouped home menu loop. Drives the two levels and the back/next/previous\n * navigation. Dispatch through the shared seam; a selected action returns so\n * `runMenu` can print the outro.\n */\nasync function runGroupedMenu(\n clack: Clack,\n opts: CliOptions,\n deps: HomeDeps,\n sections: readonly HomeSection[],\n): Promise<void> {\n // Start on the first section's action list if the user just wants to move\n // fast; otherwise start at the section picker. We begin at the picker so\n // first-run users see the grouping.\n let currentSection: number | null = null; // null = level 1 (section picker)\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // LEVEL 1 — section picker\n if (currentSection === null) {\n const picked = await pickSection(clack, sections, opts);\n if (clack.isCancel(picked) || picked === 'exit') {\n clack.outro('bye');\n return;\n }\n const idx = sections.findIndex((s) => s.id === picked);\n if (idx === -1) {\n // Unknown section value — defensive: re-show the picker.\n continue;\n }\n currentSection = idx;\n continue;\n }\n\n // LEVEL 2 — the current section's action list\n const section = sections[currentSection];\n if (!section) {\n // Defensive: index out of range — back to the picker.\n currentSection = null;\n continue;\n }\n const chosen = await pickAction(clack, section, sections, opts);\n if (clack.isCancel(chosen)) {\n // Esc at the action list → back to the picker.\n currentSection = null;\n continue;\n }\n\n switch (chosen) {\n case 'back': {\n // ← Back → level 1.\n currentSection = null;\n continue;\n }\n case 'next': {\n // → Next section (progressive: no need to re-pick at level 1).\n currentSection = (currentSection + 1) % sections.length;\n continue;\n }\n case 'prev': {\n // ← Previous section.\n currentSection = (currentSection - 1 + sections.length) % sections.length;\n continue;\n }\n default: {\n const action = section.items.find((a) => a.id === chosen);\n if (!action) {\n // Defensive: unknown action id — back to the picker.\n currentSection = null;\n continue;\n }\n const argv = await argvForAction(clack, action, opts);\n if (argv === null) {\n // Destructive declined (or arg cancelled) → stay in the section.\n continue;\n }\n await deps.dispatch(argv);\n clack.outro('done');\n return; // one action per `noir` run, then the process exits\n }\n }\n }\n}\n\n/** The @clack select menu (interactive arm). */\nasync function runMenu(opts: CliOptions, deps: HomeDeps): Promise<void> {\n // Lazy import: the non-interactive paths never load @clack (NF6).\n const clack = await import('@clack/prompts');\n\n const project = tryProject();\n\n // fire-and-forget; never blocks, never prints under --json/--quiet/CI/non-TTY.\n void runAsyncUpdateCheck({\n env: process.env,\n configUpdate: (() => {\n // Try to read the project's update config block; fall back to defaults\n // if the project isn't initialized or the block is absent.\n try {\n const info = loadProjectInfo(process.cwd());\n return info.config.update ?? DEFAULT_UPDATE_CONFIG;\n } catch {\n return DEFAULT_UPDATE_CONFIG;\n }\n })(),\n quiet: opts.json === true || opts.quiet === true || !process.stdout.isTTY,\n });\n\n if (shouldShowBanner(opts)) {\n const host = project?.host ?? 'claude';\n process.stderr.write(\n `\\n${renderBanner()}\\n${NOIR_TAGLINE}\\n\\n${hostLaunchDirective(host)}\\n${COMMANDS_HINT}\\n\\n`,\n );\n }\n\n // One-time migration nudge for non-native installs.\n const rec = readInstallRecord();\n if (rec && shouldShowMigrationBanner(rec, NOIR_VERSION)) {\n process.stderr.write(\n `\\n ${c.warn(`noir installed via ${rec.method}`)} — consider \\`noir install\\` for the native path (auto-update, no npm prefix/PATH issues). Dismiss with: \\`noir install --dismiss\\` (persisted per version).\\n\\n`,\n );\n }\n\n // Update-available notice (version + install-type-specific advice). Reads the\n // CACHED latest version (the async check above refreshes it for next time) and\n // advertises it only when strictly newer (semver downgrade guard).\n const updateNotice = buildUpdateNotice({\n method: detectActiveMethod(),\n currentVersion: NOIR_VERSION,\n latestKnown: latestVersionFromCache(readUpdateCache(), 'latest'),\n });\n if (updateNotice) {\n process.stderr.write(\n `\\n ${c.warn(`noir ${updateNotice.latestVersion} is available`)} (you have ${updateNotice.currentVersion}) — ${updateNotice.advice}.\\n\\n`,\n );\n }\n\n clack.intro(project ? `noir — ${project.name}` : 'noir');\n\n // Resolve the curated sections against the LIVE palette registry. bin.ts\n // injects `deps.commands` (the same list the TUI palette uses). Tests that\n // omit it (backward-compat) pass an empty list → sections degrade to those\n // whose ids exist (none) — the menu would be empty, but those tests don't\n // drive the interactive arm's content. bin.ts always injects the real list.\n const sections = await resolveSections(deps.commands ?? []);\n\n await runGroupedMenu(clack, opts, deps, sections);\n}\n","// S9 — `noir memory {recall,save,sessions,forget,consolidate}`.\n//\n// Thin MCP-client commands over the running daemon's memory engine. Each is\n// a `callDaemonTool` round-trip (or `withDaemon` when capability discovery is\n// needed); the daemon owns the sole store handle, so the CLI never opens it\n// in-process. Daemon-unreachable ⇒ exit 4 (DAEMON_DOWN) from daemon-client.\n//\n// Scriptability (S9 hard rule): `memory save --content` may be supplied by\n// flag; only when it's missing AND the session is interactive do we prompt via\n// @clack (lazy-imported so non-interactive paths never load it). Missing\n// content under non-interactive / --no-input / --json / CI / NO_COLOR ⇒ exit 2\n// (USAGE) naming the missing flag — NO blocking prompt on a pipe. A cancel at\n// the prompt ⇒ exit 5 (CANCELLED).\n//\n// `memory consolidate` is opt-in + provider-explicit (blueprint D5/D6): the\n// daemon registers the `memory_consolidate` tool ONLY when the user set\n// `memory.consolidation.enabled: true` AND a provider+model resolved. Calling a\n// tool the daemon doesn't register would mis-map onto exit 4 (daemon-down), so\n// we discover the daemon's tool list first (caller.listTools) and, if the tool\n// is absent, emit a clear \"not exposed\" message + exit 1 (honest — the daemon IS\n// up, consolidation just isn't enabled). A provider refusal\n// (`{ok:false,reason:'no-provider'|'model-unavailable'|'no-candidates'}`) is\n// honored as exit 1 with the reason; it is NEVER a silent paid call.\n\nimport { readFileSync } from 'node:fs';\nimport { loadProjectInfo, type ProjectInfo } from '@noir-ai/core';\nimport {\n callDaemonTool,\n type DaemonClientOptions,\n type DaemonProbe,\n type DaemonToolCaller,\n probeDaemon,\n probeWorkspaceDaemon,\n type WorkspaceRouting,\n withDaemon,\n withInProcessRead,\n withWorkspaceDaemon,\n withWorkspaceMemoryRead,\n workspaceRoutingTarget,\n} from '../daemon-client.js';\nimport {\n type CliOptions,\n definitionList,\n EXIT,\n fail,\n info,\n isInteractive,\n log,\n table,\n} from '../output.js';\nimport { badge } from '../theme.js';\n\n/** Options accepted by every `memory` sub-command (globals + daemon knobs). */\nexport interface MemoryOptions extends CliOptions, DaemonClientOptions {}\n\n/**\n * Resolve this repo's workspace membership (marker → routing + project), or\n * `null` when it is not joined. Memory commands route to the WORKSPACE daemon\n * when joined (spec §6); context/workflow/task stay per-project.\n */\nfunction resolveWorkspace(): {\n routing: WorkspaceRouting;\n project: ProjectInfo;\n} | null {\n const routing = workspaceRoutingTarget(process.cwd());\n if (routing === null) return null;\n try {\n return { routing, project: loadProjectInfo(process.cwd()) };\n } catch {\n return null;\n }\n}\n\n/** Call a memory tool — workspace daemon when joined, else the project daemon. */\nasync function callMemory<T>(\n opts: MemoryOptions,\n name: string,\n args: Record<string, unknown> = {},\n): Promise<T> {\n const ws = resolveWorkspace();\n if (ws === null) return callDaemonTool<T>(opts, name, args);\n return withWorkspaceDaemon<T>(opts, ws.routing, (caller) => caller.callTool<T>(name, args));\n}\n\n/** Multi-call memory path (capability discovery): workspace daemon when joined. */\nasync function withMemoryDaemon<T>(\n opts: MemoryOptions,\n fn: (caller: DaemonToolCaller) => Promise<T>,\n): Promise<T> {\n const ws = resolveWorkspace();\n if (ws === null) return withDaemon(opts, fn);\n return withWorkspaceDaemon<T>(opts, ws.routing, fn);\n}\n\n// ---------------------------------------------------------------------------\n// Tool result shapes (slices of the daemon wire payloads; CLI depends only on\n// the MCP contract — local types, mirrors context.ts / status.ts).\n// ---------------------------------------------------------------------------\n\n/** A daemon logical-failure envelope, read generically. */\ninterface ToolFailure {\n ok: false;\n degraded?: boolean;\n error?: string;\n reason?: string;\n logged?: boolean;\n}\n\n/** Normalized memory hit (MemoryHit) rendered to humans + JSON. */\nexport interface MemoryHitData {\n id: string;\n type: string;\n score: number;\n content: string;\n concepts: string[];\n files: string[];\n ts: number;\n importance: number;\n source: string;\n}\n\n/** `memory_recall` success payload. */\ninterface MemoryRecallResult {\n ok: true;\n results?: unknown;\n degraded?: boolean;\n}\n\n/** `memory_save` success payload. */\ninterface MemorySaveResult {\n ok: true;\n id?: string;\n observation?: Record<string, unknown>;\n}\n\n/** One row of `memory_sessions`. */\nexport interface SessionRow {\n id: string;\n count: number;\n lastTs: number;\n}\n\n/** `memory_sessions` success payload. */\ninterface MemorySessionsResult {\n ok: true;\n sessions?: unknown;\n}\n\n/** `memory_forget` success payload (ForgetResult). */\ninterface MemoryForgetResult {\n ok: true;\n deleted?: number;\n ids?: unknown;\n}\n\n/** `memory_consolidate` success payload (ConsolidationResult success branch). */\ninterface MemoryConsolidateOk {\n ok: true;\n lessons?: unknown;\n from?: unknown;\n}\n\n/** `memory_consolidate` refusal (ConsolidationResult failure branch — data). */\ninterface MemoryConsolidateRefusal {\n ok: false;\n reason: 'no-provider' | 'model-unavailable' | 'no-candidates';\n logged?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Small shared helpers\n// ---------------------------------------------------------------------------\n\n/** Coerce `--limit` (commander string) to a positive int, else exit 2. */\nfunction parseLimit(raw: string | undefined, label: string, opts: CliOptions): number | undefined {\n if (raw === undefined) return undefined;\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n fail(EXIT.USAGE, `${label}: --limit must be a positive integer (got '${raw}')`, opts);\n }\n return n;\n}\n\n/** Split a comma-separated flag value (`--files a,b,c`) into a trimmed list. */\nfunction splitCsv(raw: string | undefined): string[] | undefined {\n if (raw === undefined) return undefined;\n const out = raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return out.length > 0 ? out : undefined;\n}\n\n/** Narrow a raw memory hit (MemoryHit) into the rendered {@link MemoryHitData}. */\nfunction toHit(raw: unknown): MemoryHitData {\n const h = (raw ?? {}) as Record<string, unknown>;\n const arr = (v: unknown): string[] =>\n Array.isArray(v) ? (v.filter((x) => typeof x === 'string') as string[]) : [];\n return {\n id: typeof h.id === 'string' ? h.id : '',\n type: typeof h.type === 'string' ? h.type : '',\n score: typeof h.score === 'number' ? h.score : 0,\n content: typeof h.content === 'string' ? h.content : '',\n concepts: arr(h.concepts),\n files: arr(h.files),\n ts: typeof h.ts === 'number' ? h.ts : 0,\n importance: typeof h.importance === 'number' ? h.importance : 0,\n source: typeof h.source === 'string' ? h.source : '',\n };\n}\n\n/** Surface a daemon logical-failure envelope as exit 1 (daemon was reachable). */\nfunction failTool(label: string, envelope: ToolFailure, opts: CliOptions): never {\n const detail = envelope.error ?? envelope.reason ?? 'unknown failure';\n fail(EXIT.ERROR, `${label}: ${detail}`, opts);\n}\n\n/** Render an epoch-ms as a short ISO-ish stamp for human tables. */\nfunction stamp(ts: number): string {\n if (typeof ts !== 'number' || ts <= 0) return '-';\n // UTC ISO (no tz surprises); clipped for table density.\n return new Date(ts).toISOString().replace('T', ' ').replace(/\\..*$/, 'Z');\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory recall <query> [--limit N]`\n// ---------------------------------------------------------------------------\nexport interface MemoryRecallOptions extends MemoryOptions {\n query: string;\n limit?: string;\n}\n\n/**\n * Run `memory_recall` against the daemon when it is up, or fall back to the\n * IN-PROCESS read-only memory engine when the daemon probe reports it down\n * (S9 DS-5). Reads degrade to the read-only store (never exit 4); writes\n * (`memory save` / `forget` / `consolidate`) keep the daemon-required path.\n * The probe is conservative (mirrors contextSearch): only a CONFIRMED\n * `{running:false}` engages the fallback; an unavailable probe defaults to the\n * daemon path.\n */\nexport async function memoryRecall(opts: MemoryRecallOptions): Promise<void> {\n const limit = parseLimit(opts.limit, 'memory recall', opts);\n // Workspace mode: route to the workspace daemon / workspace read-only store.\n const ws = resolveWorkspace();\n if (ws !== null) {\n const probe = await probeWorkspaceDaemon(ws.routing);\n if (!probe.running) {\n await withWorkspaceMemoryRead(opts, ws.routing, ws.project, async (memory) => {\n const { hits, degraded, mode } = await memory.recallWithMeta(opts.query, {\n ...(limit === undefined ? {} : { limit }),\n });\n const data = { query: opts.query, hits, degraded, mode };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderRecall(data.query, data.hits, data.degraded, opts);\n });\n return;\n }\n const res = await callMemory<MemoryRecallResult | ToolFailure>(opts, 'memory_recall', {\n query: opts.query,\n ...(limit === undefined ? {} : { limit }),\n });\n if (res.ok !== true) failTool('memory recall', res, opts);\n const hits = Array.isArray(res.results) ? (res.results as unknown[]).map(toHit) : [];\n const data = { query: opts.query, hits, degraded: res.degraded === true };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderRecall(data.query, hits, data.degraded, opts);\n return;\n }\n let probe: DaemonProbe;\n try {\n probe = await probeDaemon(opts);\n } catch {\n probe = { running: true };\n }\n if (!probe.running) {\n await withInProcessRead(opts, async (engines) => {\n const { hits, degraded, mode } = await engines.memory.recallWithMeta(opts.query, {\n ...(limit === undefined ? {} : { limit }),\n });\n // Honest per-call outcome: this branch runs because the daemon is DOWN\n // (read-only fallback), AND the recall itself reports its own degraded\n // (BM25-only) state — both feed the rendered flag.\n const data = { query: opts.query, hits, degraded, mode };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderRecall(data.query, data.hits, data.degraded, opts);\n });\n return;\n }\n const res = await callDaemonTool<MemoryRecallResult | ToolFailure>(opts, 'memory_recall', {\n query: opts.query,\n ...(limit === undefined ? {} : { limit }),\n });\n if (res.ok !== true) failTool('memory recall', res, opts);\n\n const hits = Array.isArray(res.results) ? (res.results as unknown[]).map(toHit) : [];\n const data = { query: opts.query, hits, degraded: res.degraded === true };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderRecall(data.query, hits, data.degraded, opts);\n}\n\nfunction renderRecall(\n query: string,\n hits: MemoryHitData[],\n degraded: boolean,\n opts: CliOptions,\n): void {\n const flag = degraded ? ` ${badge('warn', 'degraded: BM25-only')}` : '';\n log(\n `memory recall — ${hits.length} hit${hits.length === 1 ? '' : 's'} for '${query}'${flag}`,\n opts,\n );\n if (hits.length === 0) {\n info('(no memories matched)', opts);\n return;\n }\n // Full content per hit (never truncate the DATA; display shows it whole\n // in a readable block rather than a cramped table cell). The block-list shape\n // is load-bearing — do NOT sweep this into the responsive table().\n for (let i = 0; i < hits.length; i++) {\n const h = hits[i];\n if (h === undefined) continue;\n const head = `[${i + 1}] ${h.type} · score ${h.score.toFixed(4)} · importance ${h.importance.toFixed(2)} · ${h.id}`;\n log(head, opts);\n if (h.files.length > 0) log(` files: ${h.files.join(', ')}`, opts);\n if (h.concepts.length > 0) log(` tags: ${h.concepts.join(', ')}`, opts);\n log(` ${h.content}`, opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory save [--content] [--type] [--files]`\n// ---------------------------------------------------------------------------\nexport interface MemorySaveOptions extends MemoryOptions {\n content?: string;\n type?: string;\n files?: string;\n}\n\nexport async function memorySave(opts: MemorySaveOptions): Promise<void> {\n const content = await resolveContent(opts);\n const files = splitCsv(opts.files);\n const args: Record<string, unknown> = { content };\n if (typeof opts.type === 'string' && opts.type.length > 0) args.type = opts.type;\n if (files !== undefined) args.files = files;\n\n const res = await callMemory<MemorySaveResult | ToolFailure>(opts, 'memory_save', args);\n if (res.ok !== true) failTool('memory save', res, opts);\n\n const id = typeof res.id === 'string' ? res.id : '';\n const observation = res.observation ?? {};\n const data = { id, observation };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(`Saved memory ${id}.`, opts);\n renderObservation(observation, opts);\n}\n\n/**\n * Resolve `--content`: from the flag when given; otherwise prompt interactively\n * (only when the session is interactive); otherwise exit 2 naming the flag. The\n * @clack import is lazy so a scripted / CI / --json run never loads it, and a\n * cancel at the prompt maps to exit 5.\n */\nasync function resolveContent(opts: MemorySaveOptions): Promise<string> {\n if (typeof opts.content === 'string' && opts.content.length > 0) return opts.content;\n if (!isInteractive(opts)) {\n fail(\n EXIT.USAGE,\n 'memory save requires --content <text> (or re-run in an interactive terminal to be prompted)',\n opts,\n );\n }\n const clack = await import('@clack/prompts');\n const value = await clack.text({\n message: 'Memory to save:',\n placeholder: 'the insight / decision / pattern to remember',\n validate: (v: string | undefined) =>\n !v || v.trim().length === 0 ? 'content cannot be empty' : undefined,\n });\n if (clack.isCancel(value)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n return String(value);\n}\n\nfunction renderObservation(obs: Record<string, unknown>, opts: CliOptions): void {\n const rows: Array<{ label: string; value: unknown }> = [];\n for (const key of ['id', 'type', 'importance', 'ts', 'source', 'project', 'sessionId']) {\n if (obs[key] !== undefined) rows.push({ label: key, value: obs[key] });\n }\n if (rows.length > 0) definitionList(rows, opts);\n const content = obs.content;\n if (typeof content === 'string') log(`\\n${content}`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory capture [file]` — manual transcript/notes distill (S9)\n// ---------------------------------------------------------------------------\nexport interface MemoryCaptureOptions extends MemoryOptions {\n /** A transcript/notes file to distill (or pipe stdin). */\n file?: string;\n /** Inline distilled content (overrides file/stdin). */\n content?: string;\n /** Capture hook label for provenance (defaults to 'Stop'). */\n eventType?: string;\n}\n\nexport async function memoryCapture(opts: MemoryCaptureOptions): Promise<void> {\n const content = await resolveCaptureContent(opts);\n const args: Record<string, unknown> = { content };\n if (typeof opts.eventType === 'string' && opts.eventType.length > 0)\n args.eventType = opts.eventType;\n\n const res = await callMemory<MemorySaveResult | ToolFailure>(opts, 'memory_capture', args);\n if (res.ok !== true) failTool('memory capture', res, opts);\n\n const id = typeof res.id === 'string' ? res.id : '';\n const observation = res.observation ?? {};\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { id, observation } })}\\n`);\n return;\n }\n log(`Captured memory ${id}.`, opts);\n renderObservation(observation, opts);\n}\n\n/**\n * Resolve capture content: `--content` > `--file` (read the file) > piped stdin\n * > interactive prompt. Missing all sources under non-interactive ⇒ exit 2.\n */\nasync function resolveCaptureContent(opts: MemoryCaptureOptions): Promise<string> {\n if (typeof opts.content === 'string' && opts.content.length > 0) return opts.content;\n if (typeof opts.file === 'string' && opts.file.length > 0) {\n try {\n return readFileSync(opts.file, 'utf8');\n } catch {\n fail(EXIT.USAGE, `memory capture could not read --file ${opts.file}`, opts);\n }\n }\n if (!process.stdin.isTTY) {\n // A non-TTY stdin can still be unreadable (closed fd 0, EAGAIN under CI).\n // Route that through fail() so `--json` gets its {ok:false} envelope instead\n // of a raw readFileSync error on stderr.\n try {\n return readFileSync(0, 'utf8');\n } catch {\n fail(\n EXIT.USAGE,\n 'memory capture could not read piped stdin — pass --content <text> or --file <path>.',\n opts,\n );\n }\n }\n if (!isInteractive(opts)) {\n fail(\n EXIT.USAGE,\n 'memory capture requires --content <text> or --file <path> (or piped stdin).',\n opts,\n );\n }\n const clack = await import('@clack/prompts');\n const value = await clack.text({ message: 'Captured memory content:' });\n if (clack.isCancel(value)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n return String(value);\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory sessions`\n// ---------------------------------------------------------------------------\nexport async function memorySessions(opts: MemoryOptions): Promise<void> {\n const ws = resolveWorkspace();\n if (ws !== null) {\n const probe = await probeWorkspaceDaemon(ws.routing);\n if (!probe.running) {\n await withWorkspaceMemoryRead(opts, ws.routing, ws.project, async (memory) => {\n const sessions: SessionRow[] = memory.sessions().map((s) => ({\n id: s.id,\n count: s.count,\n lastTs: s.lastTs,\n }));\n const data = { sessions };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSessions(sessions, opts);\n });\n return;\n }\n const res = await callMemory<MemorySessionsResult | ToolFailure>(opts, 'memory_sessions');\n if (res.ok !== true) failTool('memory sessions', res, opts);\n const sessions: SessionRow[] = Array.isArray(res.sessions)\n ? (res.sessions as unknown[]).map((raw) => {\n const s = (raw ?? {}) as Record<string, unknown>;\n return {\n id: typeof s.id === 'string' ? s.id : '',\n count: typeof s.count === 'number' ? s.count : 0,\n lastTs: typeof s.lastTs === 'number' ? s.lastTs : 0,\n };\n })\n : [];\n const data = { sessions };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSessions(sessions, opts);\n return;\n }\n let probe: DaemonProbe;\n try {\n probe = await probeDaemon(opts);\n } catch {\n probe = { running: true };\n }\n if (!probe.running) {\n await withInProcessRead(opts, async (engines) => {\n const raw = engines.memory.sessions();\n const sessions: SessionRow[] = raw.map((s) => ({\n id: s.id,\n count: s.count,\n lastTs: s.lastTs,\n }));\n const data = { sessions };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSessions(sessions, opts);\n });\n return;\n }\n const res = await callDaemonTool<MemorySessionsResult | ToolFailure>(opts, 'memory_sessions');\n if (res.ok !== true) failTool('memory sessions', res, opts);\n\n const sessions: SessionRow[] = Array.isArray(res.sessions)\n ? (res.sessions as unknown[]).map((raw) => {\n const s = (raw ?? {}) as Record<string, unknown>;\n return {\n id: typeof s.id === 'string' ? s.id : '',\n count: typeof s.count === 'number' ? s.count : 0,\n lastTs: typeof s.lastTs === 'number' ? s.lastTs : 0,\n };\n })\n : [];\n const data = { sessions };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSessions(sessions, opts);\n}\n\n/** Render the sessions list (human table to stderr; --json handled by caller). */\nfunction renderSessions(sessions: SessionRow[], opts: CliOptions): void {\n log(`memory sessions — ${sessions.length} session${sessions.length === 1 ? '' : 's'}`, opts);\n table(\n sessions.map((s) => ({ Session: s.id, Observations: s.count, 'Last seen': stamp(s.lastTs) })),\n ['Session', 'Observations', 'Last seen'],\n opts,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory forget <id> [<id> ...]`\n// ---------------------------------------------------------------------------\nexport interface MemoryForgetOptions extends MemoryOptions {\n /** Positional observation ids. */\n ids: string[];\n}\n\nexport async function memoryForget(opts: MemoryForgetOptions): Promise<void> {\n if (opts.ids.length === 0) {\n fail(EXIT.USAGE, 'memory forget requires at least one <id>', opts);\n }\n const res = await callMemory<MemoryForgetResult | ToolFailure>(opts, 'memory_forget', {\n ids: opts.ids,\n });\n if (res.ok !== true) failTool('memory forget', res, opts);\n\n const deleted = typeof res.deleted === 'number' ? res.deleted : 0;\n const data = { deleted, ids: opts.ids };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(`Forgot ${deleted} observation${deleted === 1 ? '' : 's'}.`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory consolidate [--types <csv>] [--limit N]`\n// ---------------------------------------------------------------------------\nexport interface MemoryConsolidateOptions extends MemoryOptions {\n /** Comma-separated type filter (mapped to the daemon's `types[]`). */\n types?: string;\n limit?: string;\n}\n\nexport async function memoryConsolidate(opts: MemoryConsolidateOptions): Promise<void> {\n // Capability discovery (spec: \"only if the daemon exposes it — else a clear\n // message\"): a daemon that didn't opt into consolidation registers no\n // `memory_consolidate` tool. Detecting that via listTools lets us say so\n // honestly instead of calling a missing tool (which would mis-map to exit 4).\n const exposed = await withMemoryDaemon(opts, async (caller) => caller.listTools());\n if (!exposed.includes('memory_consolidate')) {\n fail(\n EXIT.ERROR,\n resolveWorkspace() !== null\n ? 'memory consolidate is not supported on shared workspaces (the workspace memory store has no consolidation model).'\n : 'memory consolidate: the daemon does not expose the memory_consolidate tool. Enable it in .noir/config under memory.consolidation (enabled: true + a provider + model), then restart the daemon.',\n opts,\n );\n }\n\n const types = splitCsv(opts.types);\n const limit = parseLimit(opts.limit, 'memory consolidate', opts);\n const args: Record<string, unknown> = {};\n if (types !== undefined) args.types = types;\n if (limit !== undefined) args.limit = limit;\n\n const res = await callMemory<MemoryConsolidateOk | MemoryConsolidateRefusal | ToolFailure>(\n opts,\n 'memory_consolidate',\n args,\n );\n if (res.ok === true) {\n const lessons = Array.isArray(res.lessons) ? (res.lessons as unknown[]) : [];\n const from = Array.isArray(res.from) ? (res.from as unknown[]) : [];\n const data = { lessons, from };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(\n `Consolidated ${lessons.length} lesson${lessons.length === 1 ? '' : 's'} from ${from.length} observation${from.length === 1 ? '' : 's'}.`,\n opts,\n );\n for (let i = 0; i < lessons.length; i++) {\n const l = lessons[i];\n if (l === undefined) continue;\n const obs = (l ?? {}) as Record<string, unknown>;\n const id = typeof obs.id === 'string' ? obs.id : `<lesson ${i + 1}>`;\n const content = typeof obs.content === 'string' ? obs.content : '';\n log(`- ${id}: ${content}`, opts);\n }\n return;\n }\n\n // Refusal (no-provider / model-unavailable / no-candidates) or a degraded\n // envelope. Both are honest exit-1 outcomes — consolidation did NOT run. Read\n // via a record cast so the picks are valid across the refusal + ToolFailure\n // union members (they don't all carry `error` / `degraded`).\n const env = res as unknown as Record<string, unknown>;\n const reason =\n (typeof env.reason === 'string' ? env.reason : undefined) ??\n (typeof env.error === 'string' ? env.error : undefined) ??\n 'unknown';\n const logged = env.logged === true || env.degraded === true;\n const suffix = logged ? ' (logged)' : '';\n fail(EXIT.ERROR, `memory consolidate did not run: ${reason}${suffix}`, opts);\n}\n","// c4-release-phase — `noir release [<version>] [--channel beta|stable]`.\n// A guided orchestrator over the existing patch-release flow (CLAUDE.md).\n// Walks the checklist; hands off at human-approval gates. The FSM release\n// phase (optional, done→released) is documented in the design spec but not\n// yet wired — this CLI is the release tool the roadmap delta called for.\n// Build-once / idempotent: bump-version.mjs is idempotent; release:tag\n// refuses dirty/unpushed; tags are immutable (never reuse — deprecate+patch).\n\nimport { execSync, spawnSync } from 'node:child_process';\nimport { type CliOptions, EXIT, fail, info, log, success, tip, warn } from '../output.js';\n\nexport interface ReleaseOptions extends CliOptions {\n version?: string;\n channel?: string;\n dryRun?: boolean;\n}\n\ntype Step =\n | 'preflight'\n | 'bump'\n | 'gate'\n | 'commit'\n | 'ci-develop'\n | 'beta-tag'\n | 'human-approve-beta'\n | 'merge-main'\n | 'ci-main'\n | 'stable-tag'\n | 'human-approve-stable'\n | 'dist-bump'\n | 'sync';\n\n/** Report what step does and who runs it. */\nfunction describe(step: Step, who: 'noir' | 'human'): string {\n const m: Record<Step, string> = {\n preflight: 'check clean tree, on develop, HEAD pushed, gate green, no version collision',\n bump: 'run bump-version.mjs + update CHANGELOG + roadmap docs',\n gate: 're-run the full gate (lint/build/typecheck/test/docs:validate)',\n commit: 'commit chore(release) + push develop',\n 'ci-develop': 'wait for CI on develop → green',\n 'beta-tag': 'pnpm release:tag → beta tag + push',\n 'human-approve-beta': 'approve the release workflow publish job in GitHub Actions',\n 'merge-main': 'merge develop → main (fast-forward) + push',\n 'ci-main': 'wait for CI on main → green',\n 'stable-tag': 'pnpm release:tag → stable tag + push',\n 'human-approve-stable': 'approve the release workflow publish job in GitHub Actions (again)',\n 'dist-bump':\n 'update Homebrew (packaging/homebrew/noir.rb) + Scoop (packaging/scoop/noir.json) from npm',\n sync: 'merge main → develop + verify both branches at same SHA',\n };\n return `${m[step]} [${who}]`;\n}\n\nconst ALL: Step[] = [\n 'preflight',\n 'bump',\n 'gate',\n 'commit',\n 'ci-develop',\n 'beta-tag',\n 'human-approve-beta',\n 'merge-main',\n 'ci-main',\n 'stable-tag',\n 'human-approve-stable',\n 'dist-bump',\n 'sync',\n];\n\nexport async function release(opts: ReleaseOptions): Promise<void> {\n if (opts.dryRun) {\n log('noir release --dry-run (no changes will be made)', opts);\n for (const s of ALL) {\n const who: 'noir' | 'human' = s.startsWith('human-approve') ? 'human' : 'noir';\n info(`${s}: ${describe(s, who)}`, opts);\n }\n tip('full release flow documented in CLAUDE.md \"Patch release flow (beta → stable)\"', opts);\n return;\n }\n\n const version = opts.version?.trim();\n if (!version) {\n fail(EXIT.USAGE, 'release: <version> is required (e.g. noir release 1.10.0)', opts);\n }\n const channel = opts.channel === 'stable' ? 'stable' : 'beta';\n\n log(`noir release ${version} (${channel} channel)`, opts);\n\n // Run each step; hand off at human gates. Failures surface clearly.\n try {\n // 1. Preflight\n info(`[1/13] preflight: ${describe('preflight', 'noir')}`, opts);\n execSync('git diff --quiet', { encoding: 'utf8' });\n const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n if (branch !== 'develop') fail(EXIT.ERROR, 'release: must be on develop', opts);\n const status = execSync('git status --porcelain', { encoding: 'utf8' }).trim();\n if (status) fail(EXIT.ERROR, 'release: tree must be clean', opts);\n success('preflight: ✓ clean, on develop', opts);\n\n // 2. Bump\n info(`[2/13] bump: ${describe('bump', 'noir')}`, opts);\n spawnSync('node', ['scripts/bump-version.mjs', version], {\n encoding: 'utf8',\n stdio: 'inherit',\n });\n success(`bump: ✓ version ${version}`, opts);\n\n // 3. Gate\n info(`[3/13] gate: ${describe('gate', 'noir')}`, opts);\n execSync('pnpm lint && pnpm build && pnpm typecheck && pnpm test && pnpm docs:validate', {\n encoding: 'utf8',\n stdio: 'inherit',\n });\n success('gate: ✓ full gate green', opts);\n\n // 4. Commit (spawnSync — no shell, no injection).\n info(`[4/13] commit/push: ${describe('commit', 'noir')}`, opts);\n spawnSync('git', ['add', '-A'], { encoding: 'utf8', stdio: 'inherit' });\n spawnSync('git', ['commit', '-m', `chore(release): v${version} + docs sync`], {\n encoding: 'utf8',\n stdio: 'inherit',\n });\n execSync('git push origin develop', { encoding: 'utf8' });\n success('commit: ✓ pushed to develop', opts);\n\n // 5-6. CI wait + beta tag\n info(`[5/13] ci-develop: waiting for CI on develop…`, opts);\n info(`[6/13] beta-tag: ${describe('beta-tag', 'noir')}`, opts);\n tip('after CI green: run `pnpm release:tag` and push the beta tag', opts);\n\n // 7. Human handoff\n warn(`[7/13] ${describe('human-approve-beta', 'human')}`, opts);\n tip('open GitHub Actions → approve the publish job, then continue', opts);\n tip('after approval: verify `npm view @noir-ai/cli dist-tags beta` = beta version', opts);\n\n // 8-13 (remaining steps — documented for human continuation)\n info('remaining steps (merge→stable→dist→sync): see CLAUDE.md', opts);\n for (const s of ALL.slice(7)) {\n const who: 'noir' | 'human' = s.startsWith('human-approve') ? 'human' : 'noir';\n info(` ${s}: ${describe(s, who)}`, opts);\n }\n\n success(`release ${version}: preflight→bump→gate→commit done. Resume after approval.`, opts);\n } catch (err) {\n fail(EXIT.ERROR, `release failed: ${err instanceof Error ? err.message : String(err)}`, opts);\n }\n}\n","// ENOENT fallback for `noir run --command <name>`: resolves a command name\n// through the user's interactive shell. `child_process.spawn()` cannot see shell\n// aliases or functions (they only exist inside an interactive shell that has\n// sourced .zshrc/.bashrc/config.fish), and PATH entries exported only in rc\n// files are invisible to non-interactive children — both surface as ENOENT.\n//\n// Safety model (the whole point of this module):\n// - the NAME is validated against a strict charset and passed ONLY via argv\n// ($1), never interpolated into the -c string;\n// - the host's flags + prompt travel only as \"$@\" / $argv positional\n// parameters, so no user-controlled text ever reaches the shell parser;\n// - the probe is a one-shot lookup with a Node-side timeout killed via the\n// process group (rc files can hang, exit, or spawn orphaned children).\n// On Windows the fallback is disabled entirely (cmd/PowerShell aliases are\n// session-scoped and unspawnable).\n\nimport { spawn } from 'node:child_process';\nimport { basename } from 'node:path';\n\nconst PROBE_TIMEOUT_MS = 3000;\n/** Strict command-name charset — anything else is never handed to a shell. */\nconst NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;\nconst SUPPORTED_SHELLS = new Set(['zsh', 'bash', 'fish']);\n\nexport interface ShellBridgeEnv {\n SHELL?: string;\n PATH?: string;\n HOME?: string;\n [key: string]: string | undefined;\n}\n\n/** What the probe found for a name, or why it bailed. */\nexport type ShellResolution =\n | { readonly kind: 'path'; readonly path: string; readonly shell: string }\n | { readonly kind: 'alias'; readonly shell: string }\n | { readonly kind: 'function'; readonly shell: string }\n | { readonly kind: 'none'; readonly shell: string }\n | { readonly kind: 'error'; readonly message: string }\n | { readonly kind: 'unsupported' };\n\nexport interface ResolveOptions {\n readonly env?: ShellBridgeEnv;\n readonly platform?: NodeJS.Platform;\n readonly timeoutMs?: number;\n}\n\n/**\n * Guard: only plain command names on a POSIX platform with a supported\n * interactive shell (zsh/bash/fish) are eligible for the shell fallback.\n */\nexport function isEligibleForShellFallback(name: string, opts: ResolveOptions = {}): boolean {\n const { env = process.env, platform = process.platform } = opts;\n if (platform === 'win32') return false;\n if (name.length === 0 || name.includes('/') || !NAME_RE.test(name)) return false;\n const shell = env.SHELL;\n if (!shell || shell.length === 0) return false;\n return SUPPORTED_SHELLS.has(basename(shell).toLowerCase());\n}\n\n/** Extract the LAST `N:` (resolved path) and `K:` (kind) sentinel lines. */\nexport function parseProbeOutput(stdout: string): { path: string; kind: string } {\n let path = '';\n let kind = '';\n for (const line of stdout.split('\\n')) {\n if (line.startsWith('N:')) path = line.slice(2).trim();\n else if (line.startsWith('K:')) kind = line.slice(2).trim();\n }\n return { path, kind };\n}\n\n/** Map probe output onto a spawnable outcome (PATH wins over kind). */\nexport function classifyResolution(\n path: string,\n kind: string,\n): 'path' | 'alias' | 'function' | 'none' {\n if (path.trim().startsWith('/')) return 'path';\n const k = kind.trim().toLowerCase();\n if (k.includes('alias')) return 'alias';\n if (k.includes('function')) return 'function';\n return 'none';\n}\n\nfunction shellKind(shell: string): 'zsh' | 'bash' | 'fish' | null {\n const kind = basename(shell).toLowerCase();\n return (SUPPORTED_SHELLS.has(kind) ? kind : null) as 'zsh' | 'bash' | 'fish' | null;\n}\n\n/** Login+interactive flags per shell. fish has no separate login mode — `-i -c`\n * sources config.fish; zsh/bash use `-lic` (login+interactive) so rc files that\n * only load in login shells (PATH via .zprofile / /etc/zprofile) are visible. */\nconst PROBE_FLAGS: Record<'zsh' | 'bash' | 'fish', string> = {\n zsh: '-lic',\n bash: '-lic',\n fish: '-ic',\n};\n\n/**\n * One-shot probe scripts. The NAME is always read from `$1`/`$argv[1]` (argv),\n * never written into the script. bash login shells skip .bashrc, so it is\n * sourced explicitly (double-source is harmless). stdout noise (rc banners,\n * \"no job control\") is ignored by sentinel parsing.\n */\nconst PROBE_SCRIPTS: Record<'zsh' | 'bash' | 'fish', string> = {\n zsh: 'printf \"\\\\nN:%s\" \"$(whence -p \"$1\")\"; printf \"\\\\nK:%s\" \"$(whence -w \"$1\")\"',\n bash: 'shopt -s expand_aliases; [ -r \"$HOME/.bashrc\" ] && . \"$HOME/.bashrc\" 2>/dev/null; printf \"\\\\nN:%s\" \"$(type -P \"$1\")\"; printf \"\\\\nK:%s\" \"$(type -t \"$1\")\"',\n fish: 'printf \"\\\\nN:%s\" (type -P \"$argv[1]\"); printf \"\\\\nK:%s\" (type -t \"$argv[1]\")',\n};\n\n/** Run the probe; always resolves (never rejects), reporting a timeout. */\nfunction probeCommand(\n name: string,\n shell: string,\n opts: ResolveOptions,\n): Promise<{ stdout: string; timedOut: boolean }> {\n const kind = shellKind(shell);\n const timeoutMs = opts.timeoutMs ?? PROBE_TIMEOUT_MS;\n return new Promise((resolve) => {\n const script = kind ? PROBE_SCRIPTS[kind] : '';\n const flags = kind ? PROBE_FLAGS[kind] : '-lic';\n // bash/zsh consume a positional `$0` (the `noirbridge` dummy) and read the\n // command name from `$1`; fish has NO `$0` — its positional args ARE `$argv`\n // starting at $argv[1] — so the dummy must be omitted there or $argv[1] would\n // resolve the literal \"noirbridge\" and the probe would always come back empty.\n const probeArgs = kind === 'fish' ? [name] : ['noirbridge', name];\n const child = spawn(shell, [flags, script, ...probeArgs], {\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: true, // new process group so the timeout can kill rc-spawned children\n ...(opts.env ? { env: opts.env as NodeJS.ProcessEnv } : {}),\n });\n let stdout = '';\n child.stdout.setEncoding('utf8');\n child.stdout.on('data', (c: string) => {\n stdout += c;\n });\n child.stderr.resume(); // rc noise (e.g. \"no job control\") — ignored\n const timer = setTimeout(() => {\n if (child.pid) {\n try {\n process.kill(-child.pid, 'SIGKILL'); // orphan guard: group, not just shell\n } catch {\n /* already gone */\n }\n }\n resolve({ stdout, timedOut: true });\n }, timeoutMs);\n child.on('error', () => {\n clearTimeout(timer);\n resolve({ stdout, timedOut: false });\n });\n child.on('close', () => {\n clearTimeout(timer);\n resolve({ stdout, timedOut: false });\n });\n });\n}\n\n/** Cache resolved absolute paths per (shell, name) so repeated runs skip the probe. */\nconst pathCache = new Map<string, string>();\n\n/**\n * Resolve `name` through the user's interactive shell. Returns `path` for a\n * PATH executable (respawn directly, clean stream-json pipe), `alias`/`function`\n * for shell entities (bridge execution needed), `none` for a genuine miss,\n * `error` for a probe timeout, or `unsupported` when the guards reject.\n */\nexport async function resolveCommandViaShell(\n name: string,\n opts: ResolveOptions = {},\n): Promise<ShellResolution> {\n const { env = process.env } = opts;\n if (!isEligibleForShellFallback(name, opts)) return { kind: 'unsupported' };\n const shell = env.SHELL as string;\n const cached = pathCache.get(`${shell}:${name}`);\n if (cached) return { kind: 'path', path: cached, shell };\n const probe = await probeCommand(name, shell, opts);\n if (probe.timedOut) {\n return {\n kind: 'error',\n message: `shell resolution timed out after ${opts.timeoutMs ?? PROBE_TIMEOUT_MS}ms`,\n };\n }\n const { path, kind } = parseProbeOutput(probe.stdout);\n const cls = classifyResolution(path, kind);\n if (cls === 'path') {\n pathCache.set(`${shell}:${name}`, path);\n return { kind: 'path', path, shell };\n }\n if (cls === 'alias' || cls === 'function') return { kind: cls, shell };\n return { kind: 'none', shell };\n}\n\nexport interface BridgeSpec {\n readonly binary: string;\n readonly args: readonly string[];\n}\n\n/**\n * Build the bridge spawn for an alias/function: run the user's interactive\n * shell and execute `NAME \"$@\"` (fish: `NAME $argv`). The host's flags + prompt\n * are appended as positional args after the dummy `$0` (`noirbridge`) — they are\n * forwarded verbatim by the shell and never parsed. bash re-sources .bashrc\n * (login shells skip it) so the alias/function defined there is available.\n */\nexport function buildBridgeArgs(\n name: string,\n hostArgs: readonly string[],\n shell: string,\n): BridgeSpec {\n const kind = shellKind(shell) ?? 'zsh';\n if (kind === 'fish') {\n // fish has no `$0`; positional args ARE `$argv` from $argv[1], so no dummy.\n return { binary: shell, args: ['-ic', `${name} $argv`, ...hostArgs] };\n }\n if (kind === 'bash') {\n return {\n binary: shell,\n args: [\n '-lic',\n `shopt -s expand_aliases; [ -r \"$HOME/.bashrc\" ] && . \"$HOME/.bashrc\" 2>/dev/null; ${name} \"$@\"`,\n 'noirbridge',\n ...hostArgs,\n ],\n };\n }\n return { binary: shell, args: ['-lic', `${name} \"$@\"`, 'noirbridge', ...hostArgs] };\n}\n","// v2 — the host orchestrator (Archetype B). Drives the host agentic CLI as a\n// headless subprocess and consumes its `stream-json` event stream, rather than\n// Noir running its own model + tool loop (which D5 forbids).\n//\n// This module is the PURE, testable core:\n// - `resolveHostRun` — the spawn command for a host (custom binary wins over\n// the host default, so users with multiple profiles — e.g. `claude` vs\n// `claude-work` — can point Noir at their own binary without a restart).\n// - `parseStreamLine` / `normalizeStreamEvent` — map a raw `stream-json` line\n// to a small `NoirEvent` union (the `init`/`assistant`/`result` payloads are\n// the stable, shared contract across hosts).\n// - `UsageReducer` — accumulates token/cost with the `max usage per\n// message.id` rule (Claude emits one JSONL line PER content block of an\n// assistant message, each line's `usage` a CUMULATIVE snapshot of that\n// message — summing lines over-counts ~2.5-3x).\n// - `runHost` — the spawn + readline integration over that core.\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport { createInterface } from 'node:readline';\nimport type { HostId } from '@noir-ai/adapters';\nimport { buildBridgeArgs, resolveCommandViaShell } from './shell-bridge.js';\n\n/** A resolved spawn command: binary + the headless flags appended before prompt. */\nexport interface HostRunSpec {\n readonly binary: string;\n readonly flags: readonly string[];\n}\n\n/** Default headless binary per host. `null` = not a spawnable CLI. */\nconst HOST_BINARY: Record<HostId, string | null> = {\n claude: 'claude',\n gemini: 'gemini',\n opencode: 'opencode',\n cursor: 'cursor-agent',\n 'agents-md': null, // emits AGENTS.md — a file host, not a spawnable CLI\n};\n\n/**\n * Headless flags per host. `claude` is the regression anchor (fully specified);\n * the others use the same `-p --output-format stream-json` contract where the\n * host supports it, and remain overridable via the custom-binary path (D2a).\n */\nconst HOST_FLAGS: Record<HostId, readonly string[]> = {\n claude: ['-p', '--output-format', 'stream-json', '--verbose'],\n gemini: ['-p', '--output-format', 'stream-json'],\n opencode: ['run'],\n cursor: ['-p'],\n 'agents-md': [],\n};\n\n/**\n * Resolve the spawn command for `host`. `customBinary` (the user's own profile\n * command, e.g. `claude-work`) wins over the host default. Returns `null` when\n * the host has no spawnable CLI (agents-md).\n */\nexport function resolveHostRun(host: HostId, customBinary?: string): HostRunSpec | null {\n const binary = customBinary && customBinary.length > 0 ? customBinary : HOST_BINARY[host];\n if (!binary) return null;\n return { binary, flags: HOST_FLAGS[host] };\n}\n\n// ---------------------------------------------------------------------------\n// Event normalization\n// ---------------------------------------------------------------------------\n\n/** A token-usage snapshot (the fields of the stream-json `usage` object). */\nexport interface TokenUsage {\n readonly inputTokens?: number;\n readonly outputTokens?: number;\n readonly cacheReadTokens?: number;\n}\n\n/** The normalized event union — the shared, host-agnostic payload. */\nexport type NoirEvent =\n | { readonly kind: 'init'; readonly sessionId?: string; readonly model?: string }\n | {\n readonly kind: 'assistant';\n readonly messageId?: string;\n readonly text?: string;\n readonly usage?: TokenUsage;\n /** Set when the host flags the assistant message as an API error\n * (`is_api_error_message:true` or an `error` category string) — its text\n * is a diagnostic, never the answer. */\n readonly isError?: boolean;\n /** The host's error category string (e.g. \"authentication_failed\") when\n * the assistant event carries one — used for actionable auth guidance. */\n readonly errorCategory?: string;\n }\n | {\n readonly kind: 'result';\n readonly isError: boolean;\n readonly durationMs?: number;\n readonly numTurns?: number;\n readonly totalCostUsd?: number;\n readonly usage?: TokenUsage;\n }\n | { readonly kind: 'other' };\n\nfunction asObj(v: unknown): Record<string, unknown> | null {\n return typeof v === 'object' && v !== null ? (v as Record<string, unknown>) : null;\n}\n\nfunction str(v: unknown): string | undefined {\n return typeof v === 'string' ? v : undefined;\n}\n\nfunction num(v: unknown): number | undefined {\n return typeof v === 'number' && Number.isFinite(v) ? v : undefined;\n}\n\n/** Map a POSIX signal name (e.g. \"SIGKILL\") to its numeric value, else undefined. */\nfunction signalNumber(signal: NodeJS.Signals): number | undefined {\n const n = (constants.signals as Record<string, number>)[signal];\n return typeof n === 'number' ? n : undefined;\n}\n\nfunction usageFrom(v: unknown): TokenUsage | undefined {\n const o = asObj(v);\n if (!o) return undefined;\n const input = num(o.input_tokens);\n const output = num(o.output_tokens);\n const cache = num(o.cache_read_input_tokens) ?? num(o.cache_creation_input_tokens);\n if (input === undefined && output === undefined && cache === undefined) return undefined;\n return { inputTokens: input, outputTokens: output, cacheReadTokens: cache };\n}\n\n/** Extract the text delta from an assistant message's content (best-effort). */\nfunction messageText(message: Record<string, unknown> | null): string | undefined {\n if (!message) return undefined;\n const content = message.content;\n if (typeof content === 'string') return content;\n if (Array.isArray(content)) {\n const parts = content\n .map((block) => {\n const b = asObj(block);\n return b && typeof b.text === 'string' ? (b.text as string) : null;\n })\n .filter((t): t is string => t !== null);\n if (parts.length > 0) return parts.join('');\n }\n return undefined;\n}\n\n/** Parse a raw stream-json line to a JSON value, or `null` for blank/invalid. */\nexport function parseStreamLine(line: string): unknown | null {\n const trimmed = line.trim();\n if (trimmed.length === 0) return null;\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n}\n\n/** Normalize a raw stream-json event to a {@link NoirEvent}, or `null`. */\nexport function normalizeStreamEvent(raw: unknown): NoirEvent | null {\n const r = asObj(raw);\n if (!r) return null;\n const type = r.type;\n if (type === 'system') {\n if (r.subtype === 'init') {\n return { kind: 'init', sessionId: str(r.session_id), model: str(r.model) };\n }\n return { kind: 'other' };\n }\n if (type === 'assistant') {\n const message = asObj(r.message);\n return {\n kind: 'assistant',\n messageId: message ? str(message.id) : undefined,\n text: messageText(message),\n usage: message ? usageFrom(message.usage) : undefined,\n // An API error message is a diagnostic, not the answer — flag it so the\n // caller never streams it as assistant text (optional-when-true keeps\n // exact-shape consumers of the existing union intact).\n ...(r.is_api_error_message === true || typeof r.error === 'string'\n ? {\n isError: true as const,\n ...(typeof r.error === 'string' ? { errorCategory: r.error } : {}),\n }\n : {}),\n };\n }\n if (type === 'result') {\n return {\n kind: 'result',\n isError: r.is_error === true,\n durationMs: num(r.duration_ms),\n numTurns: num(r.num_turns),\n totalCostUsd: num(r.total_cost_usd),\n usage: usageFrom(r.usage),\n };\n }\n return { kind: 'other' };\n}\n\n// ---------------------------------------------------------------------------\n// Usage reduction\n// ---------------------------------------------------------------------------\n\n/** A monotonic token/cost accumulator snapshot. */\nexport interface UsageSnapshot {\n readonly inputTokens: number;\n readonly outputTokens: number;\n readonly totalCostUsd: number;\n readonly numTurns: number;\n}\n\n/**\n * Accumulates token/cost from a `stream-json` feed with the dedup rule: take\n * the `max` usage per `message.id`, never sum lines. Claude emits one line per\n * content block of an assistant message, each carrying that message's\n * CUMULATIVE usage — so the correct per-message total is the max, and the\n * session total is the sum of per-message maxes.\n */\nexport class UsageReducer {\n private readonly maxByMessage = new Map<string, { input: number; output: number }>();\n private input = 0;\n private output = 0;\n private costUsd = 0;\n private turns = 0;\n\n /** Feed one normalized event into the reducer. */\n add(event: NoirEvent): void {\n if (event.kind === 'assistant' && event.messageId && event.usage) {\n const key = event.messageId;\n const input = event.usage.inputTokens ?? 0;\n const output = event.usage.outputTokens ?? 0;\n const prev = this.maxByMessage.get(key);\n if (!prev) {\n this.maxByMessage.set(key, { input, output });\n this.input += input;\n this.output += output;\n } else {\n // Cumulative snapshot — only the growth over the last-seen max counts.\n this.input += Math.max(0, input - prev.input);\n this.output += Math.max(0, output - prev.output);\n this.maxByMessage.set(key, {\n input: Math.max(prev.input, input),\n output: Math.max(prev.output, output),\n });\n }\n } else if (event.kind === 'result') {\n if (event.totalCostUsd !== undefined) this.costUsd = event.totalCostUsd;\n if (event.numTurns !== undefined) this.turns = event.numTurns;\n }\n }\n\n /** The current accumulator snapshot. */\n snapshot(): UsageSnapshot {\n return {\n inputTokens: this.input,\n outputTokens: this.output,\n totalCostUsd: this.costUsd,\n numTurns: this.turns,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Spawn integration\n// ---------------------------------------------------------------------------\n\nexport interface RunHostOptions {\n readonly host: HostId;\n readonly prompt: string;\n readonly customBinary?: string;\n /** Extra args appended after the host's headless flags (run-profile `args`). */\n readonly extraArgs?: readonly string[];\n /** Environment for the host spawn + shell fallback (defaults to process.env). */\n readonly env?: Record<string, string | undefined>;\n /** Raw stream-json line (for transcript persistence). */\n onLine?: (line: string) => void;\n /** Normalized event (for streaming render). */\n onEvent?: (event: NoirEvent) => void;\n}\n\nexport interface RunHostResult {\n readonly exitCode: number;\n readonly usage: UsageSnapshot;\n readonly eventCount: number;\n /** Host stderr (surfaced on a non-zero exit so errors are not swallowed). */\n readonly stderr: string;\n /** Whether the host signalled an error (non-zero exit OR a stream-json\n * `is_error:true` result / API-error assistant event). Exit code alone is\n * not reliable — claude can exit 0 with `is_error:true` (#79500). */\n readonly isError: boolean;\n /** The first API-error assistant text (e.g. \"Not logged in · Please run\n * /login\"), quoted into the CLI's failure message. */\n readonly errorText?: string;\n /** The first API-error category (e.g. \"authentication_failed\"), for\n * actionable auth guidance in the CLI. */\n readonly errorCategory?: string;\n}\n\n/**\n * Spawn the host headless and consume its `stream-json` over a stdio pipe.\n * Rejects when the host has no spawnable CLI or the binary fails to spawn;\n * otherwise resolves with the exit code + the reduced usage snapshot.\n */\nexport function runHost(opts: RunHostOptions): Promise<RunHostResult> {\n const spec = resolveHostRun(opts.host, opts.customBinary);\n if (!spec) {\n return Promise.reject(\n new Error(`host '${opts.host}' is not a spawnable CLI (no default command)`),\n );\n }\n return spawnAndConsume(\n spec.binary,\n [...spec.flags, ...(opts.extraArgs ?? []), opts.prompt],\n opts,\n false,\n );\n}\n\n/**\n * Spawn `binary` with `args` and consume its stream-json. On an ENOENT spawn\n * error (and not already a shell-bridge run) it attempts the shell fallback:\n * the name may be an alias/function or a PATH entry only visible inside the\n * user's interactive shell — see `shell-bridge.ts` for the safety model.\n */\nfunction spawnAndConsume(\n binary: string,\n args: readonly string[],\n opts: RunHostOptions,\n shellRun: boolean,\n): Promise<RunHostResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(binary, [...args], {\n stdio: ['ignore', 'pipe', 'pipe'],\n ...(opts.env ? { env: opts.env as NodeJS.ProcessEnv } : {}),\n });\n const reducer = new UsageReducer();\n let eventCount = 0;\n let stderrBuf = '';\n let errored = false;\n let errorText: string | undefined;\n let errorCategory: string | undefined;\n // A failed spawn fires BOTH 'error' and 'close' (close with a synthetic\n // code like -2). The 'close' handler must defer to the 'error' path so the\n // shell fallback (or the rejection) owns the resolution, never a -2 result.\n let spawnError: unknown = null;\n\n child.on('error', (err) => {\n spawnError = err;\n if (!shellRun && (err as NodeJS.ErrnoException).code === 'ENOENT') {\n void shellFallback(binary, args, opts, resolve, reject, err);\n return;\n }\n reject(err);\n });\n\n const rl = createInterface({ input: child.stdout });\n rl.on('line', (line) => {\n opts.onLine?.(line);\n const raw = parseStreamLine(line);\n if (raw === null) return;\n const event = normalizeStreamEvent(raw);\n if (event === null) return;\n eventCount += 1;\n if (event.kind === 'assistant' && event.isError === true) {\n errored = true;\n if (errorText === undefined && event.text && event.text.length > 0) errorText = event.text;\n if (errorCategory === undefined && event.errorCategory) errorCategory = event.errorCategory;\n } else if (event.kind === 'result' && event.isError === true) {\n errored = true;\n }\n reducer.add(event);\n opts.onEvent?.(event);\n });\n\n child.stderr.setEncoding('utf8');\n child.stderr.on('data', (chunk: string) => {\n stderrBuf += chunk;\n });\n\n child.on('close', (code, signal) => {\n if (spawnError !== null) return; // the error/fallback path owns this spawn\n // A host terminated by a signal (crash/SIGKILL/OOM) yields code === null\n // with no result event — without this it would be reported as exit 0\n // success. Fold the kill into a non-zero exit + isError (128+n is the\n // shell convention for signal termination).\n const killed = code === null && signal != null;\n resolve({\n exitCode: killed ? 128 + (signalNumber(signal) ?? 0) : (code ?? 0),\n usage: reducer.snapshot(),\n eventCount,\n stderr: stderrBuf,\n isError: errored || killed,\n errorText: killed && errorText === undefined ? `terminated by signal ${signal}` : errorText,\n errorCategory,\n });\n });\n });\n}\n\n/**\n * ENOENT shell-bridge attempt: probe the user's shell for `binary`; respawn the\n * resolved absolute path directly, or bridge an alias/function via the shell\n * (prompt + flags travel only as argv). On any failure re-reject the original\n * ENOENT so run.ts builds the friendly \"no executable found\" message.\n */\nasync function shellFallback(\n binary: string,\n args: readonly string[],\n opts: RunHostOptions,\n resolve: (r: RunHostResult) => void,\n reject: (e: unknown) => void,\n originalErr: unknown,\n): Promise<void> {\n try {\n const res = await resolveCommandViaShell(binary, { env: opts.env ?? process.env });\n if (res.kind === 'path') {\n resolve(await spawnAndConsume(res.path, args, opts, true));\n return;\n }\n if (res.kind === 'alias' || res.kind === 'function') {\n const bridge = buildBridgeArgs(binary, args, res.shell);\n resolve(await spawnAndConsume(bridge.binary, bridge.args, opts, true));\n return;\n }\n } catch {\n // fall through to the original ENOENT below\n }\n reject(originalErr);\n}\n","// Run profiles (D2c → `run.profiles` in .noir/config.yml): named host-binary\n// bundles selected by `--profile` / NOIR_PROFILE / run.defaultProfile, modeled\n// on the ssh_config / VS-Code-terminal-profiles precedent. Selection precedence\n// is the universal ladder: explicit flag > env var > persisted config default >\n// built-in default. An explicitly named profile that does not exist is a hard\n// error that lists the available names (botocore semantics + inline listing).\n//\n// Security doctrine: `env` values may reference `${VAR}` from Noir's process\n// environment and `null` deletes a variable from the child env — .noir/config.yml\n// is committable project state, so literal secrets must never be stored there.\n\nimport type { NoirConfig } from '@noir-ai/core';\nimport { loadProjectInfo } from '@noir-ai/core';\n\nexport interface ProfileResolution {\n readonly binary?: string;\n readonly env?: Record<string, string | undefined>;\n readonly args?: readonly string[];\n readonly profileName?: string;\n}\n\nexport type ProfileResolutionResult =\n | { readonly ok: true; readonly profile: ProfileResolution }\n | { readonly ok: false; readonly message: string };\n\n/** Best-effort project config load — `noir run` keeps working outside a project. */\nexport function loadRunConfig(root: string): NoirConfig | null {\n try {\n return loadProjectInfo(root).config;\n } catch {\n return null;\n }\n}\n\n/** Expand `${NAME}` references from `env`; unresolved references stay literal. */\nexport function expandEnvVars(value: string, env: Record<string, string | undefined>): string {\n return value.replace(/\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (match, name: string) =>\n env[name] !== undefined ? (env[name] as string) : match,\n );\n}\n\n/**\n * Resolve the effective profile. `requested` is the `--profile` flag value.\n * Precedence: requested > NOIR_PROFILE env > run.defaultProfile > none.\n */\nexport function resolveRunProfile(\n requested: string | undefined,\n config: NoirConfig,\n env: Record<string, string | undefined>,\n): ProfileResolutionResult {\n const profiles = config.run?.profiles ?? {};\n const names = Object.keys(profiles);\n const name = requested ?? env.NOIR_PROFILE ?? config.run?.defaultProfile;\n if (name === undefined || name.length === 0) {\n return { ok: true, profile: {} }; // built-in host default, unchanged\n }\n const profile = profiles[name];\n if (!profile) {\n const available =\n names.length > 0\n ? names.join(', ')\n : '(none defined — add a run.profiles block to .noir/config.yml)';\n return {\n ok: false,\n message: `unknown run profile \"${name}\" — no profile named \"${name}\" is defined under run.profiles in .noir/config.yml. Available: ${available}`,\n };\n }\n const envExpanded: Record<string, string | undefined> = {};\n if (profile.env) {\n for (const [k, v] of Object.entries(profile.env)) {\n envExpanded[k] = v === null ? undefined : expandEnvVars(v, env);\n }\n }\n return {\n ok: true,\n profile: { binary: profile.binary, env: envExpanded, args: profile.args, profileName: name },\n };\n}\n\n/** Rows for `noir run --list-profiles`. Keys match the table() column labels. */\nexport function listProfiles(\n config: NoirConfig,\n): Array<{ NAME: string; DEFAULT: string; BINARY: string }> {\n const profiles = config.run?.profiles ?? {};\n const def = config.run?.defaultProfile;\n return Object.keys(profiles).map((name) => ({\n NAME: name,\n DEFAULT: name === def ? '*' : '',\n BINARY: profiles[name]?.binary ?? '',\n }));\n}\n","// v2 — `noir run <prompt>`: drive the host agentic CLI headless and render its\n// stream-json. This is the programmatic host-driving line (roadmap v2.0) and the\n// v2 orchestrator surface: Noir spawns the host as a subprocess (Archetype B),\n// streams its output, and reports the token/cost from the `result` event — it\n// never runs its own model + tool loop (D5).\n//\n// Custom command (D2a): `--command <binary>` lets users with multiple host\n// profiles (e.g. two Claude Code installs, `claude` vs `claude-work`) point the\n// orchestrator at their own binary without restarting the terminal. The host\n// default is used when `--command` is absent.\n//\n// Contract: `--json` emits one `{ok,data}` envelope to stdout (scriptable);\n// otherwise the host's assistant text streams to stdout and the token/cost\n// summary + transcript path go to stderr. A transcript of the raw stream-json is\n// always persisted to `.noir/transcripts/`.\n\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type HostId, SUPPORTED_HOSTS } from '@noir-ai/adapters';\nimport { loadNoirEnv, NOIR_DIR, parseConfig } from '@noir-ai/core';\nimport {\n type NoirEvent,\n type RunHostResult,\n runHost,\n type UsageSnapshot,\n} from '../orchestrator.js';\nimport { type CliOptions, EXIT, fail, json, log, success } from '../output.js';\nimport { loadRunConfig, resolveRunProfile } from '../run-profiles.js';\n\n/**\n * Anthropic credential / gateway variables that change how a headless (`-p`)\n * host run authenticates. A custom gateway sets `ANTHROPIC_AUTH_TOKEN` +\n * `ANTHROPIC_BASE_URL` and fails exactly like a stale API key, so the auth\n * advice must cover every recognised shape — not just `ANTHROPIC_API_KEY`.\n */\nconst CREDENTIAL_ENV_VARS = [\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_AUTH_TOKEN',\n 'ANTHROPIC_BASE_URL',\n] as const;\n\n/**\n * The auth addendum that names each credential variable in effect AND the\n * source that won for it (`.noir/.env` vs the environment), so the \"unset it\"\n * advice stays actionable: after consolidation `applyNoirEnv` re-injects a\n * file-scoped key on every invocation, so shell-level unsetting does nothing —\n * the message must point at the file (spec 13.2).\n *\n * NAMES AND SOURCES ONLY (spec G4): the message is loggable and shareable, so\n * no branch here may ever print a variable's value. Returns `''` when no\n * recognised credential variable is set.\n */\nfunction credentialNote(\n env: Record<string, string | undefined>,\n sources: Record<string, 'file' | 'env'>,\n): string {\n const named = CREDENTIAL_ENV_VARS.filter((name) => {\n const value = env[name];\n return value !== undefined && value.length > 0;\n }).map(\n (name) => `${name} (from ${sources[name] === 'file' ? `${NOIR_DIR}/.env` : 'the environment'})`,\n );\n if (named.length === 0) return '';\n const one = named.length === 1;\n const subject = one ? `${named[0]} is set` : `${named.join(', ')} are set`;\n const pronoun = one ? 'it' : 'them';\n const verb = one ? 'overrides' : 'override';\n return (\n ` Note: ${subject} — ${pronoun} ${verb} the logged-in account in \\`-p\\` mode; ` +\n `unset ${pronoun} in the named source (or fix the value) if you meant to use your subscription.`\n );\n}\n\n/** Options accepted by `noir run` (globals + host/command/profile knobs). */\nexport interface RunOptions extends CliOptions {\n /** Host to drive (default `claude`). */\n readonly host?: string;\n /** Custom host binary overriding the per-host default (D2a). */\n readonly command?: string;\n /** Named run profile from .noir/config.yml `run.profiles` (D). */\n readonly profile?: string;\n}\n\n/** A single-row token/cost summary (human-readable). */\nfunction formatUsage(u: UsageSnapshot): string {\n const cost = u.totalCostUsd > 0 ? ` $${u.totalCostUsd.toFixed(2)}` : '';\n const turns = u.numTurns > 0 ? ` · ${u.numTurns} turns` : '';\n return `${u.inputTokens.toLocaleString()} in / ${u.outputTokens.toLocaleString()} out${cost}${turns}`;\n}\n\n/**\n * Stream an assistant text delta to stdout (non-json mode only). The host's\n * answer is data, so it goes to stdout; diagnostics go to stderr.\n */\nfunction streamEvent(event: NoirEvent, opts: RunOptions): void {\n if (opts.json === true) return; // json mode buffers; no streaming writes\n // API-error assistant text (e.g. \"Not logged in · Please run /login\") is a\n // diagnostic, not the answer — never stream it to the data channel.\n if (event.kind === 'assistant' && event.text && event.text.length > 0 && event.isError !== true) {\n process.stdout.write(event.text);\n }\n}\n\n/**\n * Run the host headless and report. Rejects are translated to a clean CLI\n * failure (exit 1) rather than an unhandled rejection.\n */\nexport async function run(prompt: string, opts: RunOptions): Promise<void> {\n const host = (opts.host ?? 'claude') as HostId;\n if (!(SUPPORTED_HOSTS as readonly string[]).includes(host)) {\n fail(EXIT.USAGE, `unknown host '${host}' (supported: ${SUPPORTED_HOSTS.join(', ')})`, opts);\n }\n if (prompt.length === 0) {\n const selectors: string[] = [];\n if (opts.command) selectors.push(`--command ${opts.command}`);\n if (opts.profile) selectors.push(`--profile ${opts.profile}`);\n const usage =\n selectors.length > 0 ? `noir run ${selectors.join(' ')} <prompt>` : 'noir run <prompt>';\n fail(EXIT.USAGE, `a prompt is required: \\`${usage}\\``, opts);\n }\n\n const root = process.cwd();\n // Credential provenance for the failure advice: `sources` records, per key,\n // whether `.noir/.env` or the ambient environment won (spec 12.4). Names\n // only — never a value. Read from `process.env`, which the bin's preAction\n // has already overlaid with `.noir/.env` (spec 12.1: the file wins).\n const envSources = loadNoirEnv(root).sources;\n\n // Run-profile resolution: --profile > NOIR_PROFILE > run.defaultProfile >\n // built-in default. Config load is best-effort — `noir run` keeps working\n // outside an initialized project (no profiles, built-in host behavior).\n const config = loadRunConfig(root) ?? parseConfig({});\n const resolved = resolveRunProfile(opts.profile, config, process.env);\n if (!resolved.ok) fail(EXIT.USAGE, resolved.message, opts);\n const profile = resolved.profile;\n // An explicit `--command` (per-invocation override) wins over a profile's\n // binary; the profile's binary is the fallback when --command is absent.\n const customBinary = opts.command ?? profile.binary;\n const extraArgs = profile.args;\n const env = profile.env ? mergeEnv(process.env, profile.env) : undefined;\n\n // Spec 13.3: running outside an initialized project stays supported, but the\n // gap deserves a word — with no `.noir/.env` there are no project\n // credentials, so an auth failure would read as \"noir is broken\" instead of\n // \"this project has none\". One informational stderr line, BEFORE the spawn:\n // never a failure, never a prompt, and silenced by `log()` under\n // --json/--quiet so a machine consumer's output stays pristine.\n if (!existsSync(join(root, NOIR_DIR, '.env'))) {\n log(\n `This project has no ${NOIR_DIR}/.env — that is where Noir reads project credentials and ` +\n `run config from. Run \\`noir init\\` to scaffold one (\\`noir run\\` works without it).`,\n opts,\n );\n }\n\n const transcriptLines: string[] = [];\n\n let result: RunHostResult;\n try {\n result = await runHost({\n host,\n prompt,\n customBinary,\n extraArgs,\n env,\n onLine: (line) => transcriptLines.push(line),\n onEvent: (event) => streamEvent(event, opts),\n });\n } catch (err) {\n const binary = customBinary && customBinary.length > 0 ? customBinary : host;\n const detail = err instanceof Error ? err.message : String(err);\n const enoent = (err as NodeJS.ErrnoException)?.code === 'ENOENT';\n const guidance = enoent\n ? ` No executable '${binary}' was found. Shell aliases and functions (e.g. from .zshrc) are invisible to noir — use an executable on PATH, an absolute path, or a launcher script such as ~/.local/bin/${binary}.`\n : '';\n const subject =\n customBinary && customBinary.length > 0\n ? `custom command '${customBinary}'`\n : `host '${host}'`;\n fail(EXIT.ERROR, `failed to run ${subject}: ${detail}.${guidance}`, opts);\n }\n\n const transcript = writeTranscript(host, transcriptLines);\n const failed = result.exitCode !== 0 || result.isError;\n\n if (failed) {\n // A failed host run is an error, not a success: exit 1, {ok:false} under\n // --json, and no misleading \"usage\" line. The raw stream-json transcript is\n // still persisted (it is the audit record) and referenced in the message.\n const binary = customBinary && customBinary.length > 0 ? customBinary : host;\n const reason =\n result.errorText && result.errorText.trim().length > 0\n ? result.errorText.trim()\n : result.stderr.trim() || `exit code ${result.exitCode}`;\n // Auth guidance is keyed off the stream's error CATEGORY (authoritative),\n // falling back to a text heuristic only when the category is absent. The\n // login hint names the RESOLVED binary, not a literal 'claude'.\n const isAuth =\n result.errorCategory === 'authentication_failed' ||\n result.errorCategory === 'oauth_org_not_allowed' ||\n /not logged|login|authenticate|invalid api key/i.test(reason);\n let message = `host '${binary}' failed (exit ${result.exitCode}): ${reason}`;\n if (isAuth) {\n message += ` Open a terminal and run \\`${binary} /login\\` (interactive-only — it cannot run inside \\`noir run\\`), then retry.`;\n message += credentialNote(env ?? process.env, envSources);\n }\n message += ` If you use another profile, pass \\`--command <binary>\\` or define a run profile under run.profiles. transcript: ${transcript}`;\n fail(EXIT.ERROR, message, opts);\n }\n\n if (opts.json === true) {\n json({\n ok: true,\n data: {\n host,\n prompt,\n exitCode: result.exitCode,\n usage: result.usage,\n numTurns: result.usage.numTurns,\n events: result.eventCount,\n transcript,\n },\n });\n return;\n }\n\n // Separator so the streamed answer and the summary don't run together.\n process.stdout.write('\\n');\n success(`usage: ${formatUsage(result.usage)} (API-equivalent estimate, not billed)`, opts);\n log(`transcript: ${transcript}`, opts);\n}\n\n/** Merge a profile's env overlay over the base env; `undefined` values delete the key. */\nexport function mergeEnv(\n base: Record<string, string | undefined>,\n overlay: Record<string, string | undefined>,\n): Record<string, string | undefined> {\n const merged = { ...base, ...overlay };\n return Object.fromEntries(Object.entries(merged).filter(([, v]) => v !== undefined));\n}\n\n/** Persist the raw stream-json lines to `.noir/transcripts/<host>-<ts>.jsonl`. */\nfunction writeTranscript(host: string, lines: readonly string[]): string {\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\n const dir = join(process.cwd(), '.noir', 'transcripts');\n const file = join(dir, `${host}-${ts}.jsonl`);\n try {\n // Transcripts contain raw host prompts/output (may include secrets) — create\n // the dir 0700 and the file 0600 so they are not group/world-readable.\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeFileSync(file, `${lines.join('\\n')}${lines.length > 0 ? '\\n' : ''}`, { mode: 0o600 });\n } catch {\n // Transcript persistence is best-effort — a read-only .noir/ must not fail\n // the run.\n return '(not persisted)';\n }\n return file;\n}\n","// S9 — `noir skills {list,sync}`.\n//\n// Both sub-commands are IN-PROCESS (no daemon, no store): the builtin skill\n// pack is a filesystem artifact shipped with `@noir-ai/skills`, so `list` reads\n// it directly via `discoverBuiltin()` and `sync` re-emits it into the host\n// adapter's skills dir via `emitSkillsToDir` (the same primitive `noir init` /\n// `noir sync` use). Skill state is host-local files, never the daemon store, so\n// these never hit exit 4 (DAEMON_DOWN).\n//\n// Scriptability (S9 hard rule): `--json` emits the versioned `{ok,data}`\n// envelope to stdout (the only stdout write); the human table + banner go to\n// stderr via the centralized `table()` / `log()` helpers, which auto-strip\n// under NO_COLOR / non-TTY / --json. No interactive input is ever required, so\n// these commands are safe on a pipe / CI unconditionally.\n\nimport { resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, type ProjectInfo } from '@noir-ai/core';\nimport {\n type BuiltinSkill,\n buildRegistry,\n type CompileTarget,\n discoverAll,\n emitSkillsToDir,\n type IntegrationSkill,\n lintSkill,\n} from '@noir-ai/skills';\nimport { type CliOptions, EXIT, fail, info, log, table, warn } from '../output.js';\n\n/** Options accepted by `skills` sub-commands (the global flags only). */\nexport interface SkillsOptions extends CliOptions {}\n\n// ---------------------------------------------------------------------------\n// Category derivation.\n//\n// Skills ship no `category` frontmatter field (S5 contract is `{name,\n// description, references?}`), so the column is a PRESENTATION-layer grouping\n// derived from the skill name. The map covers the 26 builtins; an unknown name\n// falls back to its `noir-`-stripped segment so a newly authored skill still\n// gets a sensible cell instead of an empty one. This is display-only — the\n// `--json` payload carries `category` too (same derivation) for consistency.\n// ---------------------------------------------------------------------------\nconst CATEGORY: Record<string, string> = {\n 'noir-brainstorming': 'discovery',\n 'noir-sync': 'discovery',\n 'noir-checkpoint': 'discovery',\n 'noir-exploring': 'discovery',\n 'noir-spec': 'spec',\n 'noir-planning': 'plan',\n 'noir-prd': 'plan',\n 'noir-executing-plans': 'execute',\n 'noir-test-driven-development': 'execute',\n 'noir-systematic-debugging': 'execute',\n 'noir-subagent': 'execute',\n 'noir-parallel': 'execute',\n 'noir-verifying': 'verify',\n 'noir-security': 'verify',\n 'noir-wrap': 'document',\n 'noir-readme': 'document',\n 'noir-shipping': 'git',\n 'noir-worktree': 'git',\n 'noir-recall': 'memory',\n 'noir-remember': 'memory',\n 'noir-context': 'context',\n 'noir-frontend': 'domain',\n 'noir-backend': 'domain',\n 'noir-doctor': 'meta',\n 'noir-writing-skills': 'meta',\n 'noir-rules': 'meta',\n};\n\nfunction categoryOf(name: string): string {\n const mapped = CATEGORY[name];\n if (mapped !== undefined) return mapped;\n // Unknown skill: its own topic (the `noir-`-stripped name) is a reasonable\n // default cell so the table never shows an empty category.\n return name.replace(/^noir-/, '') || 'general';\n}\n\n/** Normalized skill row (the `data.skills[]` element + the table source). */\nexport interface SkillRow {\n name: string;\n category: string;\n description: string;\n /** Slice X — distinguishes the shipped builtins from the integration skills\n * (e.g. `noir-clickup`) so `noir skills list` shows the full pack and which\n * entries are integrations. Defaults to `'builtin'` for back-compat. */\n kind: 'builtin' | 'integration';\n /** C3 — `full` (playbook) vs `stub` (marker). Derived from the body; the\n * registry enriches this from the same source so list + registry agree. */\n status: 'full' | 'stub';\n}\n\nfunction toRow(s: BuiltinSkill, kind: 'builtin' | 'integration' = 'builtin'): SkillRow {\n const description =\n typeof s.frontmatter.description === 'string' ? s.frontmatter.description : '';\n const status: 'full' | 'stub' = s.skillMd.includes('> **Stub:**') ? 'stub' : 'full';\n // Category: frontmatter `metadata.category` is the C3 single source of truth\n // (\"data, not code\"); the curated CATEGORY map is only the fallback for a\n // legacy skill that lacks metadata. Keeps `skills list`, the registry, and\n // the docs table agreeing on one category per skill.\n const category = s.frontmatter.metadata?.category?.trim() || categoryOf(s.name);\n return { name: s.name, category, description, kind, status };\n}\n\n/**\n * Truncate `text` to `max` chars on a word boundary for dense table cells. This\n * is DISPLAY-ONLY for the human table; the full description is always carried\n * verbatim in the `--json` payload. (Distinct from the S6 \"never truncate the\n * retrieval DATA\" rule — that governs search snippets, not table formatting.)\n */\nfunction truncate(text: string, max: number): string {\n if (text.length <= max) return text;\n const slice = text.slice(0, Math.max(0, max - 1));\n // Cut at the last whitespace inside the slice so we don't split a word.\n const boundary = slice.lastIndexOf(' ');\n const head = boundary > 0 ? slice.slice(0, boundary) : slice;\n return `${head.trimEnd()}…`;\n}\n\n// ---------------------------------------------------------------------------\n// `noir skills list`\n// ---------------------------------------------------------------------------\n/**\n * `noir skills list`: discover the full shipped pack (builtins + integrations)\n * and render it. Consistent with `emitSkillsToDir` (which emits BOTH) — the\n * `noir-clickup` integration shows up here alongside the 26 builtins.\n *\n * `--json` emits `{ok:true, data:{count, skills: SkillRow[]}}` to stdout. A\n * discovery failure (the pack dir is unreadable) maps to exit 1 (ERROR) —\n * not exit 4, since this command never touches the daemon.\n */\nexport async function skillsList(opts: SkillsOptions): Promise<void> {\n let builtins: BuiltinSkill[];\n let integrations: IntegrationSkill[];\n try {\n const all = discoverAll();\n builtins = all.builtins;\n integrations = all.integrations;\n } catch (err) {\n fail(\n EXIT.ERROR,\n `skills list: could not discover skills (${err instanceof Error ? err.message : String(err)})`,\n opts,\n );\n }\n const rows = [\n ...builtins.map((b) => toRow(b, 'builtin')),\n ...integrations.map((i) => toRow(i, 'integration')),\n ];\n // Status is derived from the body (stub marker); category uses the CLI's\n // CATEGORY map (name-derived) until skills carry `metadata.category` — after\n // the C3 content rewrite every skill does, and `skills registry` reads it.\n // `skills list` keeps the curated CATEGORY map as its single source so the\n // human table is stable; the registry is the queryable form.\n const data = { count: rows.length, skills: rows };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n\n const intCount = integrations.length;\n const banner =\n intCount > 0\n ? `noir skills — ${rows.length} skill${rows.length === 1 ? '' : 's'} (${builtins.length} builtin, ${intCount} integration${intCount === 1 ? '' : 's'})`\n : `noir skills — ${rows.length} builtin skill${rows.length === 1 ? '' : 's'}`;\n log(banner, opts);\n table(\n rows.map((r) => ({\n Skill: r.name,\n Kind: r.kind,\n Category: r.category,\n Status: r.status,\n Description: truncate(r.description, 80),\n })),\n ['Skill', 'Kind', 'Category', 'Status', 'Description'],\n opts,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `noir skills sync`\n// ---------------------------------------------------------------------------\n/**\n * `noir skills sync`: re-emit the builtin pack into the host adapter's skills\n * dir. Reuses the same `emitSkillsToDir` primitive as `noir init` / `noir sync`\n * (the host-local file write; no daemon involved).\n *\n * Requires the project to be initialized (`loadProjectInfo` throws otherwise →\n * exit 1 with the \"Run `noir init` first\" hint). When the host adapter exposes\n * no `skillsDir` (no skill emitter for this host), the command reports nothing\n * to sync and exits 0 — behavior-preserving with the existing `noir sync`.\n *\n * `--json` emits `{ok:true, data:{emitted, references, dir}}` to stdout.\n */\nexport async function skillsSync(opts: SkillsOptions): Promise<void> {\n const root = process.cwd();\n // loadProjectInfo asserts Noir is initialized; route it through fail() so an\n // uninitialized project emits the canonical S9 {ok:false,error} envelope under\n // --json (a plain Error would leave stdout empty) — same as every other command.\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(root);\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n\n // Resolve the PROJECT's host adapter (not a hardcoded claude) — mirroring\n // sync.ts. A non-claude host (cursor/agents-md/opencode) has its own skillsDir\n // (e.g. .cursor/rules), and a host with NO skillsDir must report no-emitter\n // instead of silently writing a spurious .claude/skills/ tree.\n const adapter = resolveAdapter(project.config.host);\n if (!adapter.skillsDir) {\n const data = {\n emitted: [],\n references: 0,\n host: project.config.host,\n reason: 'no-skill-emitter',\n };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n info('This host has no skill emitter; nothing to sync.', opts);\n return;\n }\n\n const dir = adapter.skillsDir({ root });\n const target: CompileTarget = project.config.host;\n const summary = await emitSkillsToDir(dir, { target });\n const pruned = summary.pruned ?? [];\n const data = {\n emitted: summary.emitted,\n references: summary.references,\n dir,\n pruned,\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n\n log(\n `Synced ${summary.emitted.length} Noir skill${summary.emitted.length === 1 ? '' : 's'} to ${dir}.`,\n opts,\n );\n // Surface stale-dir pruning in both human + JSON modes for parity with\n // `noir sync` (the structured `pruned` field above covers --json).\n if (pruned.length > 0) {\n warn(\n `Pruned ${pruned.length} stale noir-* skill dir${pruned.length === 1 ? '' : 's'}: ${pruned.join(', ')}`,\n opts,\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir skills lint`\n// ---------------------------------------------------------------------------\n/**\n * `noir skills lint`: the C3 structural quality gate over the full shipped pack.\n * Runs `lintSkill` (validateSkill errors + soft warnings) over `discoverAll()`.\n * In-process (no daemon) — the pack is a filesystem artifact.\n *\n * Exit contract: 0 when every skill validates clean (errors empty); 1 when any\n * skill has errors (the gate fails). Warnings alone do NOT fail the lint — they\n * are advisory and listed for the author. This mirrors how a real linter treats\n * hard errors vs style warnings.\n *\n * `--json` emits `{ok, data:{skills:[{name, errors, warnings}]}}` to stdout\n * (the only stdout write); the human report goes to stderr.\n */\nexport async function skillsLint(opts: SkillsOptions): Promise<void> {\n let skills: Array<{ name: string; errors: string[]; warnings: string[] }>;\n try {\n const all = discoverAll();\n skills = [...all.builtins, ...all.integrations].map(lintSkill);\n } catch (err) {\n fail(\n EXIT.ERROR,\n `skills lint: could not lint skills (${err instanceof Error ? err.message : String(err)})`,\n opts,\n );\n }\n const errored = skills.filter((s) => s.errors.length > 0);\n const warned = skills.filter((s) => s.warnings.length > 0 && s.errors.length === 0);\n\n const data = { count: skills.length, errored: errored.length, skills };\n\n // Exit-1 contract (the gate must FAIL, not silently pass): a lint error\n // produces a non-zero exit + the canonical S9 failure envelope under --json\n // (`{ok:false,error:{code,message}}` — no ad-hoc `{ok:false,data}` shape).\n if (errored.length > 0) {\n const message = `skills lint: ${errored.length} skill${errored.length === 1 ? '' : 's'} failed validation`;\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, error: { code: EXIT.ERROR, message }, data })}\\n`,\n );\n process.exitCode = EXIT.ERROR;\n return;\n }\n warn(\n `noir skills lint — ${errored.length} skill${errored.length === 1 ? '' : 's'} FAIL validation:`,\n opts,\n );\n for (const s of errored) {\n for (const e of s.errors) warn(` ${s.name}: ${e}`, opts);\n }\n if (warned.length > 0) {\n info(` ${warned.length} skill${warned.length === 1 ? '' : 's'} carry lint warnings:`, opts);\n for (const s of warned) {\n for (const w of s.warnings) info(` ${s.name}: ${w}`, opts);\n }\n }\n fail(EXIT.ERROR, message, opts);\n }\n\n // Clean: exit 0.\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(`noir skills lint — ${skills.length} skills, all validate clean.`, opts);\n if (warned.length > 0) {\n info(` ${warned.length} skill${warned.length === 1 ? '' : 's'} carry lint warnings:`, opts);\n for (const s of warned) {\n for (const w of s.warnings) info(` ${s.name}: ${w}`, opts);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir skills registry`\n// ---------------------------------------------------------------------------\n/**\n * `noir skills registry`: emit the C3 runtime-derived skill registry (id, kind,\n * category, version, status, refs, lines). In-process (no daemon) — reads the\n * shipped pack via `buildRegistry()`. The registry is NOT a committed file;\n * frontmatter is the single source of truth and this command derives it on\n * demand (C3 decision D3).\n *\n * `--json` emits `{ok:true, data:{count, skills: SkillRegistryEntry[]}}` to\n * stdout; the human view is a table to stderr.\n */\nexport async function skillsRegistry(opts: SkillsOptions): Promise<void> {\n let reg: ReturnType<typeof buildRegistry>;\n try {\n reg = buildRegistry();\n } catch (err) {\n fail(\n EXIT.ERROR,\n `skills registry: could not build registry (${err instanceof Error ? err.message : String(err)})`,\n opts,\n );\n }\n const data = { count: reg.length, skills: reg };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(`noir skills registry — ${reg.length} skills.`, opts);\n table(\n reg.map((r) => ({\n Skill: r.name,\n Kind: r.kind,\n Category: r.category,\n Version: r.version,\n Status: r.status,\n Refs: String(r.referenceCount),\n Lines: String(r.lines),\n })),\n ['Skill', 'Kind', 'Category', 'Version', 'Status', 'Refs', 'Lines'],\n opts,\n );\n}\n","// SP-C — conflict-resolution UX seam. The engine (@noir-ai/create) is UI-free;\n// this module builds the scaffold conflict options (policy + a lazy @clack\n// resolver) per the S9 interactivity contract:\n// - TTY + interactive → @clack select menu (Replace/Rename/Duplicate/Keep/Cancel,\n// plus a 6th \"merge (with conflict markers)\" when `mergedWithMarkers` is set)\n// - non-TTY / CI / NO_COLOR → preserve (never silently clobber in a pipe)\n// - --force → overwrite (explicit re-scaffold; no prompt)\n//\n// Universal contract: this seam now drives EVERY file-producing path. The\n// engine's regenerate path consults `onConflict` (apply-to-all keyed by artifact\n// CLASS so an N-pointer `noir init --upgrade` → 1 prompt); managedBlock conflicts\n// stay PER-FILE. A unified line-diff preview (LCS-based, from\n// @noir-ai/create's `lineDiff`) renders to STDERR before the prompt (via the\n// A2 theme — `+` green / `-` red / context dim), so `--json`/piped stdout stays\n// pristine. The 6th \"merge\" option writes the engine-provided zdiff3-marked\n// bytes when a 3-way merge hit an overlap.\nimport {\n type ConflictContext,\n type ConflictResolution,\n type ConflictResolverReturn,\n lineDiff,\n} from '@noir-ai/create';\nimport { isInteractive } from './output.js';\nimport { c } from './theme.js';\n\nexport interface ConflictOptsInput {\n /** `--force`: explicit re-scaffold — overwrite differing files, no prompt. */\n force?: boolean;\n /** B1: explicit interactivity signal. When set, wins over the env/TTY\n * heuristic. `false` ⇒ preserve (never prompt); `true` ⇒ allow the @clack\n * resolver. The CLI derives this from the `NOIR_NON_INTERACTIVE` bridge\n * bin.ts owns + the `isInteractive()` TTY/CI/NO_COLOR gate. */\n interactive?: boolean;\n}\n\nexport type ScaffoldConflictOpts = {\n conflictPolicy: 'overwrite' | 'preserve';\n onConflict?: (ctx: ConflictContext) => Promise<ConflictResolverReturn> | ConflictResolverReturn;\n};\n\n/**\n * Build the scaffold conflict options for the current invocation. The engine\n * calls `onConflict` only when a `regenerate` file exists AND differs from the\n * template, so passing this on every init/create/sync is harmless on a first\n * run (no existing files → no conflict).\n *\n * B1 + SP-G: the `interactive` flag (explicit > env bridge > TTY/CI/NO_COLOR\n * gate) drives the prompt decision so the engine never reads `process.env` for\n * interactivity. The `NOIR_NON_INTERACTIVE` bridge (--json/--no-input) and the\n * `isInteractive()` gate both reduce to `preserve` (never prompt).\n *\n * B2 apply-to-all: when the resolver returns `{resolution, applyToAll: true}`\n * the engine stores the choice in its per-run memory keyed by artifact CLASS\n * (regenerate shares one decision; managedBlock/managedBlocks stay per-file).\n */\n/** True when the bin's preAction hook flagged this invocation non-interactive\n * (`--json` / `--no-input`) so the conflict resolver never prompts under those. */\nfunction flaggedNonInteractive(): boolean {\n const v = process.env.NOIR_NON_INTERACTIVE;\n return v !== undefined && v !== '';\n}\n\nexport function buildConflictOpts(input: ConflictOptsInput = {}): ScaffoldConflictOpts {\n if (input.force === true) {\n return { conflictPolicy: 'overwrite' };\n }\n // Explicit interactive flag wins over the env/TTY heuristic.\n if (input.interactive === false) {\n return { conflictPolicy: 'preserve' };\n }\n if (input.interactive === true) {\n return { conflictPolicy: 'preserve', onConflict: clackConflictResolver };\n }\n // Fallback: NOIR_NON_INTERACTIVE bridge (--json/--no-input) OR the\n // isInteractive() TTY/CI/NO_COLOR gate — either ⇒ preserve, never prompt.\n if (flaggedNonInteractive() || !isInteractive()) {\n return { conflictPolicy: 'preserve' };\n }\n return { conflictPolicy: 'preserve', onConflict: clackConflictResolver };\n}\n\n/**\n * B2 — render a colored unified line-diff of `{existing, proposed}` to STDERR\n * before the @clack prompt. Stderr (not stdout) so `--json` / piped stdout\n * stays pristine. The A2 theme colors `+` lines green (`c.ok`), `-` lines red\n * (`c.error`), and dims context lines (`c.dim`); all color is gated through\n * `useColor()` so NO_COLOR / non-TTY / CLICOLOR_FORCE behave consistently.\n * Honored by `theme.test.ts`'s NO_COLOR gate.\n */\nfunction renderDiffPreview(ctx: ConflictContext): void {\n // `lineDiff` is imported statically at the top of this module. It used to be\n // a lazy `require('@noir-ai/create')`, but tsup bundles every @noir-ai/*\n // internal into dist/bin.js — a dynamic `require` survives bundling as an\n // ESM-incompatible `__require` shim that throws \"Dynamic require of\n // @noir-ai/create is not supported\" at runtime (broke `init --upgrade`\n // whenever a file conflict triggered this preview). The static import is\n // bundled correctly and runs in pure ESM with no side effects here.\n const lines = lineDiff(ctx.existing, ctx.proposed);\n if (lines.length === 0) return;\n const out: string[] = [`noir: conflict in ${ctx.relPath}`];\n for (const dl of lines) {\n if (dl.type === 'add') out.push(c.ok(`+ ${dl.line}`));\n else if (dl.type === 'del') out.push(c.error(`- ${dl.line}`));\n else out.push(c.dim(` ${dl.line}`));\n }\n process.stderr.write(`${out.join('\\n')}\\n`);\n}\n\n/**\n * SP-C + B2 — @clack-based conflict resolver. Renders a unified diff to stderr\n * (via {@link renderDiffPreview}) BEFORE the select prompt, then offers the\n * usual Replace/Rename/Duplicate/Keep/Cancel set PLUS a 6th \"merge (with\n * conflict markers)\" option when `ctx.mergedWithMarkers` is set (a 3-way merge\n * hit an overlap and the engine has the marked bytes ready).\n *\n * Apply-to-all: when the user picks an option, @clack asks whether to reuse the\n * choice for the rest of the run; if yes, the resolver returns the rich\n * `{resolution, applyToAll: true}` shape and the engine stores it in per-class\n * memory. `managedBlock`/`managedBlocks` modes bypass apply-to-all (per-file).\n */\nasync function clackConflictResolver(ctx: ConflictContext): Promise<ConflictResolverReturn> {\n renderDiffPreview(ctx);\n const clack = await import('@clack/prompts');\n const options: Array<{ value: ConflictResolution; label: string; hint: string }> = [\n {\n value: 'replace',\n label: 'Replace',\n hint: 'overwrite with the template (discard your edits)',\n },\n { value: 'rename', label: 'Rename', hint: 'keep yours as <file>.local; write the template' },\n {\n value: 'duplicate',\n label: 'Create duplicate',\n hint: 'write template to <file>.noir; keep yours',\n },\n { value: 'preserve', label: 'Keep mine', hint: 'skip; leave your file unchanged' },\n { value: 'cancel', label: 'Cancel', hint: 'abort the scaffold' },\n ];\n // 6th \"merge\" option only when the engine has marked bytes ready.\n if (ctx.mergedWithMarkers !== undefined) {\n options.splice(4, 0, {\n value: 'merge',\n label: 'Merge (with conflict markers)',\n hint: 'write the 3-way merge with <<<<<<< / ||||||| / ======= / >>>>>>> markers',\n });\n }\n const choice = await clack.select({\n message: `${ctx.relPath} was edited and differs from the Noir template.`,\n initialValue: 'preserve',\n options,\n });\n if (clack.isCancel(choice)) return 'cancel';\n const resolution = choice as ConflictResolution;\n // Apply-to-all. The `regenerate` scaffold path AND the `skill` emit path both\n // key their per-run memory by a single class, so one \"Yes\" reuses the choice\n // across the whole run. The `managedBlock`/`managedBlocks` paths are per-file\n // (the engine keys memory by path::block, not by class), so they must NOT\n // offer \"apply to all\" — reusing a per-file decision there would silently\n // clobber unrelated blocks.\n const mode = (ctx.mode ?? 'regenerate') as string;\n const supportsApplyToAll = mode !== 'managedBlock' && mode !== 'managedBlocks';\n if (supportsApplyToAll) {\n const remember = await clack.select({\n message: `Apply \"${labelFor(resolution)}\" to all remaining conflicts this run?`,\n initialValue: 'no',\n options: [\n { value: 'yes', label: 'Yes', hint: 'reuse this choice for the rest of this run' },\n { value: 'no', label: 'No', hint: 'prompt again for each conflict' },\n ],\n });\n if (!clack.isCancel(remember) && remember === 'yes') {\n return { resolution, applyToAll: true };\n }\n }\n return resolution;\n}\n\nfunction labelFor(resolution: ConflictResolution): string {\n switch (resolution) {\n case 'replace':\n return 'Replace';\n case 'rename':\n return 'Rename';\n case 'duplicate':\n return 'Create duplicate';\n case 'preserve':\n return 'Keep mine';\n case 'merge':\n return 'Merge (with conflict markers)';\n case 'cancel':\n return 'Cancel';\n }\n}\n","// Write-path semantic dedup. After `scaffold()` writes host-context\n// files (CLAUDE.md / AGENTS.md / GEMINI.md / .noir/rules/RULES.md), embed the\n// just-written file and compare it against the OTHER existing host-context\n// files. Surface a near-duplicate as a NON-BLOCKING recommendation:\n//\n// - cosine ≥ 0.95 → ACTION prompt (interactive): Replace the existing /\n// Mirror into it / Skip (delete the new file) / Create\n// anyway. Under --json/--no-input the action tier records\n// the near-dup in ScaffoldResult.conflicts[] (mode=\n// 'artifact', similarity set) and proceeds with the\n// default (Create anyway); NEVER prompts.\n// - 0.85–0.95 → INFO-only hint to stderr, write proceeds.\n// - < 0.85 → silent.\n//\n// GRACEFUL DEGRADATION (the make-orreak constraint): the embedder (ONNX\n// Runtime + the ~22 MB all-MiniLM-L6-v2 model) is lazy-loaded with a 5s ping\n// timeout; if it is unavailable, slow, OR throws, the hook prints a one-line\n// stderr warn-skip and returns an empty result. `noir init`/`sync`/`create`\n// NEVER block on a model download or fail because the embedder is missing —\n// dedup is an ENHANCEMENT, not a gate. A fresh project with no existing\n// host-context candidates short-circuits BEFORE any embedding (fast path).\n//\n// CONTENT-HASH GATE: candidate embeddings are cached under\n// `.noir/dedup-cache.json` keyed by SHA-256(content); only changed candidates\n// re-embed. The proposed file's embedding is cached the same way, so a repeat\n// `noir sync` that re-emits byte-identical content does NOT re-embed. Cache\n// entries are scoped to the resolved model id (a model change invalidates them).\n//\n// When a near-dup is found, the conflict record (mode 'artifact'\n// — the closest generic in ConflictContext['mode']) is appended to the\n// ScaffoldResult.conflicts[] the caller returns, so `--json` consumers see the\n// near-dup with its cosine `similarity` without a prompt.\n//\n// Stream discipline: every hint / warn-skip → STDERR; the CLI's `json()` is the\n// sole stdout writer. Colors via the A2 theme (`c.warn`/`c.dim`) so NO_COLOR /\n// non-TTY strip cleanly.\n\nimport { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type ConflictResolution, type ProjectInfo, sha256Hex, sha256Hex12 } from '@noir-ai/core';\nimport type { ConflictRecord, ScaffoldResult } from '@noir-ai/create';\nimport { collectDedupCandidates } from './commands/doctor.js';\nimport { c } from './theme.js';\n\n/** Local embedder shape (@noir-ai/context's `EmbedFn`). */\nexport type EmbedLike = (text: string) => Promise<Float32Array>;\n\n/** Host-context files dedup scans (mirrors `collectDedupCandidates`). */\nconst HOST_CONTEXT_RELS = new Set(['CLAUDE.md', 'AGENTS.md', 'GEMINI.md', '.noir/rules/RULES.md']);\n\n/** Cosine ≥ this → ACTION prompt (interactive) or conflict record (--json). */\nconst ACTION_THRESHOLD = 0.95;\n/** Cosine ≥ this (and < ACTION) → INFO-only hint. Below → silent. */\nconst INFO_THRESHOLD = 0.85;\n/** The first embed() call triggers ONNX load + possibly a model download; bound\n * it so init/sync/create never block indefinitely on a cold cache. */\nconst EMBEDDER_PING_TIMEOUT_MS = 5000;\n/** Bump on disk-format changes; an older file is discarded (no migration). */\nconst DEDUP_CACHE_VERSION = 1;\n\nexport interface DedupWriteOpts {\n /** Explicit interactivity flag (the engine + skills emit read the same). */\n interactive: boolean;\n /** Project info — read from `.noir/config.yml` by the caller. When undefined\n * OR when the resolved embedder is `kind:'none'`, the hook warn-skips. */\n project?: ProjectInfo;\n /** Testability seam (mirrors `checkSemanticDupDoctor`'s `opts.embed`): when\n * set, the hook uses this embedder directly and skips the lazy ONNX load +\n * ping-timeout. Production omits it. */\n embed?: EmbedLike;\n}\n\nexport interface DedupWriteResult {\n /** Near-duplicates found, one entry per (proposed, matched) pair. */\n found: Array<{ proposedRel: string; matchedRel: string; similarity: number }>;\n /** Conflict records to append to {@link ScaffoldResult.conflicts}. */\n conflicts: ConflictRecord[];\n}\n\n/**\n * Run the write-path semantic dedup over the host-context files in\n * `res.written`. Does NOT mutate `res`; the caller decides whether to splice\n * `result.conflicts` into `res.conflicts`. Non-blocking: any embedder failure\n * degrades to a stderr warn-skip and an empty result.\n */\nexport async function checkWritePathDedup(\n root: string,\n res: ScaffoldResult,\n opts: DedupWriteOpts,\n): Promise<DedupWriteResult> {\n const result: DedupWriteResult = { found: [], conflicts: [] };\n // Fast path: only host-context files we JUST wrote are eligible. Fresh\n // projects (first init/create) typically write ONE host pointer + RULES.md;\n // if no other host-context file exists, candidates is empty → no embedding.\n const writtenHostFiles = res.written.filter((p) => HOST_CONTEXT_RELS.has(p));\n if (writtenHostFiles.length === 0) return result;\n\n const cache = loadCache(root);\n const embed = await resolveEmbedder(opts, cache);\n if (embed === undefined) return result; // warn-skip already emitted\n\n // Wrap the embedder with the content-hash cache so unchanged candidates (and\n // a repeat-emit of byte-identical proposed bytes) skip the underlying call.\n const cachedEmbed = makeCachedEmbed(cache, embed);\n\n for (const proposedRel of writtenHostFiles) {\n const proposedAbs = join(root, proposedRel);\n let proposedText: string;\n try {\n proposedText = readFileSync(proposedAbs, 'utf8');\n } catch {\n continue;\n }\n if (proposedText.trim().length === 0) continue;\n\n // Candidates = existing host-context files MINUS this proposed path. A\n // first-run init writing CLAUDE.md while AGENTS.md already exists compares\n // the new CLAUDE.md against AGENTS.md + RULES.md (not against itself).\n const candidates = collectDedupCandidates(root).filter((c) => c.path !== proposedRel);\n if (candidates.length === 0) continue; // fresh project, nothing to compare\n\n const ctx = await import('@noir-ai/context');\n const proposed = { path: proposedRel, text: proposedText };\n // Graceful degradation INSIDE the loop too: an embedder that loads (passes\n // the ping) but throws mid-batch (transient remote failure, OOM, …) must\n // never break the write. Catch, warn-skip this proposal, keep going.\n let best: Awaited<ReturnType<typeof ctx.findNearestDuplicate>>;\n try {\n best = await ctx.findNearestDuplicate(\n proposed,\n candidates,\n async (t) => cachedEmbed(t),\n INFO_THRESHOLD,\n );\n } catch (e) {\n process.stderr.write(\n `${c.dim(`noir: semantic-dedup skipped for ${proposedRel} (${errMsg(e)}).`)}\\n`,\n );\n continue;\n }\n if (best === null) continue;\n\n const matchedRel = best.a === proposedRel ? best.b : best.a;\n const sim = best.similarity;\n result.found.push({ proposedRel, matchedRel, similarity: sim });\n\n // The conflict record's resolution reflects what actually happened — not a\n // hardcoded 'preserve'. Non-interactive + cancel default to 'preserve'\n // (Create anyway); the prompt's replace/mirror choices map onto the\n // ConflictResolution union.\n let resolution: ConflictResolution = 'preserve';\n if (sim >= ACTION_THRESHOLD) {\n if (opts.interactive) {\n resolution = await promptDedupAction(root, proposedRel, matchedRel, proposedText, sim);\n } else {\n // --json / --no-input: record + proceed with the default (Create\n // anyway). Never prompt under non-interactive.\n process.stderr.write(\n `${c.warn('noir: near-duplicate')} ${proposedRel} ≈ ${matchedRel} (cosine ${sim.toFixed(2)}); creating anyway.\\n`,\n );\n }\n } else {\n // 0.85 – 0.95: INFO-only hint. Write already succeeded; just surface.\n process.stderr.write(\n `${c.dim(`noir: hint ${proposedRel} looks similar to existing ${matchedRel} (cosine ${sim.toFixed(2)}).`)}\\n`,\n );\n }\n\n // Append a structured record so --json consumers see the\n // near-dup with its cosine similarity without a prompt. The path is the\n // file we just wrote (consistent with the \"path = the file being\n // written\" convention); existingSha is the MATCHED file's hash, proposedSha the new\n // bytes'. Resolution 'preserve' = we kept both as-is under Create-anyway.\n const matchedText = safeRead(join(root, matchedRel));\n result.conflicts.push({\n path: proposedRel,\n mode: 'artifact',\n similarity: sim,\n existingSha: sha256Hex12(matchedText ?? ''),\n proposedSha: sha256Hex12(proposedText),\n resolution,\n });\n }\n\n saveCache(root, cache);\n return result;\n}\n\n/**\n * Resolve the embedder: injected (test seam) > local-embedder from the user's\n * `context.embedder` config. Returns undefined (after a stderr warn-skip) when\n * kind='none', the factory throws, OR the first ping exceeds the timeout.\n *\n * Model-id tracking runs in BOTH paths so a config change invalidates the\n * on-disk cache even when the caller injected an embedder (the test seam\n * still flows through the same cache discipline as production).\n */\nasync function resolveEmbedder(\n opts: DedupWriteOpts,\n cache: DedupCache,\n): Promise<EmbedLike | undefined> {\n const ctx = await import('@noir-ai/context');\n // Resolve the model id from config when available; an injected embedder\n // without project info skips invalidation (cache stays model-scoped to '' —\n // a fresh cache, which is fine for tests that always inject).\n let modelId: string | undefined;\n if (opts.project !== undefined) {\n const cfg = ctx.resolveEmbedderConfig(opts.project.config.context);\n if (cfg.kind === 'none') {\n process.stderr.write(`${c.dim('noir: semantic-dedup skipped (embedder kind=none).')}\\n`);\n return undefined;\n }\n modelId =\n cfg.kind === 'local' ? (cfg.model ?? ctx.DEFAULT_LOCAL_MODEL) : (cfg.model ?? cfg.kind);\n // Invalidate cache on a model change BEFORE deciding the embedder source.\n if (modelId !== undefined && cache.model !== modelId) {\n cache.model = modelId;\n cache.entries = {};\n }\n }\n // Test seam: an injected embedder bypasses the lazy ONNX load + ping.\n if (opts.embed !== undefined) return opts.embed;\n if (modelId === undefined) return undefined; // no project + no inject → skip\n\n let embed: EmbedLike;\n try {\n const project = opts.project;\n if (project === undefined) return undefined;\n const cfg = ctx.resolveEmbedderConfig(project.config.context);\n embed = ctx.createEmbedFn(cfg).embed as EmbedLike;\n } catch (e) {\n process.stderr.write(\n `${c.dim(`noir: semantic-dedup skipped (embedder unavailable: ${errMsg(e)}).`)}\\n`,\n );\n return undefined;\n }\n // Pre-warm with a ping under a timeout. The first call triggers the ONNX\n // load + (on a cold ~/.noir/models/) a ~22 MB model download; bound it so\n // init/sync never block on the network. A throw OR a timeout → warn-skip.\n const ping = await withTimeout(\n embed('ping').then(() => true),\n EMBEDDER_PING_TIMEOUT_MS,\n );\n if (ping !== true) {\n process.stderr.write(\n `${c.dim('noir: semantic-dedup skipped (embedder load too slow or unavailable).')}\\n`,\n );\n return undefined;\n }\n return embed;\n}\n\n/** ACTION prompt for the ≥0.95 tier (interactive only). */\nasync function promptDedupAction(\n root: string,\n proposedRel: string,\n matchedRel: string,\n proposedText: string,\n sim: number,\n): Promise<ConflictResolution> {\n const clack = await import('@clack/prompts');\n type Choice = 'create' | 'skip' | 'replace' | 'mirror';\n const choice = await clack.select({\n message: `${proposedRel} is near-duplicate of existing ${matchedRel} (cosine ${sim.toFixed(2)}). Create anyway?`,\n initialValue: 'create' as Choice,\n options: [\n {\n value: 'create' as Choice,\n label: 'Create anyway',\n hint: 'keep the new file as-is (default)',\n },\n { value: 'skip' as Choice, label: 'Skip', hint: `delete ${proposedRel}; keep ${matchedRel}` },\n {\n value: 'replace' as Choice,\n label: 'Replace existing',\n hint: `overwrite ${matchedRel} with this content`,\n },\n {\n value: 'mirror' as Choice,\n label: 'Mirror into existing',\n hint: `append this content to ${matchedRel}`,\n },\n ],\n });\n if (clack.isCancel(choice)) return 'preserve'; // cancel = keep the default (Create anyway)\n if (choice === 'skip') {\n try {\n rmSync(join(root, proposedRel), { force: true });\n } catch {\n /* best-effort; the write already happened */\n }\n return 'preserve'; // the matched file is kept\n } else if (choice === 'replace') {\n try {\n writeFileSync(join(root, matchedRel), proposedText, 'utf8');\n } catch {\n /* best-effort */\n }\n return 'replace';\n } else if (choice === 'mirror') {\n try {\n const existing = readFileSync(join(root, matchedRel), 'utf8');\n writeFileSync(\n join(root, matchedRel),\n `${existing}\\n\\n<!-- mirrored from ${proposedRel} -->\\n${proposedText}\\n`,\n 'utf8',\n );\n } catch {\n /* best-effort */\n }\n return 'duplicate'; // the proposed content was folded into the existing file\n }\n // 'create' → no-op (the just-written file stands).\n return 'preserve';\n}\n\n// ---------------------------------------------------------------------------\n// Content-hash cache (.noir/dedup-cache.json).\n// ---------------------------------------------------------------------------\n\ninterface DedupCache {\n version: number;\n model: string;\n entries: Record<string, { v: number[] }>;\n}\n\nfunction emptyCache(): DedupCache {\n return { version: DEDUP_CACHE_VERSION, model: '', entries: {} };\n}\n\nfunction cachePath(root: string): string {\n return join(root, '.noir', 'dedup-cache.json');\n}\n\nfunction loadCache(root: string): DedupCache {\n try {\n const raw = readFileSync(cachePath(root), 'utf8');\n const parsed = JSON.parse(raw) as Partial<DedupCache>;\n if (parsed.version !== DEDUP_CACHE_VERSION) return emptyCache();\n if (typeof parsed.entries !== 'object' || parsed.entries === null) return emptyCache();\n return {\n version: DEDUP_CACHE_VERSION,\n model: typeof parsed.model === 'string' ? parsed.model : '',\n entries: parsed.entries,\n };\n } catch {\n return emptyCache();\n }\n}\n\nfunction saveCache(root: string, cache: DedupCache): void {\n try {\n mkdirSync(join(root, '.noir'), { recursive: true });\n writeFileSync(cachePath(root), JSON.stringify(cache), 'utf8');\n } catch {\n // Best-effort; cache persistence is an enhancement, never a gate.\n }\n}\n\n/** Wrap `embed` with a SHA-256 content-hash cache. Cache hits return the\n * stored vector WITHOUT calling `embed`; misses call `embed` and store. */\nfunction makeCachedEmbed(cache: DedupCache, embed: EmbedLike): EmbedLike {\n return async (text: string): Promise<Float32Array> => {\n const key = sha256Hex(text);\n const cached = cache.entries[key];\n if (cached !== undefined) return Float32Array.from(cached.v);\n const v = await embed(text);\n cache.entries[key] = { v: Array.from(v) };\n return v;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Small helpers.\n// ---------------------------------------------------------------------------\n\nfunction safeRead(abs: string): string | undefined {\n try {\n return readFileSync(abs, 'utf8');\n } catch {\n return undefined;\n }\n}\n\n/** Resolve `p` OR undefined on timeout/throw (never rejects). The underlying\n * promise keeps running in the background; we just stop awaiting it. */\nfunction withTimeout<T>(p: Promise<T>, ms: number): Promise<T | undefined> {\n return new Promise((resolve) => {\n const t = setTimeout(() => resolve(undefined), ms);\n p.then(\n (v) => {\n clearTimeout(t);\n resolve(v);\n },\n () => {\n clearTimeout(t);\n resolve(undefined);\n },\n );\n });\n}\n\nfunction errMsg(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n","// `noir init` — first-run + `--upgrade`.\n//\n// Slice S-T2 refactor: the ad-hoc writers (syncIgnores, writeManagedRegion,\n// writeFileSync for .mcp.json/project.id/config.yml/NOIR.md/RULES.md) are\n// replaced by a single call into `@noir-ai/create`'s `scaffold({mode:'init'})`.\n// The manifest + three-mode writer are the source of truth for what init\n// emits; this module is now a thin caller that owns ONLY:\n// - the transport/url precondition (the localhost security gate, whose\n// error strings are locked by url-validation.test.ts), and\n// - skills emission (out-of-manifest by design — composed after scaffold()).\n//\n// S10 multi-host: the 8 direct `claudeAdapter` imports across init/sync/create\n// collapsed to `resolveAdapter(host)` where `host` comes from `--host <id>`\n// (default `'claude'`). The adapter drives (a) the manifest via\n// `scaffold({host})` and (b) skills emission — claude/cursor have a skill dir;\n// gemini/agents-md/opencode have no skill concept and the call is skipped.\n//\n// Deliberate behavior changes vs the predecessor (spec-aligned latent-bug\n// fixes; see S-T1 contract notes + CHANGELOG):\n// - `.noir/project.id` → skipIfExists (predecessor overwrote on every init,\n// orphaning the store DB named after the id). Re-init now preserves it.\n// - `.noir/config.yml` → skipIfExists (predecessor overwrote).\n// - `.noir/NOIR.md` → managedBlock with BRIEF_BLOCK markers (predecessor\n// wrote the whole file with no markers). First-run output GAINS markers;\n// user notes outside the markers survive re-runs.\n// - `.noir/scaffold-version` is now stamped on init/create (engine-owned).\n\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, type ProjectInfo } from '@noir-ai/core';\nimport { type ScaffoldResult, scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts, type ScaffoldConflictOpts } from './conflict.js';\nimport { checkWritePathDedup } from './dedup-write.js';\nimport { log, resolveInteractive } from './output.js';\n\nexport interface InitOptions {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n /** `noir init --upgrade`: run scaffold migrations from the on-disk\n * scaffold-version to current, then re-emit every manifest mode except\n * `mergeJson` — including `skipIfExists`, which creates only when the file is\n * absent. That backfills seeds added to the manifest after this project was\n * initialized without ever touching a user-owned file. */\n upgrade?: boolean;\n /** S10 target host. Defaults to `'claude'` (the regression anchor). Drives\n * both scaffold emission (the manifest's host-specific half) and skills\n * emission (skipped for hosts with no `skillsDir`). */\n host?: HostId;\n /** SP-A: re-scaffold even if already initialized (bypasses the\n * already-initialized no-op guard in scaffold()). */\n force?: boolean;\n /** F1: `--dry-run`/`--preview` — report the planned writes to stderr\n * (via {@link reportPlannedWrites}) without touching disk. The scaffold\n * engine already supports this; the CLI just surfaces it. */\n dryRun?: boolean;\n /** F1: alias for `--dry-run`. Kept on the options bag so direct callers can\n * pass either spelling; the bin collapses both flags before dispatch. */\n preview?: boolean;\n}\n\n/**\n * Initialize Noir in `root`. Returns the {@link ScaffoldResult} (with\n * structured `conflicts[]` + any dedup records appended) so `--json`\n * callers can surface conflict detail. `undefined` when the already-initialized\n * guard short-circuited (a no-op).\n */\nexport async function init(root: string, opts: InitOptions): Promise<ScaffoldResult | undefined> {\n assertTransportUrl(opts);\n\n const host: HostId = opts.host ?? 'claude';\n // The engine reads ScaffoldOptions.interactive (hermetic — never\n // process.env). The CLI derives it once from the bridge + TTY/CI/NO_COLOR gate.\n const interactive = resolveInteractive();\n const conflictOpts = buildConflictOpts({ force: opts.force, interactive });\n // F1: --dry-run/--preview collapse to a single dryRun boolean. The engine\n // skips every write and returns the PLANNED lists; skills emission + dedup are\n // skipped too (they would touch disk / load the embedder).\n const dryRun = opts.dryRun === true || opts.preview === true;\n\n const res = await scaffold({\n root,\n mode: 'init',\n host,\n transport: opts.transport,\n interactive,\n ...(opts.url !== undefined ? { url: opts.url } : {}),\n ...(opts.upgrade === true ? { upgrade: true } : {}),\n ...(opts.force === true ? { force: true } : {}),\n ...(dryRun ? { dryRun: true } : {}),\n ...conflictOpts,\n });\n // F1: dry-run reports the planned writes (result's written/skipped/identical)\n // and stops BEFORE skills emission + the \"initialized\" message — nothing was\n // written, so the host skill dir must stay untouched and we must not claim\n // init. Under --json the bin emits the planned list as the `{ok, data}`\n // envelope on stdout (the data channel) instead.\n if (dryRun) {\n reportPlannedWrites(res);\n return res;\n }\n // SP-A: if the already-initialized guard no-op'd scaffold, stop — don't re-emit\n // skills or print \"initialized\" (scaffold already printed the no-op message).\n if (res.noop) return res;\n\n // Thread the SAME conflictOpts into skills emission so the\n // skills-emit conflict flow is LIVE in interactive mode (the producer\n // accepts conflict opts; this closes the wiring gap). The safe-default\n // `assertNotUserOwned` runs unconditionally inside the producer.\n await emitHostSkills(root, host, conflictOpts, interactive);\n\n // Write-path semantic dedup. Non-blocking; degrades to a\n // stderr warn-skip when the embedder is unavailable. Records near-dups on\n // `res.conflicts` so `--json` consumers see them without a prompt.\n // Best-effort project read: first-run init has no .noir/config.yml yet →\n // undefined → resolveEmbedder warn-skips. `init --force` on an existing\n // project reads the config and runs the dedup against existing host files.\n let projectInfo: ProjectInfo | undefined;\n try {\n projectInfo = loadProjectInfo(root);\n } catch {\n projectInfo = undefined;\n }\n const dedup = await checkWritePathDedup(root, res, { interactive, project: projectInfo });\n if (dedup.conflicts.length > 0) res.conflicts.push(...dedup.conflicts);\n\n process.stderr.write(\n `Noir initialized in ${root} (host: ${host}, transport: ${opts.transport}).\\n`,\n );\n process.stderr.write(\n 'Next: run `noir` to open the home menu (or `noir status` for a snapshot).\\n',\n );\n return res;\n}\n\n/**\n * F1 — report a dry-run (--dry-run/--preview) scaffold result. After\n * `scaffold({dryRun:true})` the result's `written`/`skipped`/`identical` carry\n * the PLANNED paths (nothing touched disk): `written` = files that WOULD be\n * written, `skipped` = skipIfExists files already present (left alone),\n * `identical` = files whose bytes would match the template (no rewrite).\n * `conflicts` is always empty under dryRun (no writes → no conflicts).\n * `fileModes` carries the planned permission for entries that would CREATE at a\n * non-default mode — annotated onto the planned line (`… (mode 600)`).\n *\n * Emitted via the `log()` stderr helper — a HUMAN diagnostic, so under `--json`\n * the bin emits the planned list as the structured `{ok, data}` envelope on\n * stdout (the data channel) instead, matching the S9 stream discipline used by\n * the other init/create/sync diagnostics. Shared by init/create/sync (the same\n * dryRun surface on all three scaffold modes).\n */\nexport function reportPlannedWrites(res: ScaffoldResult): void {\n log('Dry run — no files were written.');\n if (res.written.length > 0) {\n log('Planned writes:');\n for (const p of res.written) {\n // Slice E (§8.1): a planned creation at a non-default mode is reported\n // here so `noir init --dry-run` shows the 0600 `.noir/.env` seed without\n // writing it. `fileModes` is absent/empty for every other entry.\n const mode = res.fileModes?.[p];\n log(mode === undefined ? ` ${p}` : ` ${p} (mode ${mode.toString(8).padStart(3, '0')})`);\n }\n }\n if (res.skipped.length > 0) {\n log('Would leave as-is (already present):');\n for (const p of res.skipped) log(` ${p}`);\n }\n if (res.identical.length > 0) {\n log('Would rewrite (byte-identical, no-op):');\n for (const p of res.identical) log(` ${p}`);\n }\n}\n\n/**\n * Compose skill emission onto the resolved adapter's `skillsDir` (claude →\n * `.claude/skills/`; cursor → `.cursor/rules/` compiled as `.mdc`; the other\n * three hosts have no skill concept and are skipped with a stderr note).\n *\n * The `CompileTarget` matches the host id (S10 foundation widened the enum to\n * the same union) so cursor skills compile to the `.mdc` rule shape via\n * `compileSkill(_, 'cursor')`; the others keep the verbatim SKILL.md format.\n */\nasync function emitHostSkills(\n root: string,\n host: HostId,\n conflictOpts: ScaffoldConflictOpts,\n interactive: boolean,\n): Promise<void> {\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // N1: standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (Pre-N1 each command phrased this differently.)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n return;\n }\n const target: CompileTarget = host;\n // Forward conflictPolicy + onConflict + interactive so an\n // interactive `noir init` with a conflicting skill emit consults the\n // resolver (clack menu + diff preview + apply-to-all); --json/--no-input\n // stays prompt-free via the `interactive: false` guard. The resolver shape\n // is structurally compatible with the skill emit seam (see skills/types.ts);\n // the cast isolates the literal-narrowing mismatch on the return type.\n type SkillEmitOpts = NonNullable<Parameters<typeof emitSkillsToDir>[1]>;\n const skillOpts: SkillEmitOpts = {\n includeIntegrations: true,\n target,\n conflictPolicy: conflictOpts.conflictPolicy,\n interactive,\n };\n if (conflictOpts.onConflict !== undefined) {\n skillOpts.onConflict = conflictOpts.onConflict as SkillEmitOpts['onConflict'];\n }\n const summary = await emitSkillsToDir(skillsDir, skillOpts);\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Emitted ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n}\n\n/**\n * Transport + URL precondition. The error strings here are the SECURITY GATE\n * locked by `url-validation.test.ts` — do not rephrase. Thrown errors are\n * plain `Error`s (bin.ts's `main().catch` maps them to stderr + exitCode 1).\n *\n * Exported so `noir create` (commands/create.ts) reuses the SAME gate instead\n * of duplicating the localhost allowlist.\n */\nexport function assertTransportUrl(opts: {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n}): void {\n if (opts.transport === 'streamable-http' && opts.url === undefined) {\n throw new Error('--transport streamable-http requires --url');\n }\n if (opts.url !== undefined) {\n assertLocalhostUrl(opts.url);\n }\n}\n\n// Validate a streamable-http --url is http(s) and localhost-only. Gate 2's\n// daemon binds 127.0.0.1, so persisting a non-localhost URL is a footgun.\n// Not exported: the single external surface is assertTransportUrl(); thrown\n// errors propagate to bin.ts's main().catch (stderr + exitCode 1).\nfunction assertLocalhostUrl(raw: string): void {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(`Invalid URL: ${raw}`);\n }\n if (!['http:', 'https:'].includes(url.protocol)) {\n throw new Error('Only http/https URLs are supported');\n }\n if (!['localhost', '127.0.0.1', '::1'].includes(url.hostname)) {\n throw new Error(`Only localhost URLs are supported (got ${url.hostname})`);\n }\n}\n","import { applyNoirEnv, loadProjectInfo } from '@noir-ai/core';\nimport { ensureDaemonRunning, startStdioServer } from '@noir-ai/daemon';\n\nexport async function serve(opts: { stdio: boolean }): Promise<void> {\n // Load .noir/.env before constructing the MCP server so integration tokens\n // (e.g. CLICKUP_API_TOKEN) are resolvable at call time even when the daemon\n // was launched from a context that did not inherit the shell rc.\n applyNoirEnv(process.cwd());\n const project = loadProjectInfo(process.cwd());\n if (opts.stdio) {\n await startStdioServer({ project, transport: 'stdio', daemon: false });\n return; // stdio server runs until stdin closes\n }\n // Prefer the shared daemon; on failure fall back to in-process stdio so Noir\n // keeps working even when the daemon cannot start (FS-degradation path).\n try {\n const { url } = await ensureDaemonRunning({\n project,\n idleTimeoutSec: project.config.daemon.idleTimeoutSec,\n ...(project.config.daemon.port !== undefined ? { port: project.config.daemon.port } : {}),\n });\n process.stderr.write(\n `Noir daemon available at ${url}. (For HTTP clients, use this URL in .mcp.json.) ` +\n `Add the daemon's bearer token via the host MCP headersHelper (run \\`noir daemon token\\`).\\n`,\n );\n } catch (err) {\n process.stderr.write(\n `Noir daemon unavailable (${err instanceof Error ? err.message : String(err)}); falling back to stdio.\\n`,\n );\n await startStdioServer({ project, transport: 'stdio', daemon: false });\n }\n}\n","// `noir create [dir]` — greenfield first-run for the AI layer only.\n//\n// Slice S — new command. Bootstraps Noir's AI layer (.noir/ store, host\n// pointers, skills) into `dir` (created if absent), with NO external\n// scaffolder chaining (S-OQ1 = AI-layer only; no `pnpm create` wrapping).\n//\n// Reuses init's transport/url handling (including the localhost security\n// gate) by calling into the same engine: `scaffold({mode:'create', root})`.\n// The only difference from `noir init` is the mode tag (which selects the\n// manifest's first-run subset + generates a fresh project id unconditionally)\n// and that the target dir is `resolve(dir ?? cwd)` rather than `process.cwd()`.\n//\n// S10 multi-host: gains `--host <id>` (default `'claude'`); the host drives\n// both scaffold emission + skills emission via `resolveAdapter(host)` —\n// shared with init through `emitHostSkills`. See `init.ts` for the matrix.\n//\n// Stream discipline matches init: diagnostics → stderr, nothing on stdout\n// (so `noir create --json` is safe by construction — there is no data payload).\n//\n// Exit codes: `assertTransportUrl` is called DIRECTLY (M2) so transport/url\n// errors propagate as plain `Error` → bin.ts's handleError → exit 1, EXACTLY\n// matching `noir init` for the same error class. (The S9 spec lists usage=2 for\n// a missing `--url`; neither `init` nor `create` implements that split today —\n// both report it as a plain error=1 — and the review asked for consistency\n// over a one-sided fix. Changing them together is a separate, contract-level\n// change.) The target dir is created by the engine's `create` mode (N1: the\n// duplicate mkdir that used to live here is gone — one attributable failure\n// point, in scaffold.ts).\n\nimport { resolve } from 'node:path';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, type ProjectInfo } from '@noir-ai/core';\nimport { type ScaffoldResult, scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from '../conflict.js';\nimport { checkWritePathDedup } from '../dedup-write.js';\nimport { assertTransportUrl, reportPlannedWrites } from '../init.js';\nimport { resolveInteractive } from '../output.js';\n\nexport interface CreateOptions {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n /** S10 target host. Defaults to `'claude'`. */\n host?: HostId;\n /** SP-A: re-scaffold even if already initialized. */\n force?: boolean;\n /** F1: `--dry-run`/`--preview` — report the planned writes to stderr\n * without touching disk (incl. NOT creating the target dir). */\n dryRun?: boolean;\n /** F1: alias for `--dry-run`. Kept on the options bag so direct callers can\n * pass either spelling; the bin collapses both flags before dispatch. */\n preview?: boolean;\n}\n\n/**\n * Bootstrap Noir's AI layer in `dir`. Returns the {@link ScaffoldResult} (with\n * structured `conflicts[]` + any dedup records appended); the bin\n * emits it under `--json`. `undefined` when the already-initialized guard\n * short-circuited (a no-op).\n *\n * @param dir Target directory (created if absent). Defaults to `process.cwd()`.\n * @param opts Transport + URL + host (same semantics as `noir init`).\n */\nexport async function create(\n dir: string | undefined,\n opts: CreateOptions,\n): Promise<ScaffoldResult | undefined> {\n // M2: call assertTransportUrl directly so the SAME error class yields the SAME\n // exit code as `noir init` (plain Error → exit 1 for both missing-url and\n // non-localhost). The previous NoirCliError(EXIT.USAGE) wrapper made `create`\n // exit 2 where `init` exits 1.\n assertTransportUrl(opts);\n\n const root = resolve(dir ?? process.cwd());\n const host: HostId = opts.host ?? 'claude';\n // Derive interactive once (consistency with init/sync) so both scaffold\n // and skills emission see the same hermetic flag.\n const interactive = resolveInteractive();\n const conflictOpts = buildConflictOpts({ force: opts.force, interactive });\n // F1: --dry-run/--preview collapse to a single dryRun boolean. The engine\n // skips the target-dir mkdir + every write; skills emission + dedup are\n // skipped too (they would touch disk / load the embedder).\n const dryRun = opts.dryRun === true || opts.preview === true;\n\n const res = await scaffold({\n root,\n mode: 'create',\n host,\n transport: opts.transport,\n interactive,\n ...(opts.url !== undefined ? { url: opts.url } : {}),\n ...(opts.force === true ? { force: true } : {}),\n ...(dryRun ? { dryRun: true } : {}),\n ...conflictOpts,\n });\n // F1: dry-run reports the planned writes and stops BEFORE skills emission +\n // the \"created\" message — the target dir was NOT created, so nothing may\n // touch disk. (The engine's create-mode mkdir is gated on !dryRun.)\n if (dryRun) {\n reportPlannedWrites(res);\n return res;\n }\n // SP-A: a no-op (already-initialized guard) must not re-emit skills.\n if (res.noop) return res;\n\n // Skills are out-of-manifest by design — same composition as init.\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // N1: standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (Pre-N1 each command phrased this differently.)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n } else {\n const target: CompileTarget = host;\n // Forward conflictOpts so an interactive `noir create` with a\n // conflicting skill emit consults the resolver (mirrors init/sync).\n type SkillEmitOpts = NonNullable<Parameters<typeof emitSkillsToDir>[1]>;\n const skillOpts: SkillEmitOpts = {\n includeIntegrations: true,\n target,\n conflictPolicy: conflictOpts.conflictPolicy,\n interactive,\n };\n if (conflictOpts.onConflict !== undefined) {\n skillOpts.onConflict = conflictOpts.onConflict as SkillEmitOpts['onConflict'];\n }\n const summary = await emitSkillsToDir(skillsDir, skillOpts);\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Emitted ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n }\n\n // Write-path semantic dedup. Greenfield create rarely has\n // existing host-context candidates (the target dir is fresh), so the fast\n // path fires; `--force` on a pre-existing Noir project reads the config and\n // runs the full dedup. Best-effort: a missing/corrupt config warn-skips.\n let projectInfo: ProjectInfo | undefined;\n try {\n projectInfo = loadProjectInfo(root);\n } catch {\n projectInfo = undefined;\n }\n const dedup = await checkWritePathDedup(root, res, { interactive, project: projectInfo });\n if (dedup.conflicts.length > 0) res.conflicts.push(...dedup.conflicts);\n\n process.stderr.write(`Noir created in ${root} (host: ${host}, transport: ${opts.transport}).\\n`);\n process.stderr.write('Next: cd into the new directory, then run `noir` to open the home menu.\\n');\n return res;\n}\n","// `noir sync` — re-emit the runtime subset of the scaffold manifest.\n//\n// Slice S-T2 refactor: delegates to `@noir-ai/create`'s `scaffold({mode:'sync'})`,\n// which emits ONLY the regenerate + managedBlock entries (the always-safe-to-\n// rewrite subset). Per the S-T2 contract notes, this ADOPTS THE ENGINE'S\n// SEMANTICS and retires the predecessor's ad-hoc behavior:\n//\n// - The engine re-emits `.mcp.json` (regenerate) + NOIR.md brief\n// (managedBlock) + CLAUDE.md context/rules blocks + ignore-file blocks.\n// The predecessor's sync re-emitted only CLAUDE.md blocks + ignores and\n// did NOT refresh `.mcp.json`/NOIR.md — the engine is a strict superset\n// here, so a transport/url change is now picked up by `noir sync`.\n// - The engine does NOT seed `.noir/rules/RULES.md` on sync (RULES.md is\n// skipIfExists, owned by init/create). The predecessor seeded it when\n// missing; init now owns all seeds. (Spec-aligned: \"sync re-emits\n// generated/managed content; init owns seeds.\")\n//\n// S10 multi-host: sync resolves the host from `.noir/config.yml` (persisted by\n// `noir init --host <id>` via the config seed's `host: {{host}}` literal) OR\n// from an explicit `--host <id>` override. The resolved adapter drives the\n// manifest + skills emission. Skills emission stays a core sync feature for\n// hosts with a `skillsDir` (claude → `.claude/skills/`; cursor → `.cursor/rules/`\n// as `.mdc`); gemini/agents-md/opencode have no skill concept and the step is\n// skipped. `scaffold({mode:'sync'})` throws when the project isn't initialized\n// (no .noir/project.id), matching the predecessor's `loadProjectInfo` gate.\n\nimport { existsSync } from 'node:fs';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, paths } from '@noir-ai/core';\nimport { type ScaffoldResult, scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from './conflict.js';\nimport { checkWritePathDedup } from './dedup-write.js';\nimport { reportPlannedWrites } from './init.js';\nimport { resolveInteractive } from './output.js';\n\nexport interface SyncOptions {\n /** S10 `--host <id>` override. When set, takes precedence over the\n * `.noir/config.yml` `host:` field. Useful for re-emitting under a\n * different host without re-init (advanced — the canonical host stays\n * whatever init wrote). */\n host?: HostId;\n /** SP-C: overwrite differing regenerated files without prompting (bypasses\n * the conflict menu). */\n force?: boolean;\n /** SP-D: three-way merge managed regions (preserve hand-edits inside\n * `<!-- noir:* -->` markers across a template update). DEFAULT TRUE;\n * `--merge` is now a no-op (kept for backward compatibility). */\n merge?: boolean;\n /** Opt OUT of managed-region merge (restore strip-replace). Exposed on\n * the bin as `--no-merge-regions`. When explicitly `false`, hand-edits\n * inside `<!-- noir:* -->` markers are discarded on a template upgrade. */\n mergeManagedRegions?: boolean;\n /** F1: `--dry-run`/`--preview` — report the planned writes to stderr\n * without touching disk. sync still requires a valid `.noir/project.id`\n * (the engine's \"not initialized\" gate fires regardless). */\n dryRun?: boolean;\n /** F1: alias for `--dry-run`. Kept on the options bag so direct callers can\n * pass either spelling; the bin collapses both flags before dispatch. */\n preview?: boolean;\n}\n\nexport async function sync(root: string, opts: SyncOptions = {}): Promise<ScaffoldResult> {\n const host = resolveSyncHost(root, opts);\n // The engine reads ScaffoldOptions.interactive (hermetic — never\n // process.env). The CLI derives it once from the bridge + TTY/CI/NO_COLOR gate.\n const interactive = resolveInteractive();\n const conflictOpts = buildConflictOpts({ force: opts.force, interactive });\n\n // Load project info once for both the dedup hook (embedder config)\n // and the existing config-host fallback. Best-effort: a missing/corrupt file\n // is left to scaffold's \"not initialized\" gate below.\n let projectInfo: ReturnType<typeof loadProjectInfo> | undefined;\n try {\n projectInfo = loadProjectInfo(root);\n } catch {\n projectInfo = undefined;\n }\n\n // F1: --dry-run/--preview collapse to a single dryRun boolean.\n const dryRun = opts.dryRun === true || opts.preview === true;\n\n const res = await scaffold({\n root,\n mode: 'sync',\n host,\n interactive,\n ...(dryRun ? { dryRun: true } : {}),\n ...conflictOpts,\n // Merge defaults TRUE inside scaffold; only forward an explicit opt-out.\n ...(opts.mergeManagedRegions === false ? { mergeManagedRegions: false } : {}),\n });\n\n // F1: dry-run reports the planned writes and stops BEFORE skills emission +\n // dedup (both would touch disk / load the embedder). The engine still throws\n // its \"not initialized\" gate when `.noir/project.id` is missing, so a dry-run\n // sync on an uninitialized project is a clean error (not a misleading no-op).\n if (dryRun) {\n reportPlannedWrites(res);\n return res;\n }\n\n // Surface the no-op so users see sync was a true no-op on disk (the\n // scaffold wrote nothing — every runtime file was content-hash identical).\n // Skills emission below still runs (it has its own dedup).\n if (res.written.length === 0 && res.identical.length > 0) {\n process.stderr.write('Nothing to sync (Noir-managed files are up to date).\\n');\n }\n\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // N1: standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (Pre-N1 sync phrased this as \"nothing to sync\".)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n } else {\n const target: CompileTarget = host;\n // Forward conflictOpts so the skills-emit conflict flow is\n // live in interactive mode (mirrors init.ts). --json/--no-input stays\n // prompt-free via the `interactive: false` guard.\n type SkillEmitOpts = NonNullable<Parameters<typeof emitSkillsToDir>[1]>;\n const skillOpts: SkillEmitOpts = {\n includeIntegrations: true,\n target,\n conflictPolicy: conflictOpts.conflictPolicy,\n interactive,\n };\n if (conflictOpts.onConflict !== undefined) {\n skillOpts.onConflict = conflictOpts.onConflict as SkillEmitOpts['onConflict'];\n }\n const summary = await emitSkillsToDir(skillsDir, skillOpts);\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Synced ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n // Surface stale-dir pruning so a user can see when a previous Noir\n // version's builtin was removed (the dir was deleted from .claude/skills/).\n // Pure hygiene; never affects correctness of the freshly-emitted pack.\n const pruned = summary.pruned ?? [];\n if (pruned.length > 0) {\n process.stderr.write(\n `Pruned ${pruned.length} stale noir-* skill dir${pruned.length === 1 ? '' : 's'}: ${pruned.join(', ')}\\n`,\n );\n }\n }\n\n // Write-path semantic dedup. Non-blocking; degrades to a\n // stderr warn-skip when the embedder is unavailable. Records near-dups on\n // `res.conflicts` so `--json` consumers see them without a prompt.\n const dedup = await checkWritePathDedup(root, res, { interactive, project: projectInfo });\n if (dedup.conflicts.length > 0) res.conflicts.push(...dedup.conflicts);\n\n return res;\n}\n\n/** Resolve the sync host: explicit `--host` override > `.noir/config.yml`\n * `host:` field > `'claude'` (default — preserves the v1.1 regression anchor\n * for projects initialized before S10). The config read is best-effort: if\n * `.noir/config.yml` is absent OR fails to parse, scaffold's own\n * \"not initialized\" gate (no `.noir/project.id`) fires below with the locked\n * error string. */\nfunction resolveSyncHost(root: string, opts: SyncOptions): HostId {\n if (opts.host !== undefined) return opts.host;\n if (!existsSync(paths.config(root))) return 'claude';\n try {\n return loadProjectInfo(root).config.host;\n } catch {\n return 'claude';\n }\n}\n","// `noir` bin entry. Migrated from the hand-rolled `parseArgs` dispatcher onto a\n// commander Command tree (S9 t1). Behavior of init / sync / mcp serve / daemon\n// start|stop / doctor is preserved flag-for-flag; the only change is the dispatch\n// path and the new global flags + exit-code contract.\n//\n// Testability: commander is configured with `.exitOverride()` so it NEVER calls\n// `process.exit`. The exported `createProgram()` factory returns a fresh, fully\n// wired program for unit tests; `run(argv)` is the bin's own entry and maps any\n// thrown error to `process.exitCode` (Node then exits naturally with that code).\n// A main-module guard prevents auto-running when bin.ts is imported by tests.\n\nimport { realpathSync } from 'node:fs';\nimport { pathToFileURL } from 'node:url';\nimport { type HostId, SUPPORTED_HOSTS } from '@noir-ai/adapters';\nimport { applyNoirEnv, NOIR_VERSION } from '@noir-ai/core';\nimport { Command, Option } from 'commander';\nimport { contextIndex, contextSearch, contextStatus } from './commands/context.js';\nimport {\n daemonRestart,\n daemonStart,\n daemonStatus,\n daemonStop,\n daemonToken,\n} from './commands/daemon.js';\nimport { doctor } from './commands/doctor.js';\nimport { captureAmbientEnv, env } from './commands/env.js';\nimport { type HandoffOptions, handoff } from './commands/handoff.js';\nimport { type HomeDeps, home } from './commands/home.js';\nimport {\n memoryCapture,\n memoryConsolidate,\n memoryForget,\n memoryRecall,\n memorySave,\n memorySessions,\n} from './commands/memory.js';\nimport { release } from './commands/release.js';\nimport { type RunOptions, run as runHostCommand } from './commands/run.js';\nimport { skillsLint, skillsList, skillsRegistry, skillsSync } from './commands/skills.js';\nimport { type StatusOptions, status } from './commands/status.js';\nimport {\n taskAbandon,\n taskAdvance,\n taskBlock,\n taskDecompose,\n taskNew,\n taskNext,\n taskResearch,\n taskResearchRecord,\n taskResume,\n taskStatus,\n taskVerify,\n} from './commands/task.js';\nimport {\n daemonJoin,\n workspaceLeave,\n workspaceList,\n workspaceStatus,\n workspaceStop,\n} from './commands/workspace.js';\nimport { init } from './init.js';\nimport {\n type CliOptions,\n EXIT,\n fail,\n handleError,\n inferExitCode,\n info,\n json,\n NoirCliError,\n requireInteractive,\n table,\n tip,\n} from './output.js';\nimport { listProfiles, loadRunConfig } from './run-profiles.js';\nimport { serve } from './serve.js';\nimport { buildPaletteCommands } from './tui/commands/registry.js';\nimport type { PaletteCommand } from './tui/palette/types.js';\n\n// Exit-code contract, error type, `fail`, and exit-code mapping live in\n// `./output.js` (S9 t2 central output infra). Re-exported here so existing\n// imports from `./bin.js` (bin.test.ts, future commands) keep working without\n// a second source of truth.\nexport { EXIT, fail, inferExitCode, NoirCliError };\n\n/**\n * The TUI palette source (B3). Walks a FRESH {@link createProgram} at `noir tui`\n * launch and projects every leaf subcommand into a {@link PaletteCommand}.\n * Defined here (not in the tui) so the palette derives from the REAL command\n * tree without the tui graph importing the bin (which would be circular).\n */\nexport function buildPaletteCommandsForTui(): PaletteCommand[] {\n return buildPaletteCommands(createProgram());\n}\n\n// ---------------------------------------------------------------------------\n// Program factory. Each call returns a fresh, independently-parseable Command\n// (commander parse state is mutable, so tests should drive a fresh program).\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Action-option bridge. Commander passes each action handler the positional\n// args + options + the action's Command as the LAST argument. The global flags\n// (`--json/--no-input/--quiet/--verbose/--cwd`, registered `` on\n// the program) land on `command.optsWithGlobals()`. The two helpers below read\n// those globals off the trailing Command without depending on how many params\n// commander injected in front of it (it varies with the command's arity), so a\n// single pattern works for every action.\n// ---------------------------------------------------------------------------\nfunction actionGlobals(args: readonly unknown[]): Record<string, unknown> {\n // `noUncheckedIndexedAccess` → element access yields `unknown`; the optional\n // chain + cast handles the undefined case without a runtime branch.\n const last = args[args.length - 1];\n return (last as Command | undefined)?.optsWithGlobals() ?? {};\n}\n\n/**\n * The trailing {@link Command} commander always passes as the LAST action\n * argument. Used by the context/memory/task actions to read their own options\n * (`cmd.opts()`) + globals (`cmd.optsWithGlobals()`) without depending on how\n * many positional args commander injected in front of it. Positional values are\n * read off the FRONT of `args` (commander puts positionals first, then the\n * options object, then the command). Throws on a malformed call rather than\n * silently coercing `undefined`.\n */\nfunction trailingCmd(args: readonly unknown[]): Command {\n const last = args[args.length - 1];\n if (!(last instanceof Command)) {\n throw new NoirCliError(\n EXIT.ERROR,\n 'internal: commander action callback received no trailing Command',\n );\n }\n return last;\n}\n\n/** Map commander's parsed global-flag bag onto the CLI's {@link CliOptions}. */\nfunction toCliOptions(g: Record<string, unknown>): CliOptions {\n // `--no-input` is stored under `input` (commander strips the `no-` prefix);\n // default `true`, so `input !== false` ⇒ input allowed.\n return {\n json: g.json === true,\n quiet: g.quiet === true,\n verbose: g.verbose === true,\n input: g.input !== false,\n // TUI policy — additive only. `--tui`/`--no-tui` land on `tui`\n // (true/false; absent when neither flag is given). Spread conditionally so\n // the default-args case (no flag) does NOT add a `tui` key — keeps the\n // exact-shape assertions in bin.test.ts green.\n ...(g.tui === true || g.tui === false ? { tui: g.tui === true } : {}),\n // `--no-tips` is stored under `tips` (default true; flag ⇒ false). Map to\n // the additive `noTips` spelling used by CliOptions + `tip()`.\n ...(g.tips === false ? { noTips: true } : {}),\n };\n}\n\n/** {@link toCliOptions} + the daemon-client knobs `status` needs. */\nfunction toStatusOptions(g: Record<string, unknown>): StatusOptions {\n return {\n json: g.json === true,\n quiet: g.quiet === true,\n verbose: g.verbose === true,\n input: g.input !== false,\n ...(g.tips === false ? { noTips: true } : {}),\n };\n}\n\n/**\n * S10 — narrow commander's `string | undefined` host option to `HostId |\n * undefined`. Commander's `.choices(SUPPORTED_HOSTS)` already rejects unknown\n * values at parse time (usage=2), so by the time the action runs any string\n * present IS one of `SUPPORTED_HOSTS`. The cast is therefore total — but kept\n * explicit (not a type assertion) so a future change to the choices wiring\n * surfaces here rather than silently coercing. Returns `undefined` for an\n * unset flag so callers can conditional-spread (preserves the exact-arg-shape\n * assertions in bin.test.ts).\n */\nfunction parseHost(raw: string | undefined): HostId | undefined {\n if (raw === undefined) return undefined;\n // The cast is safe: commander's `.choices` enforces it pre-action.\n return raw as HostId;\n}\n\n// ---------------------------------------------------------------------------\n// TUI policy + deprecation / redirect infrastructure.\n//\n// Approach B (locked): TUI-primary UX, but NEVER hard-gate any subcommand. The\n// bare `noir` home menu is the documented human entry point; every subcommand\n// stays 100% scriptable. `--json` is the headless contract. The deprecation\n// registry below is the formal \"warn for N → redirect for N → never silently\n// remove\" channel; ZERO entries today (no command is deprecated).\n// ---------------------------------------------------------------------------\n\n/** One entry in the {@link DEPRECATIONS} registry. */\nexport interface DeprecationEntry {\n /** Old command form as a path prefix, e.g. `['legacy-status']` or\n * `['context','legacy']`. Matched against the dispatched command's path\n * (program name `noir` stripped). */\n readonly oldArgv: readonly string[];\n /** Replacement command form, e.g. `['status']`. Shown in the redirect hint. */\n readonly newArgv: readonly string[];\n /** Version the deprecation started in, e.g. `'1.4.0'`. */\n readonly since: string;\n}\n\n/**\n * Deprecation registry. **Empty today** — no `noir` command is deprecated.\n * When adding an entry, update the CHANGELOG + `docs/reference/cli.md` (and,\n * if the old form still routes, add a redirect). {@link emitDeprecationHintsFor}\n * scans this on every dispatch and emits one `tip()` per match; `--no-tips` /\n * `--json` suppress the hint (see {@link tip}).\n */\nexport const DEPRECATIONS: DeprecationEntry[] = [];\n\n/**\n * Build the dispatched command's path (e.g. `['noir','context','search']`) by\n * walking `.parent` up to the program root. Used by {@link emitDeprecationHintsFor}\n * to match an {@link DeprecationEntry.oldArgv} prefix (program name stripped).\n */\nfunction commandPath(cmd: Command): string[] {\n const path: string[] = [];\n let c: Command | null = cmd;\n while (c !== null) {\n path.unshift(c.name());\n c = c.parent;\n }\n return path;\n}\n\n/**\n * Emit one redirect hint per matching {@link DEPRECATIONS} entry for the\n * dispatched command. Respects `--no-tips` and `--json` via {@link tip} (a CI\n * run's stderr stays quiet and its stdout envelope stays pristine). Exported so\n * tests can drive it with a temporarily-populated registry without going\n * through commander.\n */\nexport function emitDeprecationHintsFor(cmd: Command, opts: CliOptions): void {\n if (DEPRECATIONS.length === 0) return; // common case (no entries today)\n // Strip the leading program name ('noir') for matching.\n const rel = commandPath(cmd).slice(1);\n for (const d of DEPRECATIONS) {\n if (d.oldArgv.every((a, i) => rel[i] === a)) {\n tip(\n `\\`noir ${d.oldArgv.join(' ')}\\` is deprecated since v${d.since}; use \\`noir ${d.newArgv.join(' ')}\\`.`,\n opts,\n );\n }\n }\n}\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name('noir')\n .description('Noir — discipline, context, and memory layer for agentic CLIs.')\n .version(NOIR_VERSION, '-v, --version')\n // Global flags (S9). makeGlobal() propagates each to every subcommand so\n // they parse in any position (e.g. `noir status --json` as well as `noir\n // --json status`) and appear on subcommand --help.\n .addOption(new Option('--json', 'emit machine-readable JSON to stdout'))\n .addOption(new Option('--no-input', 'never prompt; error if input is required'))\n .addOption(new Option('--quiet', 'suppress non-essential diagnostics'))\n .addOption(new Option('--verbose', 'show additional diagnostic detail'))\n .addOption(new Option('--cwd <path>', 'run as if started in <path>'))\n // TUI policy — advisory routing for bare `noir` only (never hard-gates a\n // subcommand). `--tui`/`--no-tui` are a commander negatable pair: parsed\n // onto a single `tui` attribute (true / false / absent=auto). Bare `noir`\n // in a TTY runs the home menu by default; `--no-tui` forces the\n // non-interactive `status` path even in a TTY; `--tui` is a hint that still\n // requires a TTY (so it never blocks in CI / a pipe). See home.ts + CLAUDE.md.\n .addOption(\n new Option('--tui', 'prefer the interactive home menu for bare `noir` (advisory; TTY-only)'),\n )\n .addOption(\n new Option(\n '--no-tui',\n 'route bare `noir` to the non-interactive `status` path even in a TTY',\n ),\n )\n // Deprecation / redirect hints — `--no-tips` silences the `tip()` helper\n // for CI / log-friendly runs. No command is deprecated today; the flag is\n // the headless contract for quieting future notices.\n .addOption(new Option('--no-tips', 'suppress redirect / deprecation hints on stderr'))\n .exitOverride((err) => {\n // Never process.exit; surface to the caller (run()/test) instead.\n throw err;\n })\n .configureOutput({\n // Stream discipline: --help/--version → stdout (conventional; users pipe\n // them), errors → stderr. --json data is emitted by command actions via\n // process.stdout directly, NOT by commander's writeOut, so routing help/\n // version to stdout does not conflict with --json payloads.\n writeOut: (str) => process.stdout.write(str),\n writeErr: (str) => process.stderr.write(str),\n });\n\n // --cwd: chdir before the action runs so the existing modules (which read\n // process.cwd()) honor it. actionCmd.optsWithGlobals() works on whichever\n // program instance is being parsed, so createProgram() isolates correctly.\n program.hook('preAction', (_thisCmd, actionCmd) => {\n const opts = actionCmd.optsWithGlobals();\n const cwd = opts.cwd;\n if (typeof cwd === 'string' && cwd.length > 0) {\n try {\n process.chdir(cwd);\n } catch (err) {\n throw new NoirCliError(\n EXIT.USAGE,\n `--cwd: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n // Load project-local .noir/.env AFTER the --cwd chdir so the .env root\n // matches the effective project (a `--cwd /other` run must never inherit the\n // launch dir's tokens into the host subprocess). Idempotent — and spec 12.1\n // inverts the usual dotenv rule: a key the file DEFINES wins, and the real\n // environment is the fallback for keys the file omits. The overlay is\n // confined to this process tree (see applyNoirEnv), so a sibling shell and\n // the user's own manual `claude` invocations are untouched.\n //\n // `noir env` is the ONE command that must also see the AMBIENT environment\n // as it was before that overlay: reporting \"the file's value won, and the\n // one you exported is being ignored\" needs the pre-overlay values, which are\n // indistinguishable from the file's own afterwards. Capture it for that\n // command only — every other command just applies the overlay.\n if (actionCmd.name() === 'env') captureAmbientEnv();\n applyNoirEnv(process.cwd());\n // SP-G: propagate --json / --no-input to the deep conflict resolver via env\n // so a regenerate conflict never prompts under those flags (the @clack\n // prompt writes to stdout — it would corrupt --json output and violate\n // --no-input). Done here centrally so init/create/sync need no arg changes\n // (no bin.test.ts arg-pin cascade). buildConflictOpts reads this flag.\n const nonInteractive = opts.json === true || opts.input === false;\n if (nonInteractive) process.env.NOIR_NON_INTERACTIVE = '1';\n else delete process.env.NOIR_NON_INTERACTIVE;\n // Deprecation / redirect hints. Scans {@link DEPRECATIONS} against the\n // dispatched command path; emits via `tip()` (suppressed by --no-tips /\n // --json). Empty registry today — no-op until an entry is added.\n emitDeprecationHintsFor(actionCmd, toCliOptions(opts));\n });\n\n // ----- migrated commands (behavior-preserving) -----\n\n program\n .command('init')\n .description('scaffold Noir in the current project (.noir/, .mcp.json, CLAUDE.md, skills)')\n .option('--transport <transport>', 'stdio | streamable-http (default: stdio)', 'stdio')\n .option('--url <url>', 'streamable-http daemon URL (localhost only)')\n .option(\n '--upgrade',\n 'run scaffold migrations before re-emitting (re-run on an existing project)',\n )\n .option('--force', 're-scaffold even if already initialized (bypasses the already-init no-op)')\n .option('--dry-run', 'report planned writes without writing anything')\n .option('--preview', 'alias for --dry-run')\n .addOption(\n // S10: target host. Defaults to `'claude'` (the regression anchor). The\n // chosen host is forwarded to scaffold() + skills emission via\n // resolveAdapter. The choice list mirrors `SUPPORTED_HOSTS` so a new host\n // lands here automatically; commander rejects anything else as usage=2.\n new Option(\n '--host <id>',\n 'target agentic CLI (default: claude) — drives host-side emission',\n ).choices(SUPPORTED_HOSTS),\n )\n .action(\n async (\n opts: {\n transport?: string;\n url?: string;\n upgrade?: boolean;\n force?: boolean;\n host?: string;\n dryRun?: boolean;\n preview?: boolean;\n },\n cmd: Command,\n ) => {\n // Preserve the parseArgs coercion exactly: only 'streamable-http' is\n // special; any other value (incl. typos / future transports) → 'stdio'.\n const transport: 'stdio' | 'streamable-http' =\n opts.transport === 'streamable-http' ? 'streamable-http' : 'stdio';\n // `upgrade`/`host` are conditionally spread so an UNSET flag does NOT add\n // an `upgrade: false` / `host: …` key to the opts object — that would\n // break the toEqual-based arg assertions in bin.test.ts (which expects\n // exactly `{transport, url}` for the default-invocation cases). Matches\n // the conditional-spread pattern used for task/memory options elsewhere.\n const upgrade = opts.upgrade === true;\n const force = opts.force === true;\n const host = parseHost(opts.host);\n // F1: --dry-run/--preview collapse to a single dryRun boolean (the\n // command modules read both spellings, but the bin normalizes).\n const dryRun = opts.dryRun === true || opts.preview === true;\n const result = await init(process.cwd(), {\n transport,\n url: opts.url,\n ...(upgrade ? { upgrade } : {}),\n ...(force ? { force } : {}),\n ...(host !== undefined ? { host } : {}),\n ...(dryRun ? { dryRun } : {}),\n });\n // ScaffoldResult gap close: surface the structured ScaffoldResult (with\n // `conflicts[]`) on stdout under `--json`, wrapped in the versioned\n // `{ok, data}` envelope so it matches the headless contract every read\n // command uses. The args passed to init() are UNCHANGED (bin.test.ts\n // arg pins still hold); only the return value is captured + emitted.\n // `init` returns `undefined` on the already-initialized no-op — skip.\n if (cmd.optsWithGlobals().json === true && result !== undefined) {\n json({ ok: true, data: result });\n }\n },\n );\n\n // `noir create [dir]` — greenfield AI-layer bootstrap (slice S). Lazy import\n // mirrors sync's dispatcher so the create module isn't loaded for unrelated\n // commands. Optional `[dir]` defaults to process.cwd() inside the action.\n program\n .command('create [dir]')\n .description('bootstrap the Noir AI layer in a new or empty directory')\n .option('--transport <transport>', 'stdio | streamable-http (default: stdio)', 'stdio')\n .option('--url <url>', 'streamable-http daemon URL (localhost only)')\n .option('--force', 're-scaffold even if already initialized (bypasses the already-init no-op)')\n .option('--dry-run', 'report planned writes without writing anything')\n .option('--preview', 'alias for --dry-run')\n .addOption(\n new Option(\n '--host <id>',\n 'target agentic CLI (default: claude) — drives host-side emission',\n ).choices(SUPPORTED_HOSTS),\n )\n .action(\n async (\n dir: string | undefined,\n opts: {\n transport?: string;\n url?: string;\n force?: boolean;\n host?: string;\n dryRun?: boolean;\n preview?: boolean;\n },\n cmd: Command,\n ) => {\n const transport: 'stdio' | 'streamable-http' =\n opts.transport === 'streamable-http' ? 'streamable-http' : 'stdio';\n const force = opts.force === true;\n const host = parseHost(opts.host);\n // F1: --dry-run/--preview collapse to a single dryRun boolean.\n const dryRun = opts.dryRun === true || opts.preview === true;\n const { create } = await import('./commands/create.js');\n const result = await create(dir, {\n transport,\n url: opts.url,\n ...(force ? { force } : {}),\n ...(host !== undefined ? { host } : {}),\n ...(dryRun ? { dryRun } : {}),\n });\n // ScaffoldResult gap close: surface the structured ScaffoldResult (with\n // `conflicts[]`) on stdout under `--json`, wrapped in the `{ok, data}`\n // envelope (headless contract). Skipped on the no-op return.\n if (cmd.optsWithGlobals().json === true && result !== undefined) {\n json({ ok: true, data: result });\n }\n },\n );\n\n program\n .command('sync')\n .description(\n 're-emit Noir managed files (.mcp.json, CLAUDE.md blocks, NOIR.md brief, ignores) + skills',\n )\n .option(\n '--force',\n 'overwrite differing regenerated files without prompting (bypasses the conflict menu)',\n )\n .option(\n '--merge',\n 'three-way merge managed regions (default since 1.3.0; flag kept for compatibility)',\n )\n .option('--dry-run', 'report planned writes without writing anything')\n .option('--preview', 'alias for --dry-run')\n .addOption(\n // Opt OUT of managed-region merge (restore strip-replace). Commander\n // stores `--no-merge-regions` under `mergeRegions` (default true; flag →\n // false). Bare `noir sync` keeps the merge default (true).\n new Option(\n '--no-merge-regions',\n 'strip-replace managed regions (discard hand-edits inside <!-- noir:* --> markers)',\n ),\n )\n .addOption(\n // S10: optional `--host` override. When omitted, sync reads host from\n // `.noir/config.yml` (whatever `noir init --host <id>` persisted). The\n // override is rarely needed — documented as advanced.\n new Option(\n '--host <id>',\n 'override the configured host (advanced; default reads .noir/config.yml)',\n ).choices(SUPPORTED_HOSTS),\n )\n .action(\n async (\n opts: {\n host?: string;\n force?: boolean;\n merge?: boolean;\n mergeRegions?: boolean;\n dryRun?: boolean;\n preview?: boolean;\n },\n cmd: Command,\n ) => {\n // Lazy import preserves the original dispatcher's deferred module load.\n const { sync } = await import('./sync.js');\n const host = parseHost(opts.host);\n const force = opts.force === true;\n const merge = opts.merge === true;\n // `--no-merge-regions` → commander stores `mergeRegions: false`.\n const noMergeRegions = opts.mergeRegions === false;\n // F1: --dry-run/--preview collapse to a single dryRun boolean.\n const dryRun = opts.dryRun === true || opts.preview === true;\n // Single-positional regression anchor: when no `--host`/`--force`/`--merge`/\n // `--no-merge-regions`/`--dry-run` is given, call `sync(cwd)` exactly\n // (bin.test.ts pins this). Only spread the opts bag when a flag was\n // explicit so the default-args snapshot stays green.\n const result =\n host === undefined && !force && !merge && !noMergeRegions && !dryRun\n ? await sync(process.cwd())\n : await sync(process.cwd(), {\n ...(host !== undefined ? { host } : {}),\n ...(force ? { force } : {}),\n ...(merge ? { merge } : {}),\n ...(noMergeRegions ? { mergeManagedRegions: false } : {}),\n ...(dryRun ? { dryRun } : {}),\n });\n // ScaffoldResult gap close: surface the structured ScaffoldResult (with\n // `conflicts[]`) on stdout under `--json`, wrapped in the `{ok, data}`\n // envelope (headless contract). sync() always returns a ScaffoldResult\n // (never undefined), so the guard is the json flag only.\n if (cmd.optsWithGlobals().json === true) {\n json({ ok: true, data: result });\n }\n },\n );\n\n // `mcp` group — preserve legacy bare-`mcp` usage error.\n const mcpCmd = program.command('mcp').description('MCP server control');\n mcpCmd\n .command('serve')\n .description('run the Noir MCP server (stdio, or via the shared daemon)')\n .option('--stdio', 'force the stdio transport')\n .action(async (opts: { stdio?: boolean }) => {\n await serve({ stdio: opts.stdio === true });\n });\n mcpCmd.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir mcp serve [--stdio]');\n });\n\n // `daemon` group — start/stop/status/restart (S9).\n // `start` runs the daemon in the FOREGROUND by default; `--detach` forks a\n // detached child (D1). `--_detached-child` is the hidden marker the detached\n // child carries (D2): it tells the child it IS the daemon (run in-process),\n // so it is never shown in `--help` and never meant for users.\n const daemonGrp = program.command('daemon').description('control the Noir daemon');\n daemonGrp\n .command('start')\n .description('start the Noir daemon (foreground, or background with --detach)')\n .option('--detach', 'run the daemon in the background and exit')\n .option('--workspace <name>', 'start a shared cross-repo workspace daemon instead')\n .option('--force', 'overwrite a non-Noir .mcp.json entry (--workspace only)')\n .addOption(\n new Option('--_detached-child', 'reserved: detached daemon child (internal)').hideHelp(),\n )\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const detach = g.detach === true;\n const detachChild = g._detachedChild === true;\n const workspace = typeof g.workspace === 'string' ? g.workspace : undefined;\n await daemonStart({\n ...toCliOptions(g),\n ...(detach ? { detach } : {}),\n ...(detachChild ? { detachChild } : {}),\n ...(workspace !== undefined ? { workspace } : {}),\n ...(g.force === true ? { force: true } : {}),\n });\n });\n daemonGrp\n .command('join')\n .description('join a shared workspace from this repo')\n .argument('<name>', 'workspace name')\n .option('--force', 'overwrite a non-Noir .mcp.json entry')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const name = typeof args[0] === 'string' ? (args[0] as string) : '';\n await daemonJoin({ ...toCliOptions(g), name, ...(g.force === true ? { force: true } : {}) });\n });\n daemonGrp\n .command('stop')\n .description('stop the Noir daemon')\n .action(async (...args: unknown[]) => {\n await daemonStop(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n daemonGrp\n .command('status')\n .description('report daemon pid/uptime/mode (exit 4 if not running)')\n .action(async (...args: unknown[]) => {\n await daemonStatus(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n daemonGrp\n .command('token')\n .description('print the daemon bearer token to stdout (for a host headersHelper)')\n .action(async (...args: unknown[]) => {\n await daemonToken(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n daemonGrp\n .command('restart')\n .description('stop then start the daemon')\n .option('--detach', 'run the daemon in the background and exit')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n await daemonRestart({\n ...toCliOptions(g),\n ...(g.detach === true ? { detach: true } : {}),\n });\n });\n daemonGrp.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir daemon start|join|stop|status|restart|token');\n });\n daemonGrp.addHelpText(\n 'after',\n [\n '',\n 'Shared workspaces (two repos, one product — e.g. backend + frontend):',\n ' noir daemon start --workspace <name> --detach found a workspace from this repo',\n ' noir daemon join <name> join it from the other repo',\n '',\n 'Guide: docs/how-to/shared-workspaces.md',\n ].join('\\n'),\n );\n\n const workspaceGrp = program.command('workspace').description('shared cross-repo workspaces');\n workspaceGrp\n .command('list')\n .description('list workspaces on this machine')\n .action(async (...args: unknown[]) => {\n await workspaceList(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n workspaceGrp\n .command('status')\n .description('members + daemon liveness for a workspace')\n .argument('[name]', \"workspace name (defaults to this repo's joined workspace)\")\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const name =\n typeof args[0] === 'string' && (args[0] as string).length > 0\n ? (args[0] as string)\n : undefined;\n await workspaceStatus({\n ...toCliOptions(cmd.optsWithGlobals()),\n ...(name !== undefined ? { name } : {}),\n });\n });\n workspaceGrp\n .command('leave')\n .description('leave the workspace this repo joined (restore stdio)')\n .option('--force', 'overwrite a non-Noir .mcp.json entry')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n await workspaceLeave({\n ...toCliOptions(g),\n ...(g.force === true ? { force: true } : {}),\n });\n });\n workspaceGrp\n .command('stop')\n .description('stop a workspace daemon (membership retained)')\n .argument('[name]', \"workspace name (defaults to this repo's joined workspace)\")\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const name =\n typeof args[0] === 'string' && (args[0] as string).length > 0\n ? (args[0] as string)\n : undefined;\n await workspaceStop({\n ...toCliOptions(cmd.optsWithGlobals()),\n ...(name !== undefined ? { name } : {}),\n });\n });\n workspaceGrp.addHelpText(\n 'after',\n [\n '',\n 'This group only inspects and leaves workspaces. To CREATE or JOIN one:',\n ' noir daemon start --workspace <name> --detach found it (from the first repo)',\n ' noir daemon join <name> join it (from the second repo)',\n '',\n 'Guide: docs/how-to/shared-workspaces.md',\n ].join('\\n'),\n );\n workspaceGrp.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir workspace list|status|leave|stop');\n });\n\n program\n .command('doctor')\n .description('environment + project health')\n .option(\n '--dedup',\n 'scan host-context + .noir/ docs for semantic near-duplicates (loads the local embedder)',\n )\n .action(async (...args: unknown[]) => {\n const g = actionGlobals(args);\n const dedup = trailingCmd(args).opts().dedup === true;\n await doctor({ ...toCliOptions(g), ...(dedup ? { dedup: true } : {}) });\n });\n\n // ----- new subcommand groups (wired by t4) -----\n // Signatures match S9 §7 so --help is accurate; every action dispatches to\n // its command module in ./commands/*.js.\n\n program\n .command('status')\n .description('project + daemon + workflow + store snapshot')\n .action(async (...args: unknown[]) => {\n // Global flags (`--json`/`--verbose`/…) reach status via the trailing\n // Command; the action itself owns no command-specific options.\n await status(toStatusOptions(actionGlobals(args)));\n });\n\n // `noir env` — which configuration is in effect and where each value comes\n // from. READ-ONLY, and names-and-shapes only: never a value (spec 12.4).\n program\n .command('env')\n .description('which configuration is in effect and where each value comes from')\n .action(async (...args: unknown[]) => {\n await env(toCliOptions(actionGlobals(args)));\n });\n\n const contextGrp = program.command('context').description('context engine');\n contextGrp\n .command('search')\n .description('hybrid search over the indexed context')\n .argument('<query>', 'search query')\n .option('--limit <n>', 'max results', '10')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n // Positional `<query>` is always the leading action arg.\n const query = typeof args[0] === 'string' ? (args[0] as string) : '';\n const limit = typeof g.limit === 'string' ? (g.limit as string) : undefined;\n await contextSearch({\n ...toCliOptions(g),\n query,\n ...(limit === undefined ? {} : { limit }),\n });\n });\n contextGrp\n .command('index')\n .description('(re)index project files into the context store')\n // Repeatable: the `(val, acc) => [...acc, val]` coercion accumulates each\n // `--path` into an array (commander's idiomatic collect pattern), so\n // `--path a --path b` → `['a','b']` instead of last-wins.\n .option(\n '--path <p>',\n 'path to index (repeatable)',\n (val: string, acc: string[]) => [...acc, val],\n [] as string[],\n )\n .option('--force', 'force a full reindex (drop all chunks+vectors, re-index from scratch)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const rawPaths = Array.isArray(g.path)\n ? (g.path as unknown[]).filter((p): p is string => typeof p === 'string')\n : [];\n const force = g.force === true;\n await contextIndex({\n ...toCliOptions(g),\n ...(rawPaths.length === 0 ? {} : { paths: rawPaths }),\n ...(force ? { force } : {}),\n });\n });\n contextGrp\n .command('status')\n .description('index freshness + counts')\n .action(async (...args: unknown[]) => {\n await contextStatus(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n contextGrp.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir context search|index|status');\n });\n\n const memoryGrp = program.command('memory').description('memory engine');\n memoryGrp\n .command('recall')\n .description('recall memories for a query')\n .argument('<query>', 'recall query')\n .option('--limit <n>', 'max results', '10')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const query = typeof args[0] === 'string' ? (args[0] as string) : '';\n const limit = typeof g.limit === 'string' ? (g.limit as string) : undefined;\n await memoryRecall({\n ...toCliOptions(g),\n query,\n ...(limit === undefined ? {} : { limit }),\n });\n });\n memoryGrp\n .command('save')\n .description('save an observation to long-term memory')\n // Both optional: `--content` is prompted interactively when absent (memory.ts),\n // or the command fails exit 2 under non-interactive / --no-input / --json.\n .option('--content <text>', 'memory content (prompted interactively if omitted)')\n .option(\n '--type <type>',\n 'observation type (pattern | preference | architecture | bug | workflow | fact | decision)',\n )\n .option('--files <files>', 'comma-separated related file paths')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const content = typeof g.content === 'string' ? (g.content as string) : undefined;\n const type = typeof g.type === 'string' ? (g.type as string) : undefined;\n const files = typeof g.files === 'string' ? (g.files as string) : undefined;\n await memorySave({\n ...toCliOptions(g),\n ...(content === undefined ? {} : { content }),\n ...(type === undefined ? {} : { type }),\n ...(files === undefined ? {} : { files }),\n });\n });\n memoryGrp\n .command('sessions')\n .description('list recent memory sessions')\n .action(async (...args: unknown[]) => {\n await memorySessions(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n memoryGrp\n .command('forget')\n .description('delete one or more memories by id')\n .argument('<ids...>', 'observation id(s)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n // Variadic positional → commander passes a string[] as the leading arg.\n const rawIds = Array.isArray(args[0]) ? (args[0] as unknown[]) : [];\n const ids = rawIds.filter((x): x is string => typeof x === 'string');\n await memoryForget({ ...toCliOptions(g), ids });\n });\n memoryGrp\n .command('consolidate')\n .description('consolidate memories into a derived lesson (provider-explicit)')\n .option('--types <types>', 'comma-separated observation types to consolidate')\n .option('--limit <n>', 'cap on candidate observations')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const types = typeof g.types === 'string' ? (g.types as string) : undefined;\n const limit = typeof g.limit === 'string' ? (g.limit as string) : undefined;\n await memoryConsolidate({\n ...toCliOptions(g),\n ...(types === undefined ? {} : { types }),\n ...(limit === undefined ? {} : { limit }),\n });\n });\n memoryGrp\n .command('capture')\n .description('distill a transcript/notes file (or stdin) into a memory (manual)')\n .argument('[file]', 'transcript/notes file (or pipe stdin)')\n .option('--content <text>', 'inline distilled content')\n .option('--event-type <type>', 'capture hook label (defaults to Stop)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const file =\n typeof args[0] === 'string' && (args[0] as string).length > 0\n ? (args[0] as string)\n : undefined;\n const content = typeof g.content === 'string' ? (g.content as string) : undefined;\n const eventType = typeof g.eventType === 'string' ? (g.eventType as string) : undefined;\n await memoryCapture({\n ...toCliOptions(g),\n ...(file === undefined ? {} : { file }),\n ...(content === undefined ? {} : { content }),\n ...(eventType === undefined ? {} : { eventType }),\n });\n });\n memoryGrp.action(() => {\n throw new NoirCliError(\n EXIT.USAGE,\n 'Usage: noir memory recall|save|capture|sessions|forget|consolidate',\n );\n });\n\n // `skills` group — list/sync the builtin pack in-process (S9, S5).\n const skillsGrp = program.command('skills').description('builtin skills');\n skillsGrp\n .command('list')\n .description('list installed Noir skills')\n .action(async (...args: unknown[]) => {\n await skillsList(toCliOptions(actionGlobals(args)));\n });\n skillsGrp\n .command('sync')\n .description('re-emit skills to the host skills dir')\n .action(async (...args: unknown[]) => {\n await skillsSync(toCliOptions(actionGlobals(args)));\n });\n skillsGrp\n .command('lint')\n .description('structural quality gate over the shipped pack')\n .action(async (...args: unknown[]) => {\n await skillsLint(toCliOptions(actionGlobals(args)));\n });\n skillsGrp\n .command('registry')\n .description('emit the runtime-derived skill registry')\n .action(async (...args: unknown[]) => {\n await skillsRegistry(toCliOptions(actionGlobals(args)));\n });\n skillsGrp.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir skills list|sync|lint|registry');\n });\n\n const taskGrp = program.command('task').description('workflow task control');\n taskGrp\n .command('new')\n .description('start a new workflow task')\n .requiredOption('--slug <slug>', 'task slug')\n .option('--mode <mode>', 'full | quick')\n .option(\n '--class <taskClass>',\n 'task class (feature/epic/enhancement/bugfix/spike/quick-task/refactor) — drives the PRD gate',\n )\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const slug = typeof g.slug === 'string' ? (g.slug as string) : '';\n const mode = typeof g.mode === 'string' ? (g.mode as string) : undefined;\n const taskClass = typeof g.class === 'string' ? (g.class as string) : undefined;\n await taskNew({\n ...toCliOptions(g),\n slug,\n ...(mode === undefined ? {} : { mode }),\n ...(taskClass === undefined ? {} : { taskClass }),\n });\n });\n taskGrp\n .command('status')\n .description('active (or named) task status')\n .argument('[id]', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n // Optional positional: only a string at args[0] is the task id.\n const first = args[0];\n const id = typeof first === 'string' ? first : undefined;\n await taskStatus({\n ...toCliOptions(g),\n ...(id === undefined ? {} : { id }),\n });\n });\n taskGrp\n .command('advance')\n .description('advance the active task to the next phase')\n .option('--to <phase>', 'target phase')\n .option('--force <reason>', 'force the gate with a reason')\n .option('--skip', 'record the landing gate as skipped (advance continues)')\n .option('--no-artifacts', 'skip the document-phase artifact writes at done')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const to = typeof g.to === 'string' ? (g.to as string) : undefined;\n const force = typeof g.force === 'string' ? (g.force as string) : undefined;\n await taskAdvance({\n ...toCliOptions(g),\n ...(to === undefined ? {} : { to }),\n ...(force === undefined ? {} : { force }),\n ...(g.skip === true ? { skip: true } : {}),\n ...(g.artifacts === false ? { noArtifacts: true } : {}),\n });\n });\n taskGrp\n .command('next')\n .description('suggest the next phase + applicable skill')\n .action(async (...args: unknown[]) => {\n await taskNext(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n taskGrp\n .command('decompose')\n .description('decompose a capability into an implementation plan')\n .argument('<capability>', 'capability id')\n .option('--out <path>', 'output path for the plan JSON')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const capability = typeof args[0] === 'string' ? (args[0] as string) : '';\n await taskDecompose({\n ...toCliOptions(g),\n capability,\n ...(typeof g.out === 'string' && g.out.length > 0 ? { out: g.out } : {}),\n });\n });\n taskGrp\n .command('verify')\n .description('run configured verify checks and submit evidence to the verify gate')\n .option(\n '--check <name>',\n 'restrict to a named check (repeatable)',\n (val: string, acc: string[]) => [...acc, val],\n [],\n )\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const check = Array.isArray(g.check) ? (g.check as string[]) : undefined;\n await taskVerify({\n ...toCliOptions(g),\n ...(check === undefined || check.length === 0 ? {} : { check }),\n });\n });\n taskGrp\n .command('research')\n .description('list research findings for the active (or named) task')\n .argument('[id]', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const first = args[0];\n const id = typeof first === 'string' ? first : undefined;\n await taskResearch({\n ...toCliOptions(g),\n ...(id === undefined ? {} : { id }),\n });\n });\n taskGrp\n .command('research-record')\n .description('record a research finding for the active (or named) task')\n .requiredOption('--type <type>', 'assumption | discovery | decision | grounding-fact')\n .requiredOption('--text <text>', 'finding text (capped)')\n .option('--source <ref>', 'evidence/citation (required unless grounding-fact)')\n .option('--task <id>', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n await taskResearchRecord({\n ...toCliOptions(g),\n type: String(g.type ?? ''),\n text: String(g.text ?? ''),\n ...(typeof g.source === 'string' && g.source.length > 0 ? { source: g.source } : {}),\n ...(typeof g.task === 'string' && g.task.length > 0 ? { task: g.task } : {}),\n });\n });\n taskGrp\n .command('resume')\n .description('resume the active (or named) in-flight/blocked task')\n .argument('[id]', 'task id (defaults to active)')\n .option('--prompt <text>', 'a continue instruction to surface in the briefing')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const first = args[0];\n const id = typeof first === 'string' ? first : undefined;\n const prompt = typeof g.prompt === 'string' ? (g.prompt as string) : undefined;\n await taskResume({\n ...toCliOptions(g),\n ...(id === undefined ? {} : { id }),\n ...(prompt === undefined ? {} : { prompt }),\n });\n });\n taskGrp\n .command('block')\n .description('mark the active (or named) task blocked with a reason')\n .argument('<reason>', 'why the task is stuck (non-empty)')\n .option('--task <id>', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const reason = typeof args[0] === 'string' ? (args[0] as string) : '';\n const task = typeof g.task === 'string' ? (g.task as string) : undefined;\n await taskBlock({\n ...toCliOptions(g),\n reason,\n ...(task === undefined ? {} : { task }),\n });\n });\n taskGrp\n .command('abandon')\n .description('abandon the active (or named) task (terminal, confirmed)')\n .option('--task <id>', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const task = typeof g.task === 'string' ? (g.task as string) : undefined;\n await taskAbandon({\n ...toCliOptions(g),\n ...(task === undefined ? {} : { task }),\n });\n });\n taskGrp.action(() => {\n throw new NoirCliError(\n EXIT.USAGE,\n 'Usage: noir task new|status|advance|next|verify|research|research-record|resume|block|abandon',\n );\n });\n\n // C1 -- `noir install` / `noir migrate`: move to the native install path,\n // preserving all settings. `migrate` is an alias (mirrors claude migrate-installer).\n const installCmd = program\n .command('install')\n .description(\n 'install Noir via the native managed-Node path (or migrate from another install method)',\n )\n .option('--list', 'list detected install methods')\n .option('--uninstall-prev', 'after a successful migrate, uninstall the previous install method')\n .option(\n '--dismiss',\n 'dismiss the migration banner for the current version (persists in install.json)',\n )\n .argument('[spec]', \"channel ('latest'|'beta') or exact version (default: latest)\")\n .action(\n async (\n spec: string | undefined,\n opts: { list?: boolean; uninstallPrev?: boolean; dismiss?: boolean },\n cmd: Command,\n ) => {\n const { install } = await import('./commands/install.js');\n const cli = cmd.optsWithGlobals() as CliOptions;\n await install({\n ...cli,\n spec,\n list: opts.list === true,\n uninstallPrev: opts.uninstallPrev === true,\n dismiss: opts.dismiss === true,\n });\n },\n );\n // `migrate` alias -- same behavior.\n installCmd.alias('migrate');\n\n // C1 --- `noir update`: self-update via the active install method.\n program\n .command('update')\n .description('update Noir to the latest version via the active install method')\n .option('--check', 'check for a new version without changing anything')\n .argument('[spec]', \"channel ('latest'|'beta') or exact version (default: latest)\")\n .action(async (spec: string | undefined, opts: { check?: boolean }, cmd: Command) => {\n const { update } = await import('./commands/update.js');\n const cli = cmd.optsWithGlobals() as CliOptions;\n await update({ ...cli, spec, check: opts.check === true });\n });\n\n // `noir handoff` + the `noir wrap` session-end alias. Both dispatch the\n // SAME handler; the artifact reuses `gatherStatusPayload` (status.ts) +\n // `PHASE_SKILL` (task.ts) for the snapshot, does a bounded context/memory\n // extraction, and renders a pasteable host-handoff markdown block to STDOUT.\n // `--write` persists to `.noir/handoff/HO-<NNNN>-<id>.md` (gitignored); `--json` emits\n // the structured payload. Doctrine: the host-launch directive is TEXT ONLY —\n // Noir never spawns the host.\n function buildHandoffOptions(g: Record<string, unknown>): HandoffOptions {\n return { ...toCliOptions(g), ...(g.write === true ? { write: true } : {}) };\n }\n program\n .command('handoff')\n .description('emit a ready-to-paste host handoff prompt')\n .option('--write', 'persist to .noir/handoff/HO-<NNNN>-<id>.md (gitignored)')\n .action(async (...args: unknown[]) => {\n await handoff(buildHandoffOptions(trailingCmd(args).optsWithGlobals()));\n });\n // `noir wrap` — session-end alias (same handler, friendlier name at the end of\n // a session). Same options; no separate code path.\n program\n .command('wrap')\n .description('session-end alias for `noir handoff`')\n .option('--write', 'persist to .noir/handoff/HO-<NNNN>-<id>.md (gitignored)')\n .action(async (...args: unknown[]) => {\n await handoff(buildHandoffOptions(trailingCmd(args).optsWithGlobals()));\n });\n\n // `noir release <version> [--channel beta|stable] [--dry-run]` — guided\n // orchestrator over the patch-release flow (c4-release-phase S2). Hands off\n // at the human-approval gates; NEVER auto-approves the GitHub publish job.\n program\n .command('release')\n .description('guided release orchestrator over the patch-release flow')\n .argument('[version]', 'target version, e.g. 1.10.0')\n .option('--channel <channel>', 'beta (default) | stable')\n .option('--dry-run', 'print the checklist without executing any steps')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n await release({\n ...toCliOptions(g),\n ...(typeof args[0] === 'string' && (args[0] as string).length > 0\n ? { version: args[0] as string }\n : {}),\n ...(typeof g.channel === 'string' && (g.channel as string).length > 0\n ? { channel: g.channel as string }\n : {}),\n ...(g.dryRun === true ? { dryRun: true } : {}),\n });\n });\n\n // `noir tui` — the interactive Ink dashboard. LAZY-loaded: the Ink app\n // (React) is imported ONLY inside this action via `await import('./tui/…')`,\n // so React + Ink never enter the main CLI startup path. `noir status`,\n // `noir doctor`, a bare `noir`, etc. stay fast and React-free. The dispatch\n // seam is the SAME shape `home(opts, deps)` uses (a fresh commander program\n // per dispatch, with its own error → exit-code mapping) — command routing is\n // NOT reimplemented in the TUI; `/`-prefixed inputs are routed through it.\n // Interactive-only: requires a TTY; under `--json` / `--no-input` / non-TTY\n // / CI / NO_COLOR it errors cleanly via `requireInteractive` (exit 2).\n program\n .command('tui')\n .description('interactive Ink dashboard (host · phase · daemon + /command dispatch)')\n .action(async (...args: unknown[]) => {\n const g = actionGlobals(args);\n const opts = toCliOptions(g);\n // requireInteractive honors all the non-interactive conditions (no TTY,\n // --no-input, --json, CI, NO_COLOR) and fails exit 2 with a clear message.\n requireInteractive(opts, '`noir tui`');\n // LAZY load the Ink dashboard. The import path is computed at runtime\n // (not a string literal) so esbuild cannot statically follow it into the\n // tui graph during the bin build — that static analysis is what hoists\n // bin.ts's entry body (incl. the `isMainModule` realpath guard) into a\n // shared chunk, turning dist/bin.js into a facade and breaking a global\n // `noir` install under its symlink. The tui is built as a sibling entry\n // (see the second tsup config) so its react/ink dependency tree never\n // enters the main CLI graph; React stays out of `noir status` etc.\n const tuiUrl = new URL('./tui/index.js', import.meta.url).href;\n // React/Ink are external (resolved from node_modules at runtime), so the\n // react-reconciler DEV build is selected unless NODE_ENV is 'production'.\n // The dev build calls performance.measure() on every render, filling\n // Node's 1M-entry performance buffer and OOMing the TUI after a few\n // minutes (QwenLM PR #4462). Force production BEFORE the TUI loads.\n process.env.NODE_ENV ||= 'production';\n const { runTui } = await import(/* @vite-ignore */ tuiUrl);\n // Same dispatch seam as homeDeps.dispatch above — a fresh program per\n // dispatched argv, errors mapped to process.exitCode (never thrown out).\n const dispatch = async (argv: readonly string[]): Promise<void> => {\n const sub = createProgram();\n try {\n await sub.parseAsync([...argv], { from: 'user' });\n } catch (err) {\n handleError(err);\n }\n };\n await runTui(opts, dispatch);\n });\n\n // `noir palette` — the fuzzy command palette opened directly (home-consolidation\n // S3). Same lazy Ink mount as `noir tui` but renders the App palette-first\n // (`{ kind: 'palette' }` initial mode), so the user can fuzzy-run any command\n // without first entering the dashboard. Interactive-only (requireInteractive →\n // exit 2 under non-TTY/--json/--no-input/CI/NO_COLOR), exactly like `noir tui`.\n program\n .command('palette')\n .description('fuzzy command palette — run any noir command (Ink)')\n .action(async (...args: unknown[]) => {\n const g = actionGlobals(args);\n const opts = toCliOptions(g);\n requireInteractive(opts, '`noir palette`');\n const tuiUrl = new URL('./tui/index.js', import.meta.url).href;\n process.env.NODE_ENV ||= 'production'; // see the `noir tui` action above\n const { runPalette } = await import(/* @vite-ignore */ tuiUrl);\n const dispatch = async (argv: readonly string[]): Promise<void> => {\n const sub = createProgram();\n try {\n await sub.parseAsync([...argv], { from: 'user' });\n } catch (err) {\n handleError(err);\n }\n };\n await runPalette(opts, dispatch);\n });\n\n // `noir run <prompt>` — drive the host CLI headless and render its\n // stream-json (v2 orchestrator, Archetype B). Streams the host's output and\n // reports token/cost from the `result` event. `--command <binary>` overrides\n // the per-host default so users with multiple profiles (claude vs claude-work)\n // can point at their own binary (D2a). Scriptable under `--json`.\n program\n .command('run')\n .description('ask the host agent a question and print the answer')\n .argument('[prompt...]', 'prompt to send to the host')\n .addOption(new Option('--host <id>', 'host to drive (default claude)').choices(SUPPORTED_HOSTS))\n .option('--command <binary>', 'custom host binary (e.g. claude-work)')\n .option('--profile <name>', 'use a named run profile (run.profiles in .noir/config.yml)')\n .option('--list-profiles', 'list configured run profiles and exit')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n // A variadic `[prompt...]` argument arrives as a SINGLE array value in the\n // action's positional args, so read the parsed positional args off the\n // trailing Command (`cmd.args`) rather than filtering the raw `args`.\n const prompt = cmd.args.join(' ').trim();\n const opts: RunOptions = {\n ...toCliOptions(g),\n ...(typeof g.host === 'string' ? { host: g.host } : {}),\n ...(typeof g.command === 'string' ? { command: g.command } : {}),\n ...(typeof g.profile === 'string' ? { profile: g.profile } : {}),\n };\n if (g.listProfiles === true) {\n listProfilesCommand(opts);\n return;\n }\n await runHostCommand(prompt, opts);\n });\n\n /** `noir run --list-profiles`: show the configured run profiles (NAME / DEFAULT / BINARY).\n * Under --json the rows go to stdout as one {ok,data} envelope (S9 contract —\n * the table/info helpers are silenced in json mode, so json is handled FIRST). */\n function listProfilesCommand(opts: CliOptions): void {\n const config = loadRunConfig(process.cwd());\n const rows = config ? listProfiles(config) : [];\n if (opts.json === true) {\n json({ ok: true, data: rows });\n return;\n }\n if (rows.length === 0) {\n info('(no run profiles configured — add a run.profiles block to .noir/config.yml)', opts);\n return;\n }\n table(rows, ['NAME', 'DEFAULT', 'BINARY'], opts);\n }\n\n program.action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n // Bare `noir` (no subcommand) → the home router. But a leftover positional\n // here is an UNKNOWN command: every registered subcommand consumes its own\n // leading arg via its own action, so anything still in `cmd.args` didn't\n // match. Reject it exit 3 (NOT_FOUND) per the S9 contract instead of\n // silently routing the typo through home (which would exit 0).\n const leftovers = cmd.args;\n if (leftovers.length > 0) {\n fail(\n EXIT.NOT_FOUND,\n `unknown command '${leftovers[0]}' (no such subcommand). Run \\`noir --help\\` for the list.`,\n toCliOptions(cmd.optsWithGlobals()),\n );\n }\n await home(toCliOptions(actionGlobals(args)), homeDeps);\n });\n\n return program;\n}\n\n/** Singleton program used by the bin entry (`run`) and re-exported for convenience. */\nexport const program: Command = createProgram();\n\n// Home-menu deps, built ONCE at module scope (home-consolidation S2). Defined\n// OUTSIDE createProgram so the palette commands are computed a single time and\n// never recurse (createProgram → homeDeps → createProgram → …). The bare-`noir`\n// action inside createProgram closes over this module-level const.\nconst homeDeps: HomeDeps = {\n dispatch: async (argv: readonly string[]): Promise<void> => {\n const sub = createProgram();\n try {\n await sub.parseAsync([...argv], { from: 'user' });\n } catch (err) {\n // Map the sub-command's failure to process.exitCode (never throw out\n // of dispatch — home returns and the outer program exit reflects this).\n handleError(err);\n }\n },\n // The grouped home menu resolves its sections against the LIVE palette\n // registry (home-consolidation S1/S2) — the same source the TUI palette\n // uses — so the menu cannot drift from the commander tree.\n commands: buildPaletteCommands(createProgram()),\n};\n\n// ---------------------------------------------------------------------------\n// Error → exit-code mapping lives in `./output.js` (`handleError`); it never\n// throws and never calls `process.exit` (commander's `exitOverride` already\n// prevented that for commander's own errors).\n// ---------------------------------------------------------------------------\n\n/**\n * Parse `argv` (user-form: NO node/script prefix) on the singleton program and\n * return the resulting exit code. Sets `process.exitCode` for the real bin;\n * tests can either call this or drive `createProgram().parseAsync` directly.\n */\nexport async function run(argv: readonly string[] = []): Promise<number> {\n try {\n await program.parseAsync([...argv], { from: 'user' });\n } catch (err) {\n handleError(err);\n }\n return typeof process.exitCode === 'number' ? process.exitCode : EXIT.OK;\n}\n\n// Auto-invoke only when bin.ts is the entry point (real `noir` bin, or a tsx\n// subprocess as in gate1-stdio). When vitest imports bin.js as a module,\n// process.argv[1] is the runner, so this is skipped and the program stays idle.\nconst isMainModule = (() => {\n try {\n const entry = process.argv[1];\n if (typeof entry !== 'string' || entry.length === 0) return false;\n // Resolve symlinks: a global npm install invokes the bin via a symlink\n // (.../bin/noir -> .../lib/node_modules/@noir-ai/cli/dist/bin.js), so argv[1]\n // is the symlink path while import.meta.url is the RESOLVED real path. Compare\n // the REAL paths so `noir` runs under BOTH direct + symlinked invocation.\n // (Without this, a global `noir` install silently exits 0 — main() never runs.)\n return pathToFileURL(realpathSync(entry)).href === import.meta.url;\n } catch {\n return false;\n }\n})();\n\nif (isMainModule) {\n void run(process.argv.slice(2));\n}\n"],"mappings":"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","names":["envFlagSet","msg","c","data","paths","loadProjectInfo","readWorkspaceMarker","pidAlive","readWorkspaceDaemonRecord","ensured","loadProjectInfo","ensureDaemonRunning","pidAlive","readDaemonToken","readProjectDaemonRecord","errMsg","existsSync","readdirSync","readFileSync","join","resolveAdapter","loadProjectInfo","NOIR_VERSION","pidAlive","readProjectDaemonRecord","status","openStore","fail","c","scaffold","loadNoirEnv","loadProjectInfo","resolveModelConfig","env","join","atomicWriteFile","detectActiveMethod","loadProjectInfo","NOIR_VERSION","readInstallRecord","home","detected","detectActiveMethod","ensureShimExecutable","loadProjectInfo","readInstallRecord","readUpdateCache","runManagerCmd","semverLt","latest","loadProjectInfo","NOIR_VERSION","describeEmbedder","c","renderHuman","info","spawnSync","loadProjectInfo","s","status","c","mkdirSync","writeFileSync","join","resolveAdapter","loadProjectInfo","program","c","hostLaunchDirective","detectActiveMethod","latestVersionFromCache","loadProjectInfo","NOIR_VERSION","readInstallRecord","readUpdateCache","info","readFileSync","loadProjectInfo","parseLimit","toHit","failTool","probe","hits","data","res","sessions","env","spawnSync","status","env","PROBE_TIMEOUT_MS","resolve","c","spawn","resolve","loadProjectInfo","env","existsSync","mkdirSync","writeFileSync","join","loadNoirEnv","NOIR_DIR","env","resolveAdapter","loadProjectInfo","status","data","mkdirSync","readFileSync","writeFileSync","join","sha256Hex","c","resolve","resolveAdapter","loadProjectInfo","emitSkillsToDir","loadProjectInfo","ensureDaemonRunning","resolveAdapter","loadProjectInfo","scaffold","emitSkillsToDir","existsSync","resolveAdapter","loadProjectInfo","paths","scaffold","emitSkillsToDir","SUPPORTED_HOSTS","applyNoirEnv","NOIR_VERSION","c","program","str","create","sync","install","update","run"]}
|
|
1
|
+
{"version":3,"sources":["../src/daemon-client.ts","../src/theme.ts","../src/output.ts","../src/commands/context.ts","../src/workspace-mcp.ts","../src/commands/workspace.ts","../src/commands/daemon.ts","../src/commands/doctor.ts","../src/commands/env.ts","../src/commands/install.ts","../src/commands/update.ts","../src/commands/status.ts","../src/commands/task.ts","../src/commands/handoff.ts","../src/banner.ts","../src/tui/commands/registry.ts","../src/tui/commands/sections.ts","../src/commands/home.ts","../src/commands/memory.ts","../src/commands/release.ts","../src/run-timing.ts","../src/shell-bridge.ts","../src/orchestrator.ts","../src/run-actions.ts","../src/run-interrupt.ts","../src/run-profiles.ts","../src/run-status.ts","../src/commands/run.ts","../src/conflict.ts","../src/dedup-write.ts","../src/init.ts","../src/commands/skills.ts","../src/serve.ts","../src/commands/create.ts","../src/sync.ts","../src/bin.ts"],"sourcesContent":["// Daemon MCP client.\n//\n// Store-touching commands (`status`, `context *`, `memory *`, `task *`) are thin\n// MCP clients over the running Noir daemon rather than opening the store\n// in-process: that preserves the daemon's single-writer discipline and reuses\n// the daemon's MCP tool surface. This module is the single bridge from\n// the CLI to that daemon — every command module calls {@link callDaemonTool} (or\n// the multi-call {@link withDaemon}) instead of importing the store directly.\n//\n// Flow: {@link ensureDaemonRunning} (from @noir-ai/daemon) reads THIS project's\n// daemon record (`~/.noir/daemons/<projectId>.json`, NOIR_DAEMON_DIR override\n// for tests) and STARTS a foreground daemon if no healthy one is present,\n// returning its URL + a `stop()` tear-down. We connect a\n// @modelcontextprotocol/client `Client` over\n// Streamable HTTP to that URL (127.0.0.1 only — the daemon never binds a public\n// interface), initialize, and\n// `callTool(name, args)`; the daemon's tools always return a single text content\n// block whose `text` is `JSON.stringify(payload)` (see `textResult` in\n// packages/daemon/src/server.ts), so we parse it back into the payload object.\n//\n// Failure handling (CLI exit-code contract): ANY failure to reach or use the\n// daemon — record missing/stale, ensure-start error, transport, connect,\n// protocol, or a tool call that rejects / returns no parseable text — maps to\n// exit code `4` (DAEMON_DOWN) with a stable remediation hint. `--verbose` surfaces\n// the underlying cause on stderr; `--json` shapes the failure as the\n// `{ok:false,error:{code,message}}` envelope ON STDOUT via `output.ts:fail`\n// (data→stdout discipline — the single canonical envelope is shaped once there;\n// `failDaemonDown` passes the PLAIN hint as the message, never a pre-stringified\n// inner envelope, which would doubly-encode `error.message`). A tool that returns\n// a logical-failure envelope (`{ok:false, degraded:true, …}`) is NOT a transport\n// failure — it parses cleanly and is returned to the caller as data.\n//\n// Auth: the HTTP transport requires a bearer token, regenerated by\n// the daemon on every start and stored 0600 at `~/.noir/daemons/<scopeKey>.token`\n// (`readDaemonToken`). The scope key is the daemon's IDENTITY: a project daemon's\n// projectId, a workspace daemon's workspace NAME. Every `/mcp`\n// connect below sends the token for the daemon it is addressing via\n// `httpTransport` — never another identity's; `/health` is token-free, so the\n// liveness probe and the daemon record are unaffected. The stdio transport never\n// uses a token (no network surface, no header trust).\n\nimport { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client';\nimport { ContextEngine, createEmbedFn, resolveEmbedderConfig } from '@noir-ai/context';\nimport {\n loadProjectInfo,\n NOIR_VERSION,\n type ProjectInfo,\n readWorkspaceMarker,\n workspaceStoreDbPath,\n} from '@noir-ai/core';\nimport {\n ensureDaemonRunning,\n pidAlive,\n readDaemonToken,\n readProjectDaemonRecord,\n readWorkspaceDaemonRecord,\n resolveGateConfig,\n tokenPath,\n} from '@noir-ai/daemon';\nimport { createMemoryEngine, type MemoryEngine } from '@noir-ai/memory';\nimport { openStore, type Store } from '@noir-ai/store';\nimport { WorkflowEngine } from '@noir-ai/workflow';\nimport { EXIT, fail } from './bin.js';\n\n/** Options shared by every daemon-client entry point. */\nexport interface DaemonClientOptions {\n /** Emit the daemon-down failure as a JSON `{ok,error}` envelope under `--json`. */\n readonly json?: boolean;\n /** Surface transport / connection detail on stderr under `--verbose`. */\n readonly verbose?: boolean;\n /**\n * Project used to resolve the daemon idle timeout. Defaults to\n * `loadProjectInfo(process.cwd())` — the bin's `--cwd` preAction hook has\n * already chdir'd, so process.cwd() honors `--cwd`.\n */\n readonly project?: ProjectInfo;\n /** Override the daemon idle timeout (sec). Defaults to the project's config. */\n readonly idleTimeoutSec?: number;\n}\n\n/** A handle for calling daemon MCP tools over an already-connected client. */\nexport interface DaemonToolCaller {\n /**\n * Call a daemon MCP tool and parse its text-JSON result. Transport / parse\n * failures throw a {@link NoirCliError} (exit 4); a tool's own logical\n * failure envelope (`{ok:false,…}`) is returned as-is.\n */\n callTool<T = unknown>(name: string, args?: Record<string, unknown>): Promise<T>;\n /**\n * List the names of the tools the connected daemon currently exposes.\n * Required so a command can tell \"the daemon is up but does not register\n * tool X\" (an opt-in / engine-not-wired condition, e.g. `memory_consolidate`\n * when consolidation is disabled) apart from \"the daemon is down\" — calling\n * an unregistered tool would otherwise mis-map onto exit 4 (DAEMON_DOWN).\n * Transport failures throw exit 4 exactly like {@link callTool}.\n */\n listTools(): Promise<string[]>;\n}\n\n/** Daemon-down remediation hint. Stable across releases. */\nexport const DAEMON_DOWN_HINT = 'daemon not reachable — try `noir daemon start`';\n\n/**\n * Bounded /health probe window (ms). A probe must never hang: a stale daemon\n * record can point at a port bound by an unrelated process that accepts TCP but\n * never answers, and the probe is now the FIRST hop for every read command's\n * fallback decision. A timeout is treated as \"not running\" (reads degrade to the\n * read-only engine); the bound also keeps the daemon-up fast path snappy.\n */\nexport const PROBE_TIMEOUT_MS = 1500;\n\nfunction describeCause(cause: unknown): string {\n if (cause instanceof Error) return `${cause.name}: ${cause.message}`;\n try {\n return JSON.stringify(cause);\n } catch {\n return String(cause);\n }\n}\n\n/**\n * Map any daemon / transport failure onto the DAEMON_DOWN exit code. Always\n * throws (via {@link fail}); the `never` return lets callers use it as a\n * diverging expression inside parsed result handling.\n */\nfunction failDaemonDown(opts: DaemonClientOptions, cause: unknown): never {\n if (opts.verbose) {\n process.stderr.write(`noir: daemon transport detail: ${describeCause(cause)}\\n`);\n }\n // Pass the PLAIN hint + opts and let `output.ts:fail` shape the single\n // `{ok:false,error:{code:EXIT.DAEMON_DOWN,message:hint}}` envelope ONCE. The\n // prior branch pre-stringified an inner `{ok,error}` and passed it as `message`,\n // which `fail` then wrapped AGAIN → `error.message` was a JSON string\n // (doubly-encoded). `opts` carries `--json`/`--verbose` so `fail` selects the\n // stdout envelope vs. the plain stderr line.\n fail(EXIT.DAEMON_DOWN, DAEMON_DOWN_HINT, opts);\n}\n\n// ---------------------------------------------------------------------------\n// Liveness probe (read-only). `noir status` uses this to report daemon state\n// HONESTLY without auto-starting one: it reads the daemon record + checks the\n// pid + GETs /health, and returns {running:false} on any miss. It NEVER calls\n// ensureDaemonRunning. Active commands keep using {@link withDaemon} (which may\n// start a daemon); only the informational `status` command is probe-only.\n// ---------------------------------------------------------------------------\n\n/** Outcome of a read-only daemon liveness probe (never starts a daemon). */\nexport interface DaemonProbe {\n running: boolean;\n pid?: number;\n port?: number;\n uptimeSec?: number;\n}\n\n/**\n * Probe whether a healthy daemon is currently running, WITHOUT starting one.\n * Resolves the CALLER's project id first (that id IS the record lookup key),\n * reads the per-project record via {@link readProjectDaemonRecord}, checks\n * {@link pidAlive}, and GETs `http://127.0.0.1:<port>/health`. Any miss (no\n * project, no record, stale pid, non-200, unreachable, malformed body) →\n * `{running:false}` — this function NEVER throws and NEVER starts a daemon.\n * Under `--verbose` the reason for a miss is logged to stderr (NF5: honest\n * degradation).\n */\nexport async function probeDaemon(opts: DaemonClientOptions = {}): Promise<DaemonProbe> {\n // Resolve the project FIRST — with per-project records the ProjectId IS the\n // lookup key, so the record cannot be read before it is known.\n const expectedProject =\n opts.project?.id ??\n (() => {\n try {\n return loadProjectInfo(process.cwd()).id;\n } catch {\n return undefined; // uninitialized — no caller project to look up\n }\n })();\n if (expectedProject === undefined) {\n if (opts.verbose) process.stderr.write('noir: daemon probe: no project (uninitialized)\\n');\n return { running: false };\n }\n const rec = readProjectDaemonRecord(expectedProject);\n if (!rec) {\n if (opts.verbose) process.stderr.write('noir: daemon probe: no daemon record\\n');\n return { running: false };\n }\n if (!pidAlive(rec.pid)) {\n if (opts.verbose) process.stderr.write(`noir: daemon probe: pid ${rec.pid} not alive\\n`);\n return { running: false };\n }\n try {\n // Bounded probe: a stale daemon record pointing at a port held by an\n // unrelated process that accepts TCP but never responds (a blackhole) must\n // NOT hang the probe (or any read command that probes first). Treat a\n // timeout as \"not running\" so reads fall back to the read-only engine.\n const res = await fetch(`http://127.0.0.1:${rec.port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n if (!res.ok) {\n if (opts.verbose) process.stderr.write(`noir: daemon probe: /health → HTTP ${res.status}\\n`);\n return { running: false };\n }\n const body = (await res.json().catch(() => null)) as {\n ok?: boolean;\n pid?: number;\n uptimeSec?: number;\n } | null;\n // PID-reuse guard (same invariant as @noir-ai/daemon ensure.ts isHealthy +\n // commands/daemon.ts isHealthy): the responding /health must carry OUR\n // recorded pid — a missing or mismatched pid means a foreign process holds\n // the port; report NOT running. (The record is already scoped to the caller's\n // project, so there is no cross-project body check left to perform.)\n const pidOk = typeof body?.pid === 'number' && body.pid === rec.pid;\n if (body?.ok !== true || !pidOk) {\n if (opts.verbose)\n process.stderr.write(\n !pidOk\n ? 'noir: daemon probe: pid missing/mismatched (foreign process on recorded port)\\n'\n : 'noir: daemon probe: /health body not ok\\n',\n );\n return { running: false };\n }\n return {\n running: true,\n pid: typeof body.pid === 'number' ? body.pid : rec.pid,\n port: rec.port,\n uptimeSec: typeof body.uptimeSec === 'number' ? body.uptimeSec : undefined,\n };\n } catch (err) {\n if (opts.verbose)\n process.stderr.write(`noir: daemon probe: unreachable (${describeCause(err)})\\n`);\n return { running: false };\n }\n}\n\n/**\n * Resolve a CONNECTED daemon client for the caller's project, plus the\n * tear-down for any daemon this call started. Maps any start / connect error\n * onto exit 4.\n *\n * Ordering is CONNECT-FIRST. When `daemon.port` gives a stable\n * address we attempt the MCP session against it BEFORE spawning anything, so a\n * daemon starts because a connection was genuinely attempted rather than as a\n * side effect of running a command. Only when that attempt fails — a refused\n * connection is the common case — do we fall through to\n * {@link ensureDaemonRunning} (still the single spawn primitive; only the\n * DECISION POINT moved) and connect to whatever it returns. Without a\n * configured `daemon.port` there is no address to probe, so the previous\n * ensure-first ordering is retained verbatim (the connect still depends on the\n * ensure result).\n *\n * The failure path is BOUNDED, in both senses. Structurally: at most one direct\n * attempt, one ensure, and one retry connect — never a loop. In time: the direct\n * probe is raced against {@link PROBE_TIMEOUT_MS}, so a port that accepts TCP\n * and never answers (\"blackhole\") costs the probe window, not a multi-minute\n * stall. A permanently dead port therefore ends in the exit-4 DAEMON_DOWN\n * envelope via {@link failDaemonDown}, not a hang.\n *\n * This function cleans up after ITSELF before any throw: every client it created\n * is closed, and a daemon it started (an ensure result's `stop`) is torn down —\n * because throwing from here means `withDaemon`'s `finally` never runs, so the\n * caller's tear-down cannot be relied on for this leg.\n */\nasync function resolveDaemon(\n opts: DaemonClientOptions,\n): Promise<{ client: Client; stop: () => Promise<void> }> {\n // Lazy default: when the caller didn't inject a project (the common case from\n // command modules, which only forward --json/--verbose), resolve one here.\n // An uninitialized project (`loadProjectInfo` throws \"Run `noir init` first\")\n // is a USAGE error (exit 1), not a daemon-down (exit 4) — so route it through\n // fail() BEFORE the ensureDaemonRunning try, which maps errors to exit 4. This\n // keeps exit-1 semantics AND emits the canonical {ok:false,error} envelope on\n // stdout under --json (a plain Error would leave stdout EMPTY). It also runs\n // BEFORE any connect attempt: a project id is the daemon scope key, so there\n // is nothing to address (and no token to present) without one.\n let project: ProjectInfo;\n try {\n project = opts.project ?? loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n const idleTimeoutSec = opts.idleTimeoutSec ?? project.config.daemon.idleTimeoutSec;\n const preferred = project.config.daemon.port;\n\n // Connect-first when a stable address exists. `0` is a configured PREFERENCE\n // for an ephemeral port, i.e. explicitly NOT a stable address — it takes the\n // ensure-first path below, which still forwards the preference to the spawn.\n if (preferred !== undefined && preferred !== 0) {\n const direct = `http://127.0.0.1:${preferred}/mcp`;\n const probe = newClient();\n let probeTimer: ReturnType<typeof setTimeout> | undefined;\n try {\n // Raw connect, NOT connectClient: a failure here is the SIGNAL TO SPAWN,\n // not a command-fatal error, so it must never be mapped onto exit 4.\n //\n // BOUNDED, exactly like every other address probe in this module: a\n // configured port is a BLIND address — unlike the port\n // `ensureDaemonRunning` has just verified via `/health`, a foreign process\n // may hold it and accept TCP without ever answering. `connect()` carries\n // no deadline of its own (the only backstop would be undici's ~300s\n // headersTimeout), so the probe is raced against the same\n // {@link PROBE_TIMEOUT_MS} window the `/health` probe uses and a timeout\n // is treated as \"no daemon here\", i.e. fall through to the spawn.\n await Promise.race([\n probe.connect(httpTransport(direct, project.id)),\n new Promise<never>((_, reject) => {\n probeTimer = setTimeout(\n () => reject(new Error(`no response within ${PROBE_TIMEOUT_MS}ms`)),\n PROBE_TIMEOUT_MS,\n );\n }),\n ]);\n // A daemon was already answering at the configured address. Nothing was\n // started, so tear-down is a no-op — `stop` must stay inert (there is no\n // ensure result to defer to, and stopping a daemon we did not start would\n // kill the user's).\n return { client: probe, stop: async () => {} };\n } catch (err) {\n // Refused connection (nothing listening), no answer inside the probe\n // window (a blackhole port), a stale token from a before-restart daemon,\n // or a foreign process holding the port — each means \"no USABLE daemon at\n // this address\". Discard the probe and fall through to ensure + one retry;\n // the token is re-read at retry connect time, so a stale-token case\n // self-heals against the same (still healthy) daemon.\n if (opts.verbose) {\n process.stderr.write(\n `noir: no daemon at the configured port ${preferred} (${describeCause(err)})\\n`,\n );\n }\n await probe.close().catch(() => {\n /* the probe connection is already unusable; never mask the spawn path */\n });\n } finally {\n // Release the loser: on success the pending timer must not hold the event\n // loop open, and on failure it has already fired. (A connect that settles\n // AFTER the timeout is ignored by the settled race — and `close()` above\n // has already torn its transport down.)\n if (probeTimer !== undefined) clearTimeout(probeTimer);\n }\n }\n\n let url: string;\n let stop: () => Promise<void>;\n try {\n const ensured = await ensureDaemonRunning({\n project,\n idleTimeoutSec,\n ...(preferred !== undefined ? { port: preferred } : {}),\n });\n url = ensured.url;\n stop = ensured.stop;\n } catch (err) {\n failDaemonDown(opts, err);\n }\n\n // The retry (or the direct path when no port is configured). A failure HERE\n // is genuinely fatal — ensure already succeeded, so there is no further\n // recovery and the exit-4 mapping applies (unlike the probe above).\n const client = newClient();\n try {\n await connectClient(client, url, opts, project.id);\n } catch (err) {\n // This function is about to THROW, so `withDaemon`'s finally — the one that\n // tears down a daemon we started — never runs. Tear down BOTH here, or a\n // daemon `ensureDaemonRunning` just spawned is stranded (running, with no\n // caller left to stop it) behind an exit-4 failure. Reachable on the plain\n // ensure-first path too, not just connect-first: ensure succeeds, then the\n // connect to the daemon it returned fails.\n await client.close().catch(() => {\n /* never mask the connect failure with a close failure */\n });\n await stop().catch(() => {\n /* ditto for the tear-down — the connect failure is the real error */\n });\n throw err;\n }\n return { client, stop };\n}\n\n/**\n * Build a Streamable HTTP transport for `url`, carrying the daemon's bearer\n * token when one is on disk for `scopeKey`. The daemon regenerates its token\n * on every start, so the header is resolved at CONNECT time — never\n * cached across commands — and an absent token file simply means \"send no\n * header\" (a stdio-only or pre-auth daemon); the request then fails as any\n * other unauthorized request does (exit 4 with the daemon's own remediation).\n * `scopeKey` is the daemon identity: a project daemon's projectId, a workspace\n * daemon's name.\n */\nfunction httpTransport(url: string, scopeKey: string | null): StreamableHTTPClientTransport {\n const token = scopeKey === null ? null : readDaemonToken(scopeKey);\n return new StreamableHTTPClientTransport(\n new URL(url),\n // Verified present: StreamableHTTPClientTransportOptions.requestInit?: RequestInit\n token ? { requestInit: { headers: { Authorization: `Bearer ${token}` } } } : {},\n );\n}\n\n/**\n * The daemon scope key for the caller's project (its projectId), or `null` when\n * the project cannot be resolved. `null` ⇒ connect without a token header; the\n * daemon then answers 401 and the caller maps it to exit 4 exactly like any\n * other unreachable daemon (the caller has no project to scope a token to).\n */\nfunction callerScopeKey(opts: DaemonClientOptions): string | null {\n try {\n return (opts.project ?? loadProjectInfo(process.cwd())).id;\n } catch {\n return null;\n }\n}\n\n/**\n * Build an unconnected MCP client for the CLI. One client per connection: the\n * SDK's `connect` binds a transport to the client, so a client is never reused\n * across two daemons (the connect-first probe discards its own on failure).\n */\nfunction newClient(): Client {\n return new Client(\n { name: 'noir-cli', version: NOIR_VERSION },\n { versionNegotiation: { mode: 'auto' } },\n );\n}\n\n/** Connect a Client over Streamable HTTP, rethrowing the RAW transport error so\n * the caller owns the failure mapping. Used by {@link withWorkspaceDaemon},\n * which can read more out of a connect failure than \"the daemon is down\" (a\n * 401 means it is up and reachable but rejected our credential). Everything\n * else goes through {@link connectClient}, which maps onto exit 4. */\nasync function connectRaw(client: Client, url: string, scopeKey: string | null): Promise<void> {\n await client.connect(httpTransport(url, scopeKey));\n}\n\n/** Connect a Client over Streamable HTTP; map transport failure to exit 4. */\nasync function connectClient(\n client: Client,\n url: string,\n opts: DaemonClientOptions,\n scopeKey: string | null,\n): Promise<void> {\n try {\n await connectRaw(client, url, scopeKey);\n } catch (err) {\n failDaemonDown(opts, err);\n }\n}\n\n/**\n * Map a WORKSPACE connect failure onto exit 4 with an accurate message.\n *\n * A 401 is not \"the daemon is down\": the daemon answered, so it IS reachable —\n * it rejected our bearer token (a stale token file, or one we cannot read). The\n * generic {@link DAEMON_DOWN_HINT} sends the user to `noir daemon start`, which\n * fixes nothing. Name the token file (the workspace scope key is the workspace\n * NAME) and the remedy instead. Conservative by construction: only the MESSAGE\n * differs for the recognizable-401 case — the exit code and every non-401\n * failure keep the exact previous behaviour.\n */\nfunction failWorkspaceConnect(\n opts: DaemonClientOptions,\n routing: WorkspaceRouting,\n cause: unknown,\n): never {\n const text = String(cause).toLowerCase();\n if (text.includes('401') || text.includes('unauthorized')) {\n if (opts.verbose) {\n process.stderr.write(`noir: daemon transport detail: ${describeCause(cause)}\\n`);\n }\n // Names only, never a value: the token file's path, never its contents.\n fail(\n EXIT.DAEMON_DOWN,\n `workspace ${JSON.stringify(routing.name)} daemon is running but rejected this client's token (HTTP 401) — its token file is ${tokenPath(routing.name)}; make sure it is readable, or restart the workspace daemon with \\`noir daemon start --workspace ${routing.name}\\` to mint a fresh one.`,\n opts,\n );\n }\n failDaemonDown(opts, cause);\n}\n\n/**\n * Call one daemon tool and parse its text-JSON result. A tool's own\n * logical-failure envelope is returned as data; transport / parse failures\n * throw exit 4.\n */\nasync function callToolParse<T>(\n client: Client,\n opts: DaemonClientOptions,\n name: string,\n args: Record<string, unknown>,\n): Promise<T> {\n // `.catch` whose handler returns `never` (failDaemonDown always throws) keeps\n // `result` typed as the callTool payload — no evolving-any `let`, and the only\n // way past this line is a successful call.\n const result = await client\n .callTool({ name, arguments: args })\n .catch((err: unknown) => failDaemonDown(opts, err));\n // The daemon's tools always return `{content:[{type:'text', text: JSON}]}` via\n // `textResult`. Read defensively: any non-text / empty / non-JSON block means\n // the MCP surface is unusable → exit 4 (not a tool's logical error).\n const block = (result.content as readonly unknown[] | undefined)?.[0] as\n | { type?: string; text?: unknown }\n | undefined;\n if (block?.type !== 'text' || typeof block.text !== 'string') {\n failDaemonDown(opts, new Error(`tool '${name}' returned no text content`));\n }\n const text: string = block.text;\n try {\n return JSON.parse(text) as T;\n } catch (err) {\n failDaemonDown(opts, err);\n }\n}\n\n/**\n * Build a {@link DaemonToolCaller} over an already-connected {@link Client}.\n * Shared by {@link withDaemon} (active commands — failures map to exit 4) and\n * {@link withRunningDaemon} (status — connect failures degrade to null, caught\n * by its outer try). `callTool`/`listTools` still route transport / parse\n * failures through {@link failDaemonDown}; {@link withRunningDaemon} wraps the\n * connect+fn body so those become a null result instead of an exit.\n */\nfunction buildCaller(client: Client, opts: DaemonClientOptions): DaemonToolCaller {\n return {\n callTool: <U = unknown>(name: string, args: Record<string, unknown> = {}): Promise<U> =>\n callToolParse<U>(client, opts, name, args),\n listTools: async (): Promise<string[]> => {\n // `listTools()` is the standard MCP capability discovery method; the\n // daemon's McpServer answers with every registered tool name. Any\n // transport failure maps to exit 4 (DAEMON_DOWN) exactly like callTool;\n // a defensive read of `.tools` keeps a surprising payload from crashing.\n const res = await client.listTools().catch((err: unknown) => failDaemonDown(opts, err));\n const tools = (res as { tools?: unknown } | null)?.tools;\n if (!Array.isArray(tools)) return [];\n const names: string[] = [];\n for (const t of tools) {\n const name = (t as { name?: unknown } | null)?.name;\n if (typeof name === 'string') names.push(name);\n }\n return names;\n },\n };\n}\n\n/**\n * Connect to the daemon and run `fn` against a {@link DaemonToolCaller} that\n * shares one MCP connection across any number of tool calls.\n *\n * {@link resolveDaemon} owns the CONNECT-FIRST ordering and returns\n * a connection it has already established, so this body only has to run `fn`\n * and tear down. The client is closed and any daemon this call started is torn\n * down in `finally`, even when `fn` throws — so a one-shot CLI command never\n * strands a server or leaks a connection, while a reused (already-running)\n * daemon is left untouched (both `ensureDaemonRunning`'s `stop` for a reused\n * daemon and the connect-first path's `stop` are no-ops in that case).\n *\n * That `finally` is reached only once `resolveDaemon` has RETURNED. A throw from\n * `resolveDaemon` itself (exit 4) skips it entirely, so the same tear-down is\n * performed inside `resolveDaemon` before it throws — the two legs must stay in\n * step, or a spawned daemon is stranded. See the retry-failure catch there.\n *\n * `fn`'s own errors propagate untouched (the command module owns their exit\n * code); only transport / connection / tool-parse failures map to exit 4 here.\n */\nexport async function withDaemon<T>(\n opts: DaemonClientOptions,\n fn: (caller: DaemonToolCaller) => Promise<T>,\n): Promise<T> {\n const { client, stop } = await resolveDaemon(opts);\n try {\n return await fn(buildCaller(client, opts));\n } finally {\n await client.close().catch(() => {\n /* a close error must not mask the real failure / swallowed result */\n });\n await stop().catch(() => {\n /* ditto: tear-down failures never override the command's outcome */\n });\n }\n}\n\n/**\n * Connect to an ALREADY-RUNNING daemon (located via {@link probeDaemon}) and run\n * `fn` over one MCP connection. NEVER starts a daemon — returns `null` when the\n * probe finds none. This is the read-only, best-effort path `noir status` uses:\n * a down daemon is reported honestly (the caller renders `daemon:{running:false}`\n * and exits 0 — status is informational, not a write), and any connect failure\n * that crops up after a successful probe (the daemon died mid-snapshot) degrades\n * to `null` rather than escalating to exit 4 (DAEMON_DOWN). Active commands that\n * NEED a daemon use {@link withDaemon} (which auto-starts + maps failures to\n * exit 4); only `status` is probe-only.\n *\n * `fn` should wrap its own tool calls in a try/catch (status uses `tryTool`) so a\n * single missing engine folds to `null` instead of failing the snapshot.\n *\n * `probe` lets a caller that has ALREADY probed (status, to populate the daemon\n * section whether up or down) pass the result in so we don't GET /health twice.\n */\nexport async function withRunningDaemon<T>(\n opts: DaemonClientOptions,\n fn: (caller: DaemonToolCaller) => Promise<T>,\n probe?: DaemonProbe,\n): Promise<T | null> {\n const p = probe ?? (await probeDaemon(opts));\n if (!p.running) return null;\n const url = `http://127.0.0.1:${p.port}/mcp`;\n let client: Client | undefined;\n try {\n client = newClient();\n const connected: Client = client;\n // Connect directly to the probed port — NO ensureDaemonRunning. If the daemon\n // died between probe and connect (a race), this throws and we degrade to null\n // (the probe already supplied pid/uptime for the snapshot). The bearer header\n // is required here too: /health is token-free (the probe above passed without\n // one), but the /mcp snapshot connect is authenticated like every other one.\n await connected.connect(httpTransport(url, callerScopeKey(opts)));\n return await fn(buildCaller(connected, opts));\n } catch {\n // Best-effort: probe said up but connect/transport failed mid-snapshot.\n // Report the probe data + null sections instead of exit 4.\n return null;\n } finally {\n if (client) {\n await client.close().catch(() => {\n /* a close error must not mask the degraded result */\n });\n }\n }\n}\n\n/**\n * Call a single daemon MCP tool and return its parsed JSON payload. The\n * convenience most command modules use; built on {@link withDaemon} so connect\n * + close + tear-down are handled once.\n */\nexport async function callDaemonTool<T = unknown>(\n opts: DaemonClientOptions,\n name: string,\n args: Record<string, unknown> = {},\n): Promise<T> {\n return withDaemon<T>(opts, (caller) => caller.callTool<T>(name, args));\n}\n\n// ---------------------------------------------------------------------------\n// In-process read-only fallback. READS only — the single-writer\n// invariant is preserved because the store is opened READ-ONLY and every engine\n// built over it (context retriever, memory recall/sessions, workflow status) is\n// a pure reader. Writes keep the daemon-required path (exit 4 when down).\n// ---------------------------------------------------------------------------\n\n/** Engines constructed in-process by {@link withInProcessRead} (reads only). */\nexport interface InProcessEngines {\n /** Hybrid-retrieval engine over the read-only store handle. */\n context: ContextEngine;\n /** Cross-session memory engine over the same handle (recall/sessions read). */\n memory: MemoryEngine;\n /** Workflow engine over the same handle (status read). */\n workflow: WorkflowEngine;\n}\n\n/**\n * Open the project's store READ-ONLY, build the context + memory + workflow\n * engines in-process over that one handle, run `fn`, and close the store in\n * `finally`. This is the daemon-down fallback for READ commands (`context\n * search`, `memory recall`, `memory sessions`, `task status`): a read-only\n * handle preserves the daemon's single-writer discipline (the store is never\n * opened for writes here), and reads (FTS, kNN, counts, KV) keep working on it\n * exactly as they do for a degraded daemon handle.\n *\n * Construction mirrors {@link openStoreForDaemon} + the daemon's stdio/http\n * seams: the context engine resolves its embedder from the project's\n * `context:` config via {@link resolveEmbedderConfig} (a pure projection —\n * construction never touches the network or the native runtime; a `kind:'none'`\n * or unavailable embedder degrades reads to BM25-only); the memory engine\n * shares the SAME embed; the workflow engine reads the store KV. All three\n * reuse the injected handle — no second connection.\n *\n * `storeDegraded: true` is threaded into both engines so their persistent\n * `degraded` flag is honest (a read-only handle) and any accidental write\n * refuses with the daemon's \"store is read-only (daemon down)\" error rather\n * than silently succeeding.\n *\n * The store is ALWAYS closed in `finally`, even when `fn` throws (a one-shot\n * CLI command never strands a handle).\n *\n * @param opts Daemon-client options. `project` resolves the store path + the\n * embedder config; when absent it is resolved from `process.cwd()`\n * (uninitialized project → throws the \"Run `noir init` first\" exit-1 hint,\n * mirroring {@link resolveDaemon}).\n */\nexport async function withInProcessRead<T>(\n opts: DaemonClientOptions,\n fn: (engines: InProcessEngines) => Promise<T>,\n): Promise<T> {\n let project: ProjectInfo;\n try {\n project = opts.project ?? loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n // Route a store-open failure through fail() so --json emits {ok:false,error}\n // (a raw throw would leave stdout empty).\n let store: Store;\n try {\n store = await openStore({ projectId: project.id, root: project.root, readonly: true });\n } catch (err) {\n fail(\n EXIT.ERROR,\n `could not open the store read-only: ${err instanceof Error ? err.message : String(err)}`,\n opts,\n );\n }\n try {\n // READ-ONLY handle: `storeDegraded` is threaded so status/degraded flags are\n // honest and any accidental write refuses cleanly (single writer preserved).\n const embedderCfg = resolveEmbedderConfig(project.config.context);\n const embed = createEmbedFn(embedderCfg).embed;\n const context = new ContextEngine({\n store,\n root: project.root,\n projectId: project.id,\n embedderCfg,\n storeDegraded: true,\n });\n const memory = createMemoryEngine({\n store,\n root: project.root,\n projectId: project.id,\n embed,\n storeDegraded: true,\n });\n const workflow = new WorkflowEngine(\n store,\n project.root,\n project.id,\n resolveGateConfig(project.config),\n );\n return await fn({ context, memory, workflow });\n } finally {\n await store.close().catch(() => {\n /* a close error must not mask the command's outcome */\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// Workspace-aware routing. In a repo that joined a workspace (a\n// `.noir/workspace.json` marker), `memory *` commands route to the WORKSPACE\n// daemon (`?p=<projectId>`), not the per-project daemon. The read fallback opens\n// the workspace store read-only (single-writer preserved). Context/workflow/task\n// commands keep the project daemon.\n// ---------------------------------------------------------------------------\n\n/** A repo's workspace membership (name + this repo's project id), or null. */\nexport interface WorkspaceRouting {\n name: string;\n projectId: string;\n}\n\nexport function workspaceRoutingTarget(root: string): WorkspaceRouting | null {\n const name = readWorkspaceMarker(root);\n if (name === null) return null;\n try {\n return { name, projectId: loadProjectInfo(root).id };\n } catch {\n return null;\n }\n}\n\n/** Read-only probe of a workspace daemon (never starts one). */\nexport async function probeWorkspaceDaemon(routing: WorkspaceRouting): Promise<DaemonProbe> {\n const rec = readWorkspaceDaemonRecord(routing.name);\n if (rec === null || !pidAlive(rec.pid)) return { running: false };\n try {\n const res = await fetch(`http://127.0.0.1:${rec.port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n if (!res.ok) return { running: false };\n const body = (await res.json().catch(() => null)) as {\n ok?: boolean;\n pid?: number;\n workspace?: string;\n } | null;\n if (body?.ok !== true || body.workspace !== routing.name || body.pid !== rec.pid) {\n return { running: false };\n }\n return { running: true, pid: rec.pid, port: rec.port };\n } catch {\n return { running: false };\n }\n}\n\n/** Connect to the workspace daemon (`?p=<projectId>`) and run `fn` (active path). */\nexport async function withWorkspaceDaemon<T>(\n opts: DaemonClientOptions,\n routing: WorkspaceRouting,\n fn: (caller: DaemonToolCaller) => Promise<T>,\n): Promise<T> {\n // A write/read against a workspace whose daemon is DOWN must fail\n // with clear guidance, NOT silently auto-start a fresh daemon (which would\n // look identical to \"up\" but surprise the user). Probe-only — no ensure.\n const probe = await probeWorkspaceDaemon(routing);\n if (!probe.running || probe.port === undefined) {\n fail(\n EXIT.DAEMON_DOWN,\n `workspace ${JSON.stringify(routing.name)} daemon is not running — start it with \\`noir daemon start --workspace ${routing.name}\\` (or run \\`noir workspace status\\`).`,\n opts,\n );\n }\n const url = `http://127.0.0.1:${probe.port}/mcp?p=${routing.projectId}`;\n let client: Client | undefined;\n try {\n client = newClient();\n const connected: Client = client;\n // The workspace daemon's token scope key is the workspace NAME — its own\n // secret, resolved from disk at connect time like every other token. In\n // particular the CALLER's project token is never handed to a workspace\n // server: the two scope keys are different identities, so a repo can only\n // ever present the credential of the daemon it is actually addressing.\n // `connectRaw` (not `connectClient`) so a 401 rejection reaches\n // `failWorkspaceConnect` with its cause intact, instead of being flattened\n // into the generic \"daemon not reachable\" hint.\n await connectRaw(connected, url, routing.name).catch((err: unknown) =>\n failWorkspaceConnect(opts, routing, err),\n );\n return await fn(buildCaller(connected, opts));\n } finally {\n if (client) {\n await client.close().catch(() => {\n /* a close error must not mask the real failure */\n });\n }\n }\n}\n\n/** Open the workspace store read-only and run `fn` over a memory engine (read fallback). */\nexport async function withWorkspaceMemoryRead<T>(\n opts: DaemonClientOptions,\n routing: WorkspaceRouting,\n project: ProjectInfo,\n fn: (memory: MemoryEngine) => Promise<T>,\n): Promise<T> {\n let store: Store;\n try {\n store = await openStore({\n projectId: `ws-${routing.name}`,\n root: project.root,\n dbPath: workspaceStoreDbPath(routing.name),\n readonly: true,\n });\n } catch (err) {\n // Reached only when the workspace daemon is already down (this is its read\n // fallback). The dominant cause is a store that was never created, so give\n // the same actionable guidance + DAEMON_DOWN exit code the active path uses\n // rather than a bare store-open error — the caller's next step is the same\n // either way: start the daemon.\n fail(\n EXIT.DAEMON_DOWN,\n `workspace ${JSON.stringify(routing.name)} store is unavailable (${err instanceof Error ? err.message : String(err)}) — start its daemon with \\`noir daemon start --workspace ${routing.name}\\` (or run \\`noir workspace status\\`).`,\n opts,\n );\n }\n const embedderCfg = resolveEmbedderConfig(project.config.context);\n const embed = createEmbedFn(embedderCfg).embed;\n const memory = createMemoryEngine({\n store,\n root: project.root,\n projectId: `ws-${routing.name}`,\n embed,\n storeDegraded: true,\n softForget: true,\n });\n try {\n return await fn(memory);\n } finally {\n await store.close().catch(() => {\n /* a close error must not mask the command's outcome */\n });\n }\n}\n","// Single owner of the Noir CLI's semantic palette + status badges.\n//\n// Built on `picocolors` (the SAME lib the rest of the CLI already uses), so the\n// decoration strips consistently under NO_COLOR / non-TTY. cli-table3 styles\n// via a SECOND lib (@colors/colors) whose NO_COLOR/TTY semantics differ — that\n// split is what painted every table header red while the body stripped. Routing\n// ALL color through this module (the `c` object + `badge()`) closes that leak:\n// there is exactly one color authority, and it honors NO_COLOR / CLICOLOR_FORCE\n// / TTY uniformly.\n//\n// Accessibility:\n// - `badge()` ALWAYS returns SYMBOL + TEXT LABEL (e.g. `⚠ warn`), so NO_COLOR\n// and colorblind users get identical information. Color is decorative.\n// - `NOIR_ACCESSIBLE` disables the banner gradient (→ solid accent) and is the\n// documented opt-in for users who want the most legible form; badges already\n// carry symbol+text unconditionally, so the flag is a no-op for them.\n\nimport pc from 'picocolors';\n\n// A private always-on colorizer. The PUBLIC `c` object gates every call through\n// `useColor()`, so `useColor()` is the single live authority over whether ANY\n// ANSI leaves this module. (The default `picocolors` export captures its\n// `isColorSupported` snapshot once at module load and does NOT honor\n// CLICOLOR_FORCE; gating a forced instance here fixes both.)\nconst FORCE = pc.createColors(true);\n\n/** Badge state → semantic color role. */\nexport type BadgeState = 'ok' | 'warn' | 'error' | 'info';\n\n/**\n * The semantic palette. Each method returns its input unchanged when color is\n * off (NO_COLOR / non-TTY / not forced), so callers can wrap freely without\n * guarding. Compose with `c.bold(c.info(s))` etc.\n */\nexport const c = {\n /** Green — success / healthy. */\n ok: (s: string): string => (useColor() ? FORCE.green(s) : s),\n /** Yellow — degraded but usable. */\n warn: (s: string): string => (useColor() ? FORCE.yellow(s) : s),\n /** Red — reserved strictly for ERROR. Never a table header. */\n error: (s: string): string => (useColor() ? FORCE.red(s) : s),\n /** Cyan — informational / brand-neutral accent for labels. */\n info: (s: string): string => (useColor() ? FORCE.cyan(s) : s),\n /** Blue — the Midnight Cobalt brand role (banner / accent). */\n accent: (s: string): string => (useColor() ? FORCE.blue(s) : s),\n /** Dim — secondary text, table borders when color is on. */\n dim: (s: string): string => (useColor() ? FORCE.dim(s) : s),\n /** Bold — emphasis (table headers compose this with `info`). */\n bold: (s: string): string => (useColor() ? FORCE.bold(s) : s),\n /** Reverse video (SGR 7) — inverted background for the active list row. */\n inverse: (s: string): string => (useColor() ? FORCE.inverse(s) : s),\n};\n\n// @clack-style status symbols (the vocabulary Noir inherits via @clack/prompts\n// + ora). Paired 1:1 with a state name so a screen reader / pipe / colorblind\n// user reads the same fact a sighted interactive user sees.\nconst BADGE_SYMBOL: Readonly<Record<BadgeState, string>> = {\n ok: '✓',\n warn: '⚠',\n error: '✗',\n info: 'ℹ',\n};\n\n/**\n * Render a status badge: always `SYMBOL LABEL`, colored by `state` when color\n * is on. The TEXT LABEL is unconditional — this is the accessibility invariant.\n * `label` defaults to the state name, so `badge('ok')` reads `✓ ok`.\n *\n * @example badge('ok') // → green \"✓ ok\"\n * @example badge('warn','degraded') // → yellow \"⚠ degraded\"\n * @example badge('error','down') // → red \"✗ down\"\n */\nexport function badge(state: BadgeState, label: string = state): string {\n const text = `${BADGE_SYMBOL[state]} ${label}`;\n switch (state) {\n case 'ok':\n return c.ok(text);\n case 'warn':\n return c.warn(text);\n case 'error':\n return c.error(text);\n case 'info':\n return c.info(text);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Environment gates. Evaluated LIVE (not captured at module load) so tests that\n// flip NO_COLOR / CLICOLOR_FORCE between cases see the right answer, and so a\n// user exporting NO_COLOR mid-session is honored on the next render.\n// ---------------------------------------------------------------------------\n\nfunction envFlagSet(name: string): boolean {\n // NO_COLOR spec: present AND non-empty disables color (any value).\n const v = process.env[name];\n return v !== undefined && v !== '';\n}\n\n/**\n * True when running under a CI runner (GitHub Actions / GitLab / CircleCI /\n * Drone set `CI=true` at the conventional value). Treats the explicit opt-outs\n * `0` / `false` as \"not CI\" so a user can force-disable the guard. Owned here\n * (the color authority) and re-exported so output.ts doesn't duplicate it.\n */\nexport function isCiEnv(): boolean {\n const v = process.env.CI;\n if (v === undefined || v === '') return false;\n return v !== '0' && v !== 'false';\n}\n\n/**\n * The single authority on whether decoration should emit ANSI. Honors:\n * - `NO_COLOR` → always off (spec: present + non-empty).\n * - `CLICOLOR_FORCE=1` → always on (forces color on a redirected stream).\n * - `CI=true` (alone) → always OFF. picocolors' `isColorSupported`\n * snapshot turns color ON under `!!env.CI` (it\n * assumes a CI viewer that renders ANSI). But ANSI\n * escapes inflate the raw byte length of rendered\n * lines, which breaks the responsive-width\n * guarantee `max(line.length) <= terminalWidth()`\n * that `table()` exists to uphold — and the\n * regression test that locks it (fits 60/80/120 in\n * theme.test.ts). The CLI's own top-of-file\n * contract states decoration auto-disables under\n * CI / non-TTY; this honors that. A CI viewer that\n * wants color sets `CLICOLOR_FORCE=1` (above).\n * - otherwise → picocolors' detection (TTY / FORCE_COLOR).\n */\nexport function useColor(): boolean {\n if (envFlagSet('NO_COLOR')) return false;\n if (process.env.CLICOLOR_FORCE === '1') return true;\n if (isCiEnv()) return false;\n return pc.isColorSupported;\n}\n\n/**\n * `NOIR_ACCESSIBLE`: opt-in maximum legibility. Badges already carry symbol+\n * text unconditionally (so the flag is a no-op for them); the material effect\n * is disabling the banner gradient (→ solid accent) and is reserved here for\n * future accessible-display tweaks.\n */\nexport function accessibleMode(): boolean {\n return envFlagSet('NOIR_ACCESSIBLE');\n}\n\n/**\n * Terminal width in columns. Honors `COLUMNS` (the conventional override) then\n * `process.stdout.columns`, defaulting to 80 when neither is set (the same\n * default `picocolors` / `cli-table3` assume). Floored at a small minimum so a\n * misconfigured shell never produces a degenerate zero/negative-width table.\n */\nexport function terminalWidth(): number {\n const fromEnv = process.env.COLUMNS;\n if (fromEnv !== undefined && fromEnv !== '') {\n const n = Number(fromEnv);\n if (Number.isInteger(n) && n > 0) return Math.max(n, 20);\n }\n const cols = process.stdout.columns;\n if (typeof cols === 'number' && cols > 0) return Math.max(cols, 20);\n return 80;\n}\n\n/**\n * Usable content width inside a bordered TUI panel. Ink's `borderStyle` adds a\n * 1-char border on each side, and `paddingX={1}` adds 1 char inside that — so a\n * full-width rounded panel has `terminalWidth - 4` columns of usable text.\n * Components that truncate long lines (OutputPane) MUST use this, not\n * {@link terminalWidth}, or content overflows the border.\n *\n * @param border chars consumed by the border (default 2 = left + right).\n * @param padding chars consumed by inner padding (default 2 = left + right).\n */\nexport function contentWidth(border = 2, padding = 2): number {\n return Math.max(20, terminalWidth() - border - padding);\n}\n\n/**\n * A horizontal divider line of `─` sized for the current content width. Used\n * inside bordered panels to separate regions (e.g. status bar ↔ output ↔ input)\n * without nesting Ink borders. Always dim so it reads as structure, not content.\n */\nexport function divider(): string {\n return c.dim('─'.repeat(Math.max(1, contentWidth())));\n}\n","// Centralized output + exit-code infrastructure for `@noir-ai/cli`.\n//\n// Stream discipline: machine-readable data → stdout; every other\n// diagnostic (progress, warnings, errors, tables, help) → stderr. Under `--json`\n// the decorated stderr helpers are silenced so stdout stays pristine JSON, and\n// `fail()` emits a structured `{ok:false,error}` envelope to stdout instead.\n//\n// Decoration (picocolors / cli-table3 / ora) auto-disables under `--json`,\n// `--quiet`, `CI`, `NO_COLOR`, or a non-TTY, so the same code path is safe in an\n// interactive shell, a pipe, and CI.\n//\n// Exit codes: 0 ok · 1 error · 2 usage · 3 not-found · 4 daemon-down ·\n// 5 cancelled · 130/143 interrupted (128 + the signal that stopped the run:\n// SIGINT and SIGTERM). The first six are the `EXIT` enum below; the interrupt\n// codes come from the run's own stop path, not from `fail()`'s contract. These\n// constants live HERE (the single source of truth); bin.ts re-exports them so\n// existing imports from `./bin.js` keep working.\n\nimport Table from 'cli-table3';\nimport { CommanderError } from 'commander';\nimport ora, { type Ora } from 'ora';\nimport { c, isCiEnv, terminalWidth } from './theme.js';\n\n// ---------------------------------------------------------------------------\n// Exit-code contract + error types\n// ---------------------------------------------------------------------------\nexport const EXIT = {\n OK: 0,\n ERROR: 1,\n USAGE: 2,\n NOT_FOUND: 3,\n DAEMON_DOWN: 4,\n CANCELLED: 5,\n} as const;\n\nexport type ExitCode = (typeof EXIT)[keyof typeof EXIT];\n\n/**\n * Application-level CLI error carrying a contracted exit code. Thrown directly by\n * bin.ts for legacy usage paths; `fail()` is the ergonomic throw helper.\n * `inferExitCode` / `handleError` map it onto `process.exitCode`.\n */\nexport class NoirCliError extends Error {\n readonly exitCode: number;\n constructor(exitCode: number, message: string) {\n super(message);\n this.name = 'NoirCliError';\n this.exitCode = exitCode;\n }\n}\n\n/**\n * The `code` stamped onto every CommanderError our own `fail()` throws, so\n * `inferExitCode` can tell \"we threw this on purpose with an authoritative\n * exitCode\" apart from \"commander threw this for help/usage/unknown-command\"\n * (which must be remapped onto the CLI exit-code contract by `commanderExitCode`).\n */\nconst NOIR_ERROR_CODE = 'noir.error';\n\n// ---------------------------------------------------------------------------\n// Global-option shape. Commander stores `--no-input` under the attribute\n// `input` (default `true`; the flag sets it `false`) — see commander's\n// `Option.attributeName()` which strips the leading `no-`. Both `input`\n// (commander's real key) and `noInput` (the spec's intent) are accepted so\n// callers may pass either commander's raw globals or a hand-built object.\n// ---------------------------------------------------------------------------\nexport interface CliOptions {\n readonly json?: boolean;\n readonly quiet?: boolean;\n readonly verbose?: boolean;\n /** Commander's storage for `--no-input` (`false` ⇒ no input). */\n readonly input?: boolean;\n /** Convenience alias matching the `--no-input` intent (`true` ⇒ no input). */\n readonly noInput?: boolean;\n /** TUI-policy flag. `false` (`--no-tui`) forces bare `noir` onto the\n * non-interactive `status` path even in a TTY; `true` (`--tui`) is an\n * advisory hint that still requires a TTY; absent (auto) defers to\n * {@link isInteractive}. Advisory only — never hard-gates a subcommand. */\n readonly tui?: boolean;\n /** Hint-suppression flag (`--no-tips`). When `true`, redirect /\n * deprecation hints (and any other {@link tip} output) are silenced for\n * CI / log-friendly runs. */\n readonly noTips?: boolean;\n}\n\nfunction isJsonMode(opts: CliOptions): boolean {\n return opts.json === true;\n}\n\nfunction isQuietMode(opts: CliOptions): boolean {\n return opts.quiet === true;\n}\n\n/** True when the user has asked for no interactive input (either spelling). */\nexport function isNoInput(opts: CliOptions = {}): boolean {\n return opts.noInput === true || opts.input === false;\n}\n\nfunction envFlagSet(name: string): boolean {\n // NO_COLOR spec: present AND non-empty (regardless of value) disables color.\n const v = process.env[name];\n return v !== undefined && v !== '';\n}\n\n// `isCiEnv` is imported from ./theme.js (the color authority) — see note there.\n\n// ---------------------------------------------------------------------------\n// Interactivity gate. Drives both the @clack home menu and the\n// decoration of every helper below. Requires BOTH stdin and stdout to be TTYs:\n// @clack reads keypresses from stdin while ora/picocolors render to stdout.\n// ---------------------------------------------------------------------------\nexport function isInteractive(opts: CliOptions = {}): boolean {\n if (isJsonMode(opts) || isNoInput(opts)) return false;\n if (isCiEnv() || envFlagSet('NO_COLOR')) return false;\n return process.stdin.isTTY === true && process.stdout.isTTY === true;\n}\n\n/**\n * Derive the explicit `interactive` flag the scaffold engine reads (via\n * {@link ScaffoldOptions.interactive}) from the bridge bin.ts owns. The bin's\n * `preAction` sets `NOIR_NON_INTERACTIVE=1` under `--json`/`--no-input`; this\n * helper folds that with the {@link isInteractive} TTY/CI/NO_COLOR gate so a\n * single boolean flows into the engine + `buildConflictOpts`. The ENGINE itself\n * never reads `process.env` for interactivity — only this CLI helper does.\n */\nexport function resolveInteractive(): boolean {\n if (flaggedNonInteractiveCli()) return false;\n return isInteractive();\n}\n\n/** True when the bin's `preAction` flagged this invocation non-interactive\n * (`--json` / `--no-input` → `NOIR_NON_INTERACTIVE`). CLI-internal; the engine\n * reads {@link ScaffoldOptions.interactive} instead. */\nfunction flaggedNonInteractiveCli(): boolean {\n const v = process.env.NOIR_NON_INTERACTIVE;\n return v !== undefined && v !== '';\n}\n\n// ---------------------------------------------------------------------------\n// Data output (stdout). The ONLY helper here that writes to stdout; under\n// `--json` a command calls this exactly once with its full payload and never\n// mixes other stdout writes, keeping machine output pristine.\n// ---------------------------------------------------------------------------\nexport function json(obj: unknown): void {\n process.stdout.write(`${JSON.stringify(obj)}\\n`);\n}\n\n// ---------------------------------------------------------------------------\n// Human diagnostics (stderr). picocolors auto-strips ANSI under NO_COLOR /\n// non-TTY, and each helper additionally short-circuits under `--json`\n// (decoration off, payload is the output) — and `info`/`log`/`success` also\n// under `--quiet`. `warn`/`error` survive `--quiet` (they carry signal).\n// ---------------------------------------------------------------------------\nexport function log(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts) || isQuietMode(opts)) return;\n process.stderr.write(`${msg}\\n`);\n}\n\nexport function info(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts) || isQuietMode(opts)) return;\n process.stderr.write(`${c.info(msg)}\\n`);\n}\n\nexport function success(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts) || isQuietMode(opts)) return;\n process.stderr.write(`${c.ok(msg)}\\n`);\n}\n\nexport function warn(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts)) return;\n process.stderr.write(`${c.warn(msg)}\\n`);\n}\n\nexport function error(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts)) return;\n process.stderr.write(`${c.error(msg)}\\n`);\n}\n\n/**\n * Redirect / deprecation hint (stderr). Silenced under `--json` (a CI\n * consumer's stdout envelope must stay pristine) AND under `--no-tips`\n * ({@link CliOptions.noTips}). This is the ONLY helper deprecation / redirect\n * notices should use, so a single `--no-tips` flag quiets them all in CI / logs.\n * Style matches {@link warn} (these are advisory, not errors).\n */\nexport function tip(msg: string, opts: CliOptions = {}): void {\n if (isJsonMode(opts) || opts.noTips === true) return;\n process.stderr.write(`${c.warn(msg)}\\n`);\n}\n\n// ---------------------------------------------------------------------------\n// Tables (stderr). Suppressed entirely under `--json` — the command has\n// already emitted the rows as a JSON array via `json()`.\n//\n// Design: cli-table3 styles headers/borders via `@colors/colors`\n// (whose NO_COLOR/TTY semantics differ from picocolors), which painted EVERY\n// header red while the body stripped — RED is now reserved strictly for ERROR.\n// We bypass `@colors/colors` entirely by passing EMPTY `style.head`/`style.border`\n// arrays (cli-table3's `wrapWithStyleColors` early-returns on length 0) and\n// PRE-COLOR the header strings ourselves via `theme.c` (picocolors). Result:\n// the header, border, and body all strip consistently under NO_COLOR / non-TTY,\n// because picocolors is the SOLE color authority. Responsive `colWidths` are\n// computed from `terminalWidth()` so tables never overflow; `wordWrap` keeps\n// free-text cells readable, and `truncate: '…'` caps any single over-long token.\n// ---------------------------------------------------------------------------\nexport function table(\n rows: readonly Record<string, unknown>[],\n cols: readonly string[],\n opts: CliOptions = {},\n): void {\n if (isJsonMode(opts)) return;\n if (rows.length === 0) {\n info('(no rows)', opts);\n return;\n }\n const t = new Table({\n head: cols.map((col) => c.bold(c.info(col))),\n // Empty arrays ⇒ cli-table3 applies NO @colors/colors wrap (neither the\n // default red header nor a colored border). All color is ours, via theme.\n style: { head: [], border: [] },\n colWidths: computeColWidths(rows, cols),\n wordWrap: true,\n truncate: '…',\n });\n for (const row of rows) {\n t.push(cols.map((col) => formatCell(row[col])));\n }\n process.stderr.write(`${t.toString()}\\n`);\n}\n\n/**\n * Compute per-column widths (cli-table3 `colWidths`, which INCLUDE each column's\n * 2 padding chars) that fit `terminalWidth()`. Strategy:\n *\n * 1. Measure each column's NATURAL content width (longest cell, at least the\n * header label so a header never wraps/truncates).\n * 2. If the natural total fits, use it as-is — every cell renders whole.\n * 3. Otherwise GREEDILY TRIM ONLY THE WIDEST column (down to its header width)\n * until the row fits. Narrow columns (paths, ids, statuses) keep their full\n * width — they're single tokens that can't word-wrap, so truncating them\n * loses information. The widest column is the free-text one (descriptions,\n * details, snippets); it has spaces and absorbs the shrink via `wordWrap`.\n *\n * cli-table3 accepts only positive integers (negatives collapse). Each returned\n * width is `content + 2` to reserve the column's padding, so the content area\n * equals the measured width and the math against `terminalWidth()` is exact.\n */\nfunction computeColWidths(\n rows: readonly Record<string, unknown>[],\n cols: readonly string[],\n): number[] {\n const n = cols.length;\n if (n === 0) return [];\n const headerLen = cols.map((col) => col.length);\n const natural = cols.map((col, i) => {\n let max = Math.max(headerLen[i] ?? col.length, 3);\n for (const row of rows) {\n const len = formatCell(row[col]).length;\n if (len > max) max = len;\n }\n return max;\n });\n // cli-table3 row overhead: per column 1 left border + 2 padding, plus a final\n // right border ⇒ 3n + 1 non-content chars. `contentBudget` is what remains for\n // the sum of column CONTENT areas.\n const contentBudget = Math.max(n * 4, terminalWidth() - 3 * n - 1);\n const naturalSum = natural.reduce((a, b) => a + b, 0);\n if (naturalSum <= contentBudget) {\n return natural.map((w) => w + 2);\n }\n // Overflow: greedily cut the widest reducible column (≥ its header width).\n const content = natural.slice();\n let sum = naturalSum;\n const floor = headerLen.map((h) => Math.max(h, 3));\n while (sum > contentBudget) {\n let idx = -1;\n let widest = -1;\n for (let i = 0; i < n; i++) {\n const c = content[i] ?? 0;\n const f = floor[i] ?? 3;\n if (c > f && c > widest) {\n widest = c;\n idx = i;\n }\n }\n if (idx === -1) break; // every column is at its header floor — stop\n const overshoot = sum - contentBudget;\n const reducible = (content[idx] ?? 0) - (floor[idx] ?? 3);\n const cut = Math.min(overshoot, reducible);\n content[idx] = (content[idx] ?? 0) - cut;\n sum -= cut;\n }\n return content.map((w) => w + 2);\n}\n\nfunction formatCell(value: unknown): string {\n if (value === undefined || value === null) return '';\n if (typeof value === 'string') return value;\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n return JSON.stringify(value);\n}\n\n// ---------------------------------------------------------------------------\n// Definition list (stderr). A two-column Field/Value rendering for snapshot-\n// shaped output (status / task / context-status). `status.ts` hand-rolled this\n// exact shape; this generalizes it. Rendered via the same responsive `table()`,\n// so it inherits the clean (non-red) header + NO_COLOR behavior automatically.\n// ---------------------------------------------------------------------------\nexport interface DefinitionRow {\n /** Field label (left column). */\n label: string;\n /** Field value; coerced to a string the same way table cells are. */\n value: unknown;\n}\n\nexport function definitionList(rows: readonly DefinitionRow[], opts: CliOptions = {}): void {\n table(\n rows.map((r) => ({ Field: r.label, Value: formatCell(r.value) })),\n ['Field', 'Value'],\n opts,\n );\n}\n\n/** Render a single `label: value` line to stderr (human only). */\nexport function kv(label: string, value: unknown, opts: CliOptions = {}): void {\n log(`${c.bold(label)}: ${formatCell(value)}`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// Spinner (stderr). ora when interactive; a no-op otherwise so scriptable /\n// CI / `--json` / `--quiet` runs pay nothing and never animate a pipe.\n// ---------------------------------------------------------------------------\nexport interface Spinner {\n start(text?: string): Spinner;\n stop(): Spinner;\n clear(): Spinner;\n succeed(text?: string): void;\n fail(text?: string): void;\n warn(text?: string): void;\n info(text?: string): void;\n text: string;\n}\n\nclass NoopSpinner implements Spinner {\n text = '';\n start(_text?: string): Spinner {\n return this;\n }\n stop(): Spinner {\n return this;\n }\n clear(): Spinner {\n return this;\n }\n succeed(_text?: string): void {}\n fail(_text?: string): void {}\n warn(_text?: string): void {}\n info(_text?: string): void {}\n}\n\n/** Wraps an `ora` instance so the `Spinner` interface stays decoupled from ora's exact type. */\nclass OraSpinner implements Spinner {\n private readonly handle: Ora;\n\n constructor(initialText: string) {\n this.handle = ora(initialText);\n }\n\n get text(): string {\n return this.handle.text;\n }\n set text(value: string) {\n this.handle.text = value;\n }\n\n start(text?: string): Spinner {\n if (text !== undefined) this.handle.text = text;\n this.handle.start();\n return this;\n }\n\n stop(): Spinner {\n this.handle.stop();\n return this;\n }\n\n clear(): Spinner {\n this.handle.clear();\n return this;\n }\n\n succeed(text?: string): void {\n this.handle.succeed(text);\n }\n\n fail(text?: string): void {\n this.handle.fail(text);\n }\n\n warn(text?: string): void {\n this.handle.warn(text);\n }\n\n info(text?: string): void {\n this.handle.info(text);\n }\n}\n\nexport function spinner(text = '', opts: CliOptions = {}): Spinner {\n if (isJsonMode(opts) || isQuietMode(opts) || !isInteractive(opts)) {\n return new NoopSpinner();\n }\n return new OraSpinner(text);\n}\n\n// ---------------------------------------------------------------------------\n// Failure + exit-code mapping\n// ---------------------------------------------------------------------------\n\n/**\n * The one-line `{ok:false,error}` envelope every `--json` failure writes. Built\n * in one place so a failure written by `fail()` and one written on the way out\n * of a signal handler cannot drift apart.\n */\nfunction failureEnvelope(exitCode: number, message: string): string {\n return `${JSON.stringify({ ok: false, error: { code: exitCode, message } })}\\n`;\n}\n\n/**\n * Write a diagnostic and throw a `CommanderError` carrying the given exit code,\n * so bin.ts's `exitOverride` + `handleError` surface it as `process.exitCode`\n * without any mid-action `process.exit`. Under `--json` the message becomes a\n * structured `{ok:false,error}` envelope on STDOUT; otherwise plain text on\n * STDERR. Always throws (`never`).\n */\nexport function fail(exitCode: number, message: string, opts: CliOptions = {}): never {\n if (isJsonMode(opts)) {\n process.stdout.write(failureEnvelope(exitCode, message));\n } else if (message.length > 0) {\n process.stderr.write(`${message}\\n`);\n }\n throw new CommanderError(exitCode, NOIR_ERROR_CODE, message);\n}\n\n/** How long a leaving path waits for its own last line to land before it goes. */\nconst EXIT_FLUSH_MS = 250;\n\n/**\n * {@link fail} for the paths that leave from a signal handler, where there is no\n * caller left to catch what it throws: the verdict is written and the exit\n * follows it, with the same shape and the same exit code an ordinary failure\n * would have produced. Under `--json` the envelope is the run's last output, so\n * the exit waits for the write to land — a process that exits with stdout still\n * queued loses it — and goes without it if the write never lands, because the\n * whole point of this path is to leave.\n */\nexport function failAndExit(exitCode: number, message: string, opts: CliOptions = {}): void {\n // The code this process leaves with, even if the write below never lands.\n process.exitCode = exitCode;\n const jsonMode = isJsonMode(opts);\n const text = jsonMode\n ? failureEnvelope(exitCode, message)\n : message.length > 0\n ? `${message}\\n`\n : '';\n if (text.length === 0) {\n process.exit(exitCode);\n }\n let left = false;\n const leave = (): void => {\n if (left) return;\n left = true;\n process.exit(exitCode);\n };\n (jsonMode ? process.stdout : process.stderr).write(text, leave);\n setTimeout(leave, EXIT_FLUSH_MS);\n}\n\n/**\n * Guard for commands that would otherwise block on a prompt. Honors the\n * hard rule: \"no blocking prompts when !isTTY or --no-input — error exit 2\n * naming the missing flag instead\". `hint` describes the operation so the\n * message points the user at the flag-based alternative.\n */\nexport function requireInteractive(opts: CliOptions, hint: string): void {\n if (!isInteractive(opts)) {\n fail(\n EXIT.USAGE,\n `${hint} needs an interactive terminal, but input is disabled (non-TTY, --no-input, --json, CI, or NO_COLOR). Re-run in a TTY or supply the value as a flag.`,\n opts,\n );\n }\n}\n\n// Remap commander's own exit codes onto the CLI exit-code contract:\n// help/version → 0 · unknown command → 3 · every other usage error → 2\nfunction commanderExitCode(err: CommanderError): number {\n switch (err.code) {\n case 'commander.helpDisplayed':\n case 'commander.versionDisplayed':\n case 'commander.version': // commander v12 uses this code for --version (exit 0)\n return EXIT.OK;\n case 'commander.unknownCommand':\n return EXIT.NOT_FOUND;\n default:\n return EXIT.USAGE;\n }\n}\n\n/** Map any thrown value onto the CLI exit-code contract. */\nexport function inferExitCode(err: unknown): number {\n if (err instanceof NoirCliError) return err.exitCode;\n if (err instanceof CommanderError) {\n // Errors our own `fail()` threw carry a `noir.*` code and an authoritative\n // exitCode — respect it. Commander's own errors are remapped by `code`.\n if (err.code.startsWith('noir.')) return err.exitCode;\n return commanderExitCode(err);\n }\n return EXIT.ERROR;\n}\n\n/**\n * Map a thrown error onto stderr + `process.exitCode`. Never throws, never\n * calls `process.exit` (commander's `exitOverride` already prevented that for\n * commander's own errors). Used by the bin entry's `run()`.\n */\nexport function handleError(err: unknown): void {\n if (err instanceof NoirCliError) {\n if (err.message.length > 0) process.stderr.write(`${err.message}\\n`);\n } else if (err instanceof CommanderError) {\n // If `fail()` threw it, the message is already written (stderr or the JSON\n // stdout envelope); if commander threw it, `configureOutput.writeErr`\n // already wrote it during parse. Don't double-write either way.\n } else {\n process.stderr.write(`noir: ${err instanceof Error ? err.message : String(err)}\\n`);\n }\n process.exitCode = inferExitCode(err);\n}\n","// `noir context {search,index,status}`.\n//\n// Thin MCP-client commands over the running daemon's context engine. Every\n// read/write here is a single `callDaemonTool` round-trip; the daemon is the\n// sole writer of the store, so the CLI never opens the store in-process. If\n// the daemon can't be reached, `callDaemonTool` maps the failure onto exit 4\n// (DAEMON_DOWN) with the stable remediation hint — see daemon-client.ts.\n//\n// A daemon tool's OWN logical-failure envelope (`{ok:false, degraded:true,\n// error}` — e.g. a read-only store refusing `context_index`, or the embedder\n// throwing inside `context_search`) is DATA, not a transport failure: it parses\n// cleanly, so this module surfaces it honestly as exit 1 (ERROR) with the\n// daemon's message, rather than re-packaging it as daemon-down.\n//\n// Stream discipline: `--json` emits the versioned `{ok:true,data}`\n// envelope to STDOUT (the only stdout write); human tables / snippets go to\n// STDERR via the centralized `table()` / `log()` helpers (auto-stripped under\n// NO_COLOR / non-TTY / --json). `--limit` is coerced here; an invalid value is a\n// USAGE error (exit 2) before we touch the daemon.\n\nimport {\n callDaemonTool,\n type DaemonClientOptions,\n type DaemonProbe,\n probeDaemon,\n withInProcessRead,\n} from '../daemon-client.js';\nimport { type CliOptions, definitionList, EXIT, fail, info, log, table } from '../output.js';\nimport { badge } from '../theme.js';\n\n/** Options accepted by every `context` sub-command (globals + daemon knobs). */\nexport interface ContextOptions extends CliOptions, DaemonClientOptions {}\n\n// ---------------------------------------------------------------------------\n// Tool result shapes (the relevant slices of the daemon's wire payloads). The\n// daemon returns these as JSON text; daemon-client parses to `unknown`, so each\n// reader treats a foreign field as undefined and the command degrades to a clear\n// error rather than crashing. Local types = the CLI depends only on the MCP\n// wire contract (mirrors status.ts).\n// ---------------------------------------------------------------------------\n\n/** Normalized search hit rendered to humans + emitted in the JSON payload. */\nexport interface ContextHit {\n path: string;\n score: number;\n snippet: string;\n source: string;\n}\n\n/** `context_search` success payload (SearchResult + the echoed query). */\nexport interface ContextSearchData {\n query: string;\n hits: ContextHit[];\n consumedTokens: number;\n truncated: boolean;\n degraded: boolean;\n mode: string;\n}\n\n/** `context_index` success payload (IndexResult). */\nexport interface ContextIndexData {\n indexed: number;\n skipped: number;\n deleted: number;\n failed: number;\n totalChunks: number;\n degraded: boolean;\n}\n\n/** `context_status` payload (ContextStatus). `ok` is a literal so the success /\n * failure union narrows cleanly against {@link ToolFailure} (ok:false). */\nexport interface ContextStatusData {\n ok: true;\n projectId: string;\n docCount: number;\n vecCount: number;\n indexedFiles: number;\n embedder: { kind: string; model?: string; dim: number };\n degraded: boolean;\n}\n\n/** A daemon logical-failure envelope (`{ok:false,…}`) — read generically. */\ninterface ToolFailure {\n ok: false;\n degraded?: boolean;\n error?: string;\n reason?: string;\n}\n\n/** The raw `context_search` wire payload (success branch). */\ninterface ContextSearchResult {\n ok: true;\n results?: unknown;\n consumedTokens?: number;\n truncated?: boolean;\n degraded?: boolean;\n mode?: string;\n}\n\n/** The raw `context_index` wire payload (success branch). */\ninterface ContextIndexResult {\n ok: true;\n indexed?: number;\n skipped?: number;\n deleted?: number;\n failed?: number;\n totalChunks?: number;\n degraded?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Small shared helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Coerce a commander `--limit <n>` string into a positive int, or fail with\n * exit 2 (USAGE) naming the flag — mirrors the daemon's zod\n * `number().int().positive()` but at the CLI edge so a bad value never starts a\n * daemon round-trip.\n */\nfunction parseLimit(raw: string | undefined, label: string, opts: CliOptions): number | undefined {\n if (raw === undefined) return undefined;\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n fail(EXIT.USAGE, `${label}: --limit must be a positive integer (got '${raw}')`, opts);\n }\n return n;\n}\n\n/**\n * Normalize a daemon logical-failure envelope into a thrown exit-1 ERROR. The\n * envelope is data (it parsed cleanly), so this is NOT daemon-down; we surface\n * the daemon's own `error`/`reason` verbatim so the user sees the real cause\n * (e.g. \"store is read-only (daemon down) — context_index is unavailable\").\n */\nfunction failTool(label: string, envelope: ToolFailure, opts: CliOptions): never {\n const detail = envelope.error ?? envelope.reason ?? 'unknown failure';\n fail(EXIT.ERROR, `${label}: ${detail}`, opts);\n}\n\n/** Narrow a raw wire hit (RetrieverHit) into the rendered {@link ContextHit}. */\nfunction toHit(raw: unknown): ContextHit {\n const h = (raw ?? {}) as Record<string, unknown>;\n return {\n path: typeof h.path === 'string' ? h.path : '<unknown>',\n score: typeof h.score === 'number' ? h.score : 0,\n snippet: typeof h.snippet === 'string' ? h.snippet : '',\n source: typeof h.source === 'string' ? h.source : '',\n };\n}\n\n// ---------------------------------------------------------------------------\n// `noir context search <query> [--limit N]`\n// ---------------------------------------------------------------------------\nexport interface ContextSearchOptions extends ContextOptions {\n query: string;\n /** Raw `--limit` string from commander; parsed + validated here. */\n limit?: string;\n}\n\n/**\n * Run `context_search` against the daemon when it is up, or fall back to the\n * IN-PROCESS read-only engine when the daemon probe reports it down.\n *\n * A daemon-down probe is a READ degradation, not a write failure: the read-only\n * store keeps working, so instead of exit 4 the search runs against a fresh\n * in-process {@link withInProcessRead} ContextEngine over the same project\n * store (BM25-only when the embedder is unavailable). Writes (`context\n * index`) keep the daemon-required exit-4 path. When the daemon IS up, the\n * probe result is ignored and the normal daemon round-trip proceeds unchanged\n * (the probe only ever short-circuits on a CONFIRMED down).\n *\n * The probe is best-effort and CONSERVATIVE: an unavailable probe (a host that\n * stubs the daemon-client without a `probeDaemon` export, or a probe transport\n * hiccup) is treated as \"daemon may be up\" so the command proceeds down the\n * normal daemon path (which maps a genuinely-down daemon to exit 4). The\n * fallback only engages on a CONFIRMED `{running:false}`.\n */\nexport async function contextSearch(opts: ContextSearchOptions): Promise<void> {\n const limit = parseLimit(opts.limit, 'context search', opts);\n let probe: DaemonProbe;\n try {\n probe = await probeDaemon(opts);\n } catch {\n // Conservative: can't confirm the daemon is down → use the daemon path.\n probe = { running: true };\n }\n if (!probe.running) {\n await withInProcessRead(opts, async (engines) => {\n const result = await engines.context.search(opts.query, {\n ...(limit === undefined ? {} : { limit }),\n });\n const data: ContextSearchData = {\n query: opts.query,\n hits: result.results.map((h) => ({\n path: h.path,\n score: h.score,\n snippet: h.snippet,\n source: h.source,\n })),\n consumedTokens: result.consumedTokens,\n truncated: result.truncated,\n degraded: result.degraded,\n mode: result.mode,\n };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSearch(data, opts);\n });\n return;\n }\n const res = await callDaemonTool<ContextSearchResult | ToolFailure>(opts, 'context_search', {\n query: opts.query,\n ...(limit === undefined ? {} : { limit }),\n });\n if (res.ok !== true) failTool('context search', res, opts);\n\n const data: ContextSearchData = {\n query: opts.query,\n hits: Array.isArray(res.results) ? (res.results as unknown[]).map(toHit) : [],\n consumedTokens: typeof res.consumedTokens === 'number' ? res.consumedTokens : 0,\n truncated: res.truncated === true,\n degraded: res.degraded === true,\n mode: typeof res.mode === 'string' ? res.mode : 'unknown',\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSearch(data, opts);\n}\n\nfunction renderSearch(data: ContextSearchData, opts: CliOptions): void {\n const flag = data.degraded ? ` ${badge('warn', 'degraded: BM25-only')}` : '';\n const trunc = data.truncated ? ' (budget hit — results truncated)' : '';\n log(\n `context search — ${data.hits.length} hit${data.hits.length === 1 ? '' : 's'} · ${data.mode} · ${data.consumedTokens} tokens${flag}${trunc}`,\n opts,\n );\n table(\n data.hits.map((h, i) => ({\n '#': i + 1,\n Path: h.path,\n Score: h.score.toFixed(4),\n Snippet: h.snippet,\n })),\n ['#', 'Path', 'Score', 'Snippet'],\n opts,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `noir context index [--path ...] [--force]`\n// ---------------------------------------------------------------------------\nexport interface ContextIndexOptions extends ContextOptions {\n /** Raw repeated `--path` values from commander (`undefined` ⇒ index root). */\n paths?: string[];\n /** Force a full reindex (drop all chunks+vectors, re-index from scratch). */\n force?: boolean;\n}\n\nexport async function contextIndex(opts: ContextIndexOptions): Promise<void> {\n // force:true → the daemon drops every indexed chunk + vector and re-indexes\n // the registered roots from scratch; otherwise the content-hash\n // incremental walk. Omit the key entirely when not forced so the incremental\n // contract (and any daemon defaulting) stays unambiguous on the wire.\n const args: Record<string, unknown> =\n opts.paths && opts.paths.length > 0 ? { paths: opts.paths } : {};\n if (opts.force === true) args.force = true;\n const res = await callDaemonTool<ContextIndexResult | ToolFailure>(opts, 'context_index', args);\n if (res.ok !== true) failTool('context index', res, opts);\n\n const data: ContextIndexData = {\n indexed: typeof res.indexed === 'number' ? res.indexed : 0,\n skipped: typeof res.skipped === 'number' ? res.skipped : 0,\n deleted: typeof res.deleted === 'number' ? res.deleted : 0,\n failed: typeof res.failed === 'number' ? res.failed : 0,\n totalChunks: typeof res.totalChunks === 'number' ? res.totalChunks : 0,\n degraded: res.degraded === true,\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderIndex(data, opts.paths, opts);\n}\n\nfunction renderIndex(data: ContextIndexData, paths: string[] | undefined, opts: CliOptions): void {\n const scope = paths && paths.length > 0 ? paths.join(', ') : '.';\n const flag = data.degraded ? ` ${badge('warn', 'degraded: vectors skipped')}` : '';\n log(`context index — ${scope}${flag}`, opts);\n definitionList(\n [\n { label: 'Indexed (new)', value: data.indexed },\n { label: 'Skipped (unchanged)', value: data.skipped },\n { label: 'Deleted (removed)', value: data.deleted },\n { label: 'Failed', value: data.failed },\n { label: 'Total chunks', value: data.totalChunks },\n ],\n opts,\n );\n if (data.failed > 0) {\n info(`${data.failed} file(s) could not be indexed (binary / IO / encoding).`, opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir context status`\n// ---------------------------------------------------------------------------\nexport async function contextStatus(opts: ContextOptions): Promise<void> {\n const res = await callDaemonTool<ContextStatusData | ToolFailure>(opts, 'context_status');\n if (res.ok !== true) failTool('context status', res, opts);\n // `context_status` already carries its own `ok:true`; reuse it as the payload.\n const data = res;\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderStatus(data, opts);\n}\n\nfunction describeEmbedder(e: ContextStatusData['embedder']): string {\n if (!e || e.kind === 'none') return 'none (BM25-only)';\n const model = typeof e.model === 'string' && e.model.length > 0 ? e.model : '<unset>';\n return `${e.kind} · ${model} (${e.dim}-dim)`;\n}\n\nfunction renderStatus(data: ContextStatusData, opts: CliOptions): void {\n const flag = data.degraded ? ` ${badge('warn', 'degraded')}` : '';\n log(`context status — ${data.projectId}${flag}`, opts);\n definitionList(\n [\n { label: 'Project', value: data.projectId },\n { label: 'Docs', value: data.docCount },\n { label: 'Vectors', value: data.vecCount },\n { label: 'Indexed files', value: data.indexedFiles },\n { label: 'Embedder', value: describeEmbedder(data.embedder) },\n { label: 'Degraded', value: data.degraded ? 'yes' : 'no' },\n ],\n opts,\n );\n}\n","// `.mcp.json` rewrite/restore for workspace membership — the ONLY user-facing\n// file a join/leave touches. It rewrites just the `noir` server entry (preserving\n// every other server the user added) and refuses to clobber a config that does\n// not look Noir-emitted unless `--force` (never a silent overwrite).\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { atomicWriteFile, resolveNoirCommand } from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail } from './output.js';\n\nfunction mcpPathFor(root: string, host: HostId): string {\n return resolveAdapter(host).mcpConfigPath?.({ root }) ?? join(root, '.mcp.json');\n}\n\n/** Read + parse an existing host MCP config, or `null` when the file is absent. */\nfunction readJson(path: string): { mcpServers?: Record<string, unknown> } | null {\n if (!existsSync(path)) return null; // absent → fresh write is fine\n // Unparseable (JSONC/commented/trailing-comma) THROWS so the caller can refuse —\n // never silently clobber a user-edited file that happens to not be strict JSON.\n return JSON.parse(readFileSync(path, 'utf8')) as { mcpServers?: Record<string, unknown> };\n}\n\n/** Load an existing config, refusing (exit 1) on an unparseable file unless --force. */\nfunction loadExisting(\n path: string,\n opts: CliOptions & { force?: boolean },\n): { mcpServers?: Record<string, unknown> } | null {\n let existing: { mcpServers?: Record<string, unknown> } | null;\n try {\n existing = readJson(path);\n } catch {\n if (opts.force !== true) {\n fail(\n EXIT.ERROR,\n `Refusing to rewrite ${path}: it is not valid JSON (add --force to overwrite).`,\n opts,\n );\n }\n existing = null; // --force: overwrite the unparseable file\n }\n if (existing !== null && !isNoirEmitted(existing.mcpServers) && opts.force !== true) {\n fail(\n EXIT.ERROR,\n `Refusing to rewrite ${path}: it does not look like a Noir-emitted config (add --force to overwrite).`,\n opts,\n );\n }\n return existing;\n}\n\nfunction isNoirEmitted(mcpServers: Record<string, unknown> | undefined): boolean {\n const noir = mcpServers?.noir as { command?: unknown; type?: unknown } | undefined;\n if (!noir) return false;\n if (typeof noir.command === 'string') return true;\n return noir.type === 'http';\n}\n\n/** Point the repo's `noir` MCP entry at the workspace daemon (with the member `?p=` identity). */\nexport function writeWorkspaceHttpEntry(\n root: string,\n host: HostId,\n url: string,\n projectId: string,\n opts: CliOptions & { force?: boolean },\n): void {\n const path = mcpPathFor(root, host);\n const existing = loadExisting(path, opts);\n const next = {\n ...(existing ?? {}),\n mcpServers: {\n ...(existing?.mcpServers ?? {}),\n noir: { type: 'http', url: `${url}?p=${projectId}` },\n },\n };\n atomicWriteFile(path, `${JSON.stringify(next, null, 2)}\\n`);\n}\n\n/** Restore the repo's `noir` MCP entry to stdio (used by `workspace leave`). */\nexport function writeStdioEntry(\n root: string,\n host: HostId,\n opts: CliOptions & { force?: boolean },\n): void {\n const path = mcpPathFor(root, host);\n const existing = loadExisting(path, opts);\n const noir = { command: resolveNoirCommand(), args: ['mcp', 'serve', '--stdio'] };\n const next = { ...(existing ?? {}), mcpServers: { ...(existing?.mcpServers ?? {}), noir } };\n atomicWriteFile(path, `${JSON.stringify(next, null, 2)}\\n`);\n}\n","// `noir workspace *` + the `--workspace` branch of `noir daemon start` + `noir daemon join`.\n//\n// A workspace is a named cross-repo sharing unit. `daemon start --workspace <name>`\n// founds/joins it from the current repo and starts the detached workspace daemon;\n// `daemon join <name>` joins it from another repo. `workspace list/status/leave/stop`\n// manage membership. Joining writes a `.noir/workspace.json` marker + rewrites the\n// repo's `.mcp.json` `noir` entry to the workspace daemon URL (`?p=<projectId>`);\n// leaving reverses both. Default transport (stdio) and project behavior are\n// untouched unless a repo explicitly joins.\nimport { readdirSync } from 'node:fs';\nimport {\n clearWorkspaceMarker,\n ensureWorkspaceRegistry,\n isValidWorkspaceName,\n isWorkspaceMember,\n loadProjectInfo,\n type ProjectInfo,\n readWorkspaceMarker,\n readWorkspaceRegistry,\n removeWorkspaceMember,\n upsertWorkspaceMember,\n workspaceHomeDir,\n writeWorkspaceMarker,\n} from '@noir-ai/core';\nimport {\n clearWorkspaceDaemonRecord,\n ensureWorkspaceDaemonRunning,\n pidAlive,\n readWorkspaceDaemonRecord,\n spawnDetachedWorkspaceDaemon,\n} from '@noir-ai/daemon';\nimport { type CliOptions, EXIT, fail, info, log } from '../output.js';\nimport { writeStdioEntry, writeWorkspaceHttpEntry } from '../workspace-mcp.js';\n\nexport interface WorkspaceStartOptions extends CliOptions {\n name: string;\n detach?: boolean;\n detachChild?: boolean;\n force?: boolean;\n}\nexport interface WorkspaceJoinOptions extends CliOptions {\n name: string;\n force?: boolean;\n}\nexport interface WorkspaceStatusOptions extends CliOptions {\n name?: string;\n}\nexport interface WorkspaceLeaveOptions extends CliOptions {\n force?: boolean;\n}\n\nfunction loadProjectOrFail(opts: CliOptions): ProjectInfo {\n try {\n return loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n}\n\nfunction finishJoin(\n project: ProjectInfo,\n name: string,\n url: string,\n opts: CliOptions & { force?: boolean },\n): void {\n writeWorkspaceMarker(project.root, name);\n writeWorkspaceHttpEntry(project.root, project.config.host, url, project.id, opts);\n}\n\n/** `noir daemon start --workspace <name>` — found a workspace + start its daemon. */\nexport async function daemonStartWorkspace(opts: WorkspaceStartOptions): Promise<void> {\n const project = loadProjectOrFail(opts);\n if (!isValidWorkspaceName(opts.name)) {\n fail(\n EXIT.USAGE,\n `invalid workspace name ${JSON.stringify(opts.name)} (lowercase letters, digits, and dashes).`,\n opts,\n );\n }\n // Found/join: register THIS repo as a member (idempotent by projectId).\n upsertWorkspaceMember(ensureWorkspaceRegistry(opts.name), {\n projectId: project.id,\n root: project.root,\n joinedAt: Date.now(),\n });\n\n // Detached-child path (set by spawnDetachedWorkspaceDaemon): run in-process.\n if (opts.detachChild === true) {\n const ensured = await ensureWorkspaceDaemonRunning({\n name: opts.name,\n project,\n idleTimeoutSec: project.config.workspace.idleTimeoutSec,\n });\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { mode: 'detached' } })}\\n`);\n return;\n }\n info('noir workspace daemon: detached foreground. Ctrl+C to stop.', opts);\n log(`workspace daemon listening at ${ensured.url}`, opts);\n return;\n }\n\n // --detach: fork a detached child; the parent writes the marker + .mcp.json.\n if (opts.detach === true) {\n const spawned = await spawnDetachedWorkspaceDaemon({ name: opts.name, project });\n finishJoin(project, opts.name, `http://127.0.0.1:${spawned.port}/mcp`, opts);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { mode: 'detached', pid: spawned.pid, port: spawned.port } })}\\n`,\n );\n return;\n }\n info(\n `workspace \"${opts.name}\" daemon started (pid ${spawned.pid}, port ${spawned.port}).`,\n opts,\n );\n return;\n }\n\n // Foreground: the workspace server keeps this process alive.\n const ensured = await ensureWorkspaceDaemonRunning({\n name: opts.name,\n project,\n idleTimeoutSec: project.config.workspace.idleTimeoutSec,\n });\n finishJoin(project, opts.name, ensured.url, opts);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { url: ensured.url, port: ensured.port, reused: !ensured.started } })}\\n`,\n );\n return;\n }\n info('noir workspace daemon: foreground mode. Ctrl+C to stop.', opts);\n log(`workspace daemon listening at ${ensured.url}`, opts);\n}\n\n/** `noir daemon join <name>` — join an existing workspace from this repo. */\nexport async function daemonJoin(opts: WorkspaceJoinOptions): Promise<void> {\n const project = loadProjectOrFail(opts);\n if (!isValidWorkspaceName(opts.name)) {\n fail(EXIT.USAGE, `invalid workspace name ${JSON.stringify(opts.name)}.`, opts);\n }\n const reg = readWorkspaceRegistry(opts.name);\n if (!reg) {\n fail(\n EXIT.ERROR,\n `workspace ${JSON.stringify(opts.name)} not found — start it from a member repo with \\`noir daemon start --workspace ${opts.name}\\`.`,\n opts,\n );\n }\n const ensured = await ensureWorkspaceDaemonRunning({\n name: opts.name,\n project,\n idleTimeoutSec: project.config.workspace.idleTimeoutSec,\n });\n upsertWorkspaceMember(reg, { projectId: project.id, root: project.root, joinedAt: Date.now() });\n finishJoin(project, opts.name, ensured.url, opts);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { joined: true, url: ensured.url, port: ensured.port } })}\\n`,\n );\n return;\n }\n info(`joined workspace ${JSON.stringify(opts.name)} (${ensured.url}).`, opts);\n}\n\n/** `noir workspace list` — every workspace on this machine + member count. */\nexport async function workspaceList(opts: CliOptions): Promise<void> {\n let names: string[] = [];\n try {\n names = readdirSync(workspaceHomeDir(), { withFileTypes: true })\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n } catch {\n names = [];\n }\n const rows = names.map((n) => {\n const reg = readWorkspaceRegistry(n);\n return {\n name: n,\n members: reg?.members.length ?? 0,\n // A record file whose pid is dead (daemon killed without cleanup) must not\n // read as \"running\" — mirror the pidAlive liveness `workspace status` uses.\n running: (() => {\n const rec = readWorkspaceDaemonRecord(n);\n return rec !== null && pidAlive(rec.pid);\n })(),\n };\n });\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { workspaces: rows } })}\\n`);\n return;\n }\n if (rows.length === 0) {\n info('No workspaces yet.', opts);\n return;\n }\n for (const r of rows) {\n log(`${r.name} — ${r.members} member(s)${r.running ? ', running' : ''}`, opts);\n }\n}\n\n/** `noir workspace status [name]` — members + daemon liveness. */\nexport async function workspaceStatus(opts: WorkspaceStatusOptions): Promise<void> {\n const name = opts.name ?? readWorkspaceMarker(process.cwd()) ?? undefined;\n if (name === undefined) {\n fail(EXIT.USAGE, 'workspace status requires a name (or run from a joined repo).', opts);\n }\n if (!isValidWorkspaceName(name)) {\n fail(EXIT.USAGE, `invalid workspace name ${JSON.stringify(name)}.`, opts);\n }\n const reg = readWorkspaceRegistry(name);\n const rec = readWorkspaceDaemonRecord(name);\n const running = rec !== null && pidAlive(rec.pid);\n const data = {\n name,\n members: reg?.members.map((m) => m.projectId) ?? [],\n running,\n ...(rec ? { pid: rec.pid, port: rec.port } : {}),\n };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(\n `workspace ${name}: ${running ? `running (pid ${rec?.pid}, port ${rec?.port})` : 'not running'}`,\n opts,\n );\n for (const m of data.members) log(` member: ${m}`, opts);\n}\n\n/** `noir workspace leave` — remove this repo from membership + restore stdio. */\nexport async function workspaceLeave(opts: WorkspaceLeaveOptions): Promise<void> {\n const project = loadProjectOrFail(opts);\n const name = readWorkspaceMarker(project.root);\n if (name === null) {\n fail(EXIT.USAGE, 'this repo is not joined to a workspace.', opts);\n }\n // Restore stdio FIRST — it is the one step that can fail (a non-Noir .mcp.json\n // is refused without `--force`). Doing it last would strand the repo mid-leave:\n // membership removed + marker cleared, but .mcp.json still pointed at a\n // workspace the repo no longer belongs to.\n writeStdioEntry(project.root, project.config.host, opts);\n const reg = readWorkspaceRegistry(name);\n if (reg && isWorkspaceMember(reg, project.id)) {\n removeWorkspaceMember(reg, project.id);\n }\n clearWorkspaceMarker(project.root);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { left: name } })}\\n`);\n return;\n }\n info(`left workspace ${JSON.stringify(name)}.`, opts);\n}\n\n/** `noir workspace stop [name]` — stop the workspace daemon (membership retained). */\nexport async function workspaceStop(opts: WorkspaceStatusOptions): Promise<void> {\n const name = opts.name ?? readWorkspaceMarker(process.cwd()) ?? undefined;\n if (name === undefined) {\n fail(EXIT.USAGE, 'workspace stop requires a name (or run from a joined repo).', opts);\n }\n if (!isValidWorkspaceName(name)) {\n fail(EXIT.USAGE, `invalid workspace name ${JSON.stringify(name)}.`, opts);\n }\n const rec = readWorkspaceDaemonRecord(name);\n if (rec === null || !pidAlive(rec.pid)) {\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { running: false, stopped: false } })}\\n`,\n );\n return;\n }\n info('workspace daemon is not running.', opts);\n return;\n }\n try {\n process.kill(rec.pid, 'SIGTERM');\n } catch {\n // already exited between the probe and the signal\n }\n clearWorkspaceDaemonRecord(name);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { running: false, stopped: true, pid: rec.pid } })}\\n`,\n );\n return;\n }\n info(`stopped workspace daemon (pid ${rec.pid}).`, opts);\n}\n","// `noir daemon {start,stop,status,restart}` (+ `token`).\n//\n// Honest daemon UX. `noir daemon start` runs the daemon either\n// FOREGROUND or DETACHED, depending on flags:\n// - default (foreground): `ensureDaemonRunning` (from @noir-ai/daemon) starts\n// a fresh in-process HTTP server — whose listen handle + idle timer +\n// SIGINT/SIGTERM handlers (installed inside `startHttpServer`) keep this CLI\n// process alive until idle-stop or a signal — or reuses an already-healthy\n// one and exits.\n// - `--detach` (parent path): `spawnDetachedDaemon` forks a detached child\n// `noir daemon start --_detached-child` (unref'd + silent stdio), waits for\n// the child's daemon record + `/health` to confirm it is serving, reports\n// `{mode:'detached', pid, port}`, and the PARENT exits — the child keeps the\n// daemon alive in the background.\n// - `--_detached-child` (child path; hidden, set by the parent's spawn):\n// we ARE the detached child — run `ensureDaemonRunning` in-process (the HTTP\n// server keeps THIS process alive) and emit `{mode:'detached'}`. The child\n// is the SINGLE writer of the daemon record (its own pid), so the parent\n// discovers the port by polling that record.\n// Both detached paths guard double-spawn: an already-healthy daemon is reported\n// `{mode:'detached', reused:true}` and never spawned twice.\n//\n// Stream discipline: `--json` emits the versioned `{ok,data}` envelope\n// to stdout (the only stdout write); every human diagnostic goes to stderr via\n// the centralized helpers. Exit codes follow the CLI contract: a missing/stale\n// daemon record on `status` → exit 4 (DAEMON_DOWN); an uninitialized project\n// (`loadProjectInfo` throws) → exit 1 with the hint.\n\nimport { loadProjectInfo, type ProjectInfo } from '@noir-ai/core';\nimport {\n clearProjectDaemonRecord,\n ensureDaemonRunning,\n pidAlive,\n readDaemonToken,\n readProjectDaemonRecord,\n spawnDetachedDaemon,\n} from '@noir-ai/daemon';\nimport { PROBE_TIMEOUT_MS } from '../daemon-client.js';\nimport { type CliOptions, EXIT, fail, info, log, spinner } from '../output.js';\nimport { daemonStartWorkspace } from './workspace.js';\n\n/** Options accepted by `daemon` sub-commands (the global flags only). */\nexport interface DaemonOptions extends CliOptions {}\n\n/** `daemon start` adds the real `--detach` flag plus the hidden\n * `--_detached-child` marker the detached child carries. */\nexport interface DaemonStartOptions extends DaemonOptions {\n /** `--detach`: parent path — fork a detached child and exit. */\n detach?: boolean;\n /** `--_detached-child` (hidden, reserved): we ARE the detached child. */\n detachChild?: boolean;\n /** `--workspace <name>`: start a shared cross-repo workspace daemon instead. */\n workspace?: string;\n /** `--force`: overwrite a non-Noir `.mcp.json` entry (the `--workspace` join). */\n force?: boolean;\n}\n\n/** Human label for the detached mode in `status` output. */\nconst MODE = 'foreground' as const;\n\n/** Shape of the `/health` body the daemon's HTTP server serves. */\ninterface HealthBody {\n ok?: boolean;\n pid?: number;\n projectId?: string;\n uptimeSec?: number;\n}\n\nfunction formatUptime(sec: number): string {\n if (!Number.isFinite(sec) || sec < 0) return 'unknown';\n if (sec < 60) return `${sec}s`;\n if (sec < 3600) return `${Math.floor(sec / 60)}m ${sec % 60}s`;\n return `${Math.floor(sec / 3600)}h ${Math.floor((sec % 3600) / 60)}m`;\n}\n\n/** Resolve the CALLER's project id — the lookup key for this project's daemon\n * record (the record is per-project). `undefined` when uninitialized: there is\n * no project whose daemon we could legitimately report or stop. */\nfunction resolveCallerProjectId(): string | undefined {\n try {\n return loadProjectInfo(process.cwd()).id;\n } catch {\n return undefined;\n }\n}\n\n/** Best-effort `/health` probe (never throws). Bounded — a blackhole socket that\n * accepts TCP but never answers must not hang `noir daemon start/status/stop`\n * indefinitely. When `expectedPid` is given, the probe ALSO requires the\n * responding daemon's own pid to match (the PID-reuse guard: a foreign process\n * that recycled the recorded pid must never be signalled as \"our daemon\"). */\nasync function isHealthy(\n port: number,\n expectedPid?: number,\n expectedProjectId?: string,\n): Promise<boolean> {\n try {\n const res = await fetch(`http://127.0.0.1:${port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n if (res.status !== 200) return false;\n if (expectedPid === undefined) return true;\n const body = (await res.json()) as HealthBody | undefined;\n if (body?.ok !== true || body.pid !== expectedPid) return false;\n // Cross-project isolation: only reuse a daemon that serves the expected\n // project (absent /health projectId — a pre-1.12 daemon — is not trusted).\n if (expectedProjectId !== undefined && body.projectId !== expectedProjectId) return false;\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon start` (foreground default; `--detach` + `--_detached-child`)\n// ---------------------------------------------------------------------------\n/**\n * Start the Noir daemon — foreground by default, or detached via `--detach`.\n *\n * Three modes, chosen by the flags (all three guard double-spawn: an\n * already-healthy daemon is reported `{mode:'detached', reused:true}` / the\n * foreground `reused:true` envelope and never started twice):\n *\n * 1. `--_detached-child` (hidden, only ever set by `spawnDetachedDaemon`'s\n * child argv): we ARE the detached daemon. Run `ensureDaemonRunning`\n * in-process — the started HTTP server + idle timer + signal handlers keep\n * THIS process alive as the background daemon — emit `{mode:'detached'}`\n * (or `{mode:'detached', reused:true}` when a healthy daemon is reused, in\n * which case the redundant child exits) and return. The child is the SINGLE\n * writer of the daemon record (its own pid), which is how the parent learns\n * the port. We deliberately do NOT call `ensured.stop()` — that would undo\n * the daemon we just started (or tear down a reused one owned elsewhere).\n * 2. `--detach` (parent path): `spawnDetachedDaemon` forks the detached\n * child, waits until the child's record + `/health` confirm it is serving,\n * emits `{mode:'detached', pid, port}`, and the PARENT returns (and thus\n * exits — it never blocks). Guard first: if a daemon is ALREADY healthy,\n * report `{mode:'detached', reused:true}` and return WITHOUT spawning.\n * 3. Foreground (default): `ensureDaemonRunning` in-process. When it STARTS a\n * daemon, the HTTP server keeps this process alive until\n * SIGINT/SIGTERM/idle-timeout (the function returns but the process does not\n * exit); when it REUSES a healthy daemon, this process reports and exits.\n *\n * `--json` emits the one envelope to stdout before the process blocks/returns.\n */\nexport async function daemonStart(opts: DaemonStartOptions): Promise<void> {\n // Workspace mode: `--workspace <name>` founds/joins a cross-repo workspace and\n // starts its daemon (a separate daemon kind — never the project daemon).\n if (opts.workspace !== undefined) {\n await daemonStartWorkspace({\n ...opts,\n name: opts.workspace,\n ...(opts.detach === true ? { detach: true } : {}),\n ...(opts.detachChild === true ? { detachChild: true } : {}),\n });\n return;\n }\n // A throw from loadProjectInfo on an uninitialized project must route through\n // fail() — otherwise under --json stdout stays EMPTY (the raw error only\n // reaches stderr), violating the `{ok:false}` envelope contract.\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n\n // Detached CHILD path. The parent forked us via `--_detached-child`;\n // run the daemon in-process (the HTTP server keeps THIS process alive).\n if (opts.detachChild === true) {\n const child = await ensureDaemonRunning({\n project,\n idleTimeoutSec: project.config.daemon.idleTimeoutSec,\n ...(project.config.daemon.port !== undefined ? { port: project.config.daemon.port } : {}),\n });\n if (child.started) {\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { mode: 'detached' } })}\\n`);\n return;\n }\n // The HTTP server + idle timer + signal handlers inside startHttpServer\n // keep this child process alive past `return` — it IS the daemon now.\n info('noir daemon: detached foreground. Ctrl+C to stop.', opts);\n log(`Noir daemon listening at ${child.url}`, opts);\n return;\n }\n // An already-healthy daemon was reused — this redundant child reports and\n // exits (the parent's double-spawn guard should have caught this, but the\n // child re-checks so two racing spawns can never start two daemons).\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { mode: 'detached', reused: true } })}\\n`,\n );\n return;\n }\n info('Noir daemon already running; detached child exiting.', opts);\n return;\n }\n\n // `--detach` PARENT path. Fork a detached child and let it own the\n // daemon; the parent reports the backgrounded daemon and exits.\n if (opts.detach === true) {\n // Double-spawn guard: only spawn a child if no daemon is already healthy\n // (the child would merely reuse it and exit — a wasted detached process).\n // Scoped read: THIS project's record, so a daemon serving another project\n // is neither adopted nor counted as ours.\n const existing = readProjectDaemonRecord(project.id);\n if (\n existing &&\n pidAlive(existing.pid) &&\n (await isHealthy(existing.port, existing.pid, project.id))\n ) {\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { mode: 'detached', reused: true } })}\\n`,\n );\n return;\n }\n info(\n `Noir daemon already running at http://127.0.0.1:${existing.port}/mcp (detach not needed).`,\n opts,\n );\n return;\n }\n let spawned: Awaited<ReturnType<typeof spawnDetachedDaemon>>;\n try {\n spawned = await spawnDetachedDaemon({ project });\n } catch (err) {\n fail(\n EXIT.ERROR,\n `noir daemon start --detach failed: ${err instanceof Error ? err.message : String(err)}`,\n opts,\n );\n }\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { mode: 'detached', pid: spawned.pid, port: spawned.port } })}\\n`,\n );\n return;\n }\n info(`Noir daemon started in the background (pid ${spawned.pid}, port ${spawned.port}).`, opts);\n // The parent returns (and exits); the detached child owns the daemon.\n return;\n }\n\n // Foreground (default) — run the daemon in-process.\n const ds = spinner('Starting Noir daemon...', opts).start();\n let ensured: Awaited<ReturnType<typeof ensureDaemonRunning>>;\n try {\n ensured = await ensureDaemonRunning({\n project,\n idleTimeoutSec: project.config.daemon.idleTimeoutSec,\n ...(project.config.daemon.port !== undefined ? { port: project.config.daemon.port } : {}),\n });\n } catch (err) {\n ds.fail('Daemon failed to start');\n fail(\n EXIT.ERROR,\n `noir daemon start failed: ${err instanceof Error ? err.message : String(err)}`,\n opts,\n );\n }\n\n if (ensured.started) {\n ds.succeed('Daemon started');\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n data: {\n url: ensured.url,\n port: ensured.port,\n pid: process.pid,\n mode: MODE,\n reused: false,\n },\n })}\\n`,\n );\n return;\n }\n info('noir daemon: foreground mode. Ctrl+C to stop.', opts);\n log(`Noir daemon listening at ${ensured.url}`, opts);\n // NOTE: the started HTTP server + idle timer + signal handlers installed\n // inside startHttpServer keep this process alive past `return`. We\n // intentionally do NOT call ensured.stop(): that would undo the start.\n return;\n }\n\n // Reused an already-running daemon — this process exits after reporting.\n ds.succeed('Daemon already running');\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { url: ensured.url, port: ensured.port, reused: true } })}\\n`,\n );\n return;\n }\n log(`Noir daemon already running at ${ensured.url}`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon stop`\n// ---------------------------------------------------------------------------\n/**\n * Stop the recorded daemon (SIGTERM its pid) and clear the record.\n *\n * Best-effort exit 0: a missing record (\"not running\") and a signal failure\n * both report and succeed, matching the pre-migration behavior. `--json` emits\n * `{ok:true, data:{running, stopped, pid?}}` to stdout.\n */\nexport async function daemonStop(opts: DaemonOptions): Promise<void> {\n // The record is keyed by ProjectId, so the caller's project IS the scope: a\n // daemon serving another project is simply not visible here (no\n // cross-project guard needed — the foreign record is a different file). When\n // the caller has no project there is no record to name, and guessing would\n // mean either stopping nothing or signalling a stranger's daemon.\n const callerProject = resolveCallerProjectId();\n if (callerProject === undefined) {\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { running: false, stopped: false } })}\\n`,\n );\n return;\n }\n info('No Noir daemon is running for this directory (not a Noir project).', opts);\n return;\n }\n\n const rec = readProjectDaemonRecord(callerProject);\n if (!rec) {\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { running: false, stopped: false } })}\\n`,\n );\n return;\n }\n info('No Noir daemon is running.', opts);\n return;\n }\n\n const ds = spinner(`Stopping daemon (pid ${rec.pid})...`, opts).start();\n let signalled = false;\n let errMsg: string | undefined;\n // Guard PID reuse: only SIGTERM the recorded pid if it still answers as OUR\n // daemon on the recorded port. If the daemon crashed and the pid was recycled\n // by an unrelated process, a blind `process.kill(pid)` would signal an\n // innocent process — so probe `/health` first and clear the stale record if it\n // does not answer.\n const healthy = pidAlive(rec.pid) && (await isHealthy(rec.port, rec.pid, callerProject));\n if (healthy) {\n try {\n process.kill(rec.pid, 'SIGTERM');\n signalled = true;\n } catch (err) {\n // Process may have already exited between the probe and the signal.\n errMsg = err instanceof Error ? err.message : String(err);\n }\n }\n // Our own record only — unconditionally, now that it is unambiguously ours.\n clearProjectDaemonRecord(callerProject);\n\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({\n ok: true,\n data: {\n running: signalled,\n stopped: signalled,\n pid: rec.pid,\n ...(errMsg !== undefined ? { error: errMsg } : {}),\n },\n })}\\n`,\n );\n return;\n }\n if (signalled) {\n ds.succeed(`Stopped Noir daemon (pid ${rec.pid})`);\n } else {\n // Two non-signalled cases: a healthy probe whose signal failed (errMsg set),\n // or a pid that no longer answers as our daemon.\n const reason =\n errMsg !== undefined\n ? errMsg\n : 'the recorded pid did not answer as our daemon (stale record cleared)';\n ds.warn(`Daemon (pid ${rec.pid}) could not be signalled: ${reason}`);\n info(`Noir daemon (pid ${rec.pid}) could not be signalled: ${reason}`, opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon status`\n// ---------------------------------------------------------------------------\n/**\n * Report daemon liveness: read the record, confirm the pid is alive, and ping\n * `/health` for a live uptime. A missing/stale/unresponsive record maps to\n * exit 4 (DAEMON_DOWN) — the \"probed by script\" contract.\n *\n * `--json` emits `{ok:true, data:{running,pid,port,startedAt,uptimeSec,mode}}`\n * on success; failure goes through `fail()` (structured `{ok:false,error}` on\n * stdout under `--json`, plain message on stderr otherwise) with exit 4.\n */\nexport async function daemonStatus(opts: DaemonOptions): Promise<void> {\n // The record is keyed by ProjectId, so the caller's project IS the scope: a\n // daemon serving another project is a different file, invisible here (no\n // cross-project exit-4 block left to perform).\n const callerProject = resolveCallerProjectId();\n if (callerProject === undefined) {\n fail(EXIT.DAEMON_DOWN, 'Noir daemon is not running (not a Noir project).', opts);\n }\n const rec = readProjectDaemonRecord(callerProject);\n if (!rec) {\n fail(EXIT.DAEMON_DOWN, 'Noir daemon is not running (start with `noir daemon start`).', opts);\n }\n // `fail` returns `never` → TS narrows `rec` to ProjectDaemonRecord below.\n\n if (!pidAlive(rec.pid)) {\n clearProjectDaemonRecord(callerProject);\n fail(EXIT.DAEMON_DOWN, 'Noir daemon is not running (stale record removed).', opts);\n }\n\n // Live liveness probe: the daemon's HTTP server answers GET /health with\n // `{ok, pid, uptimeSec}`. A dead port / non-200 / unreachable host ⇒ stale.\n // Bounded like the other probes — a blackhole socket must not hang `noir\n // daemon status` (the \"probed by script\" exit-4 contract).\n const hs = spinner('Probing daemon health...', opts).start();\n let health: HealthBody | null = null;\n try {\n const res = await fetch(`http://127.0.0.1:${rec.port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n if (res.status === 200) {\n health = (await res.json()) as HealthBody;\n }\n } catch {\n // ECONNREFUSED / DNS / timeout — treat as not-running below.\n health = null;\n }\n // PID-reuse guard (same invariant as @noir-ai/daemon ensure.ts isHealthy +\n // this file's isHealthy): the responding /health must carry OUR recorded pid —\n // a MISSING or mismatched pid means a foreign process (or one that recycled\n // the pid) holds the port. Report stale, never \"running\" with the wrong\n // process's uptime. Distinguish \"no health body at all\" (port unresponsive)\n // from \"body present but pid missing/mismatched\" (foreign process).\n const pidOk = typeof health?.pid === 'number' && health.pid === rec.pid;\n if (health?.ok !== true || !pidOk) {\n hs.fail('Daemon not responding');\n clearProjectDaemonRecord(callerProject);\n fail(\n EXIT.DAEMON_DOWN,\n health !== null && !pidOk\n ? 'Noir daemon is not running (pid missing/mismatched — a foreign process holds the recorded port; stale record removed).'\n : 'Noir daemon is not running (port unresponsive; stale record removed).',\n opts,\n );\n }\n\n hs.succeed('Daemon healthy');\n\n // Prefer the daemon's own uptime count; fall back to the record's startedAt.\n const uptimeSec =\n typeof health?.uptimeSec === 'number'\n ? health.uptimeSec\n : Math.max(0, Math.floor((Date.now() - rec.startedAt) / 1000));\n const data = {\n running: true,\n pid: rec.pid,\n port: rec.port,\n startedAt: rec.startedAt,\n uptimeSec,\n // Honest ownership: the detached child writes `mode:'detached'`; anything\n // else (a record written before the field existed, foreground start)\n // reports 'foreground'.\n mode: rec.mode ?? MODE,\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(\n `Noir daemon: running (pid ${data.pid}, port ${data.port}, up ${formatUptime(uptimeSec)}, ${data.mode})`,\n opts,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon token`\n// ---------------------------------------------------------------------------\n/**\n * Print this project's daemon bearer token — the command a host's\n * MCP `headersHelper` runs at connect time so no secret lives in a config file.\n *\n * stdout carries the token and NOTHING else: no banner, no decoration, no\n * second line — the command's whole purpose is to be consumed by a program.\n * `--json` emits the `{ok:true,data:{token}}` envelope instead (the same data\n * channel, self-describing). Every diagnostic goes to stderr.\n *\n * Every \"no usable token\" outcome is exit 4 (DAEMON_DOWN), like `daemon status`:\n * an uninitialized project, no daemon record, a record whose daemon is gone, or\n * a record with no token file beside it. A token is a credential for a LIVE\n * daemon — printing the token of a daemon that is not running would authenticate\n * nothing while looking like success, which is exactly the silent failure the\n * `headersHelper` contract must not have.\n *\n * The token is read from disk at CALL time (`~/.noir/daemons/<projectId>.token`)\n * and never cached: the daemon regenerates it on every start, so a remembered\n * copy is a 401.\n */\nexport async function daemonToken(opts: DaemonOptions): Promise<void> {\n // Same caller-scope resolution as status/stop: the project id IS the record\n // (and token) lookup key, so a daemon serving another project is invisible\n // here rather than mis-reported.\n const callerProject = resolveCallerProjectId();\n if (callerProject === undefined) {\n fail(EXIT.DAEMON_DOWN, 'Noir daemon is not running (not a Noir project).', opts);\n }\n const rec = readProjectDaemonRecord(callerProject);\n if (!rec) {\n fail(\n EXIT.DAEMON_DOWN,\n 'Noir daemon is not running (no daemon record — start it with `noir daemon start`).',\n opts,\n );\n }\n // A record whose process is gone is stale, and liveness alone is not enough:\n // an unrelated process can recycle the recorded pid. /health must answer with\n // THIS pid (and project) — the same ownership proof `daemon status` demands —\n // so a recycled pid cannot mint a token for a daemon that is not there.\n if (!pidAlive(rec.pid) || !(await isHealthy(rec.port, rec.pid, callerProject))) {\n fail(\n EXIT.DAEMON_DOWN,\n 'Noir daemon is not running (stale record — start it with `noir daemon start`).',\n opts,\n );\n }\n const token = readDaemonToken(callerProject);\n if (token === null) {\n fail(\n EXIT.DAEMON_DOWN,\n 'Noir daemon is not running (the record has no token beside it — restart it with `noir daemon start`).',\n opts,\n );\n }\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { token } })}\\n`);\n return;\n }\n // The ONLY stdout write on the human path — a headersHelper reads exactly this.\n process.stdout.write(`${token}\\n`);\n}\n\n// ---------------------------------------------------------------------------\n// `noir daemon restart`\n// ---------------------------------------------------------------------------\n/**\n * Stop any running daemon, then start a fresh one (foreground by default,\n * detached when `--detach` is forwarded through).\n *\n * The stop sub-step is silenced (no separate envelope / message) so that under\n * `--json` a restart emits exactly ONE stdout envelope (the start result), and\n * so the human output's headline is the new daemon, not the torn-down old one.\n * Foreground `start` blocks once it brings up a daemon, so a foreground restart\n * blocks too — the honest behavior; with `--detach` the parent exits after the\n * detached child is confirmed serving.\n */\nexport async function daemonRestart(opts: DaemonOptions & { detach?: boolean }): Promise<void> {\n // Force the stop to be quiet + non-JSON: its output would either duplicate\n // the start envelope (--json) or noise up the headline (human). The verbose\n // flag is forwarded so a `--verbose` caller still sees stop diagnostics.\n await daemonStop({ ...opts, json: false, quiet: true });\n await daemonStart(opts);\n}\n","// `noir doctor`.\n//\n// Environment + project health. Runs a fixed set of checks and renders a\n// results table (human, stderr) or the versioned `{ok,data}` envelope (--json,\n// stdout). Exit 1 if any CRITICAL (fail) check is unhealthy, else 0.\n//\n// Severity model:\n// - ok — healthy.\n// - warn — degraded but usable (daemon down, onnx missing, provider key\n// missing, project not initialized). NEVER triggers exit 1.\n// - fail — CRITICAL: the product is broken on this host. Only `config` (parse\n// error), `native deps` (better-sqlite3 / sqlite-vec will not load),\n// and `store` (open fails) can fail. Exit 1 if any fail.\n//\n// Honesty rules: provider status uses `resolveModelConfig` — a PURE projection\n// of the user's config + env-var NAME presence; it makes NO live call.\n// Native-dep probes are best-effort try/catch (the store's native\n// binaries are probed via `vecAvailability`, the single cross-package surface;\n// onnxruntime-node is the context engine's own dep and is probed separately).\n//\n// Cold-start: the @noir-ai/store import (which loads better-sqlite3) is\n// DYNAMIC, inside the action, so merely running `noir status` / `noir --help`\n// never pays the native-bindings cost. @noir-ai/model is imported eagerly — its\n// provider self-registration is light (SDKs load lazily inside `complete()`).\n//\n// Stream discipline: `--json` writes `{ok:true, data:{checks,summary}}`\n// to stdout (the only stdout write). Doctor always emits `ok:true` because the\n// COMMAND succeeded in producing a diagnosis; the system-health pass/fail is\n// signaled by the EXIT CODE (0 healthy / 1 critical failure) — the same shape\n// `npm audit --json` uses (valid JSON out, non-zero exit when issues found).\n\nimport { spawnSync } from 'node:child_process';\nimport { existsSync, readdirSync, readFileSync, type Stats, statSync } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport {\n detectActiveMethod,\n type InstallMethod,\n type LoadedEnv,\n latestVersionFromCache,\n loadNoirEnv,\n loadProjectInfo,\n NOIR_VERSION,\n type ProjectInfo,\n paths,\n readInstallRecord,\n readUpdateCache,\n} from '@noir-ai/core';\nimport {\n CURRENT_SCAFFOLD_VERSION,\n isStaleSeed,\n loadTemplate,\n readScaffoldVersion,\n render,\n} from '@noir-ai/create';\nimport { pidAlive, readProjectDaemonRecord } from '@noir-ai/daemon';\nimport { resolveModelConfig } from '@noir-ai/model';\nimport { PROBE_TIMEOUT_MS } from '../daemon-client.js';\nimport {\n type CliOptions,\n EXIT,\n error as err,\n log,\n NoirCliError,\n success,\n table,\n warn,\n} from '../output.js';\nimport { type BadgeState, badge } from '../theme.js';\n\n/** Options accepted by `doctor`: the global flags + the opt-in `--dedup`. */\nexport interface DoctorOptions extends CliOptions {\n /** `--dedup`: scan host-context + `.noir/` docs for semantic near-duplicates.\n * Opt-in (loads the local embedder) so a default `noir doctor` stays fast. */\n dedup?: boolean;\n}\n\ntype Severity = 'ok' | 'warn' | 'fail';\n\n/** One row of the doctor report (table cell + `--json` element). */\nexport interface CheckResult {\n name: string;\n status: Severity;\n detail: string;\n}\n\n/** The `data` payload of `noir doctor --json`. */\nexport interface DoctorPayload {\n noir: string;\n checks: CheckResult[];\n summary: { ok: number; warn: number; fail: number };\n /** Scaffold-version drift. `onDisk` is null when the stamp is\n * absent (uninitialized project, or one created before the stamp existed);\n * `drift` is true only when\n * a stamp is present AND differs from the engine's current version. */\n scaffold: { onDisk: string | null; current: string; drift: boolean };\n /** RULES.md budget measurement. `null` when the project isn't\n * initialized OR `.noir/rules/RULES.md` is absent — in either case there\n * is nothing to measure and the check row stays informational (warn skip\n * or ok \"no RULES.md\"). Never drives `fail` — over-budget is a `warn`. */\n rules: {\n onDisk: { bytes: number; lines: number };\n budget: { kb: number; maxLines: number };\n over: boolean;\n } | null;\n /** Host report. `null` when the project isn't initialized (no resolved\n * host); otherwise carries the active `host` + the list of repo-relative\n * primary artifact paths the doctor verified (AGENTS.md for agents-md/\n * cursor/opencode; the host's native context file when distinct from\n * AGENTS.md (claude → CLAUDE.md, gemini → GEMINI.md); the host's MCP\n * config). The check row is `ok` when\n * all present, `warn` (NEVER `fail`) when any are missing — re-running\n * `noir sync` restores them. */\n host: { active: HostId; expected: string[]; missing: string[] } | null;\n /** Publish-readiness report (repo-developer-facing, advisory). `null`\n * when doctor is NOT running from a monorepo checkout (a global install has\n * no workspace package.json set to validate → the row reports `ok`\n * \"skipped\"). Otherwise carries the count of workspace `package.json` files\n * validated + the list of advisory issues found (bad semver, missing\n * `files`, cli missing `bin`, …). The row severity is `warn` when any issues\n * are present, `ok` otherwise — NEVER `fail` (a missing field does not break\n * the local install). */\n publish: { checked: number; issues: string[] } | null;\n /** Install-method report. `method` from ~/.noir/install.json (fallback\n * unknown); `latestKnown` from the update cache (never a live call). */\n install: {\n method: InstallMethod;\n installedVersion: string | null;\n latestKnown: string | null;\n } | null;\n}\n\n// ---------------------------------------------------------------------------\n// Small helpers\n// ---------------------------------------------------------------------------\n\n/** Best-effort probe of the `onnxruntime-node` native binding. */\nasync function probeOnnx(): Promise<{ ok: boolean; reason: string }> {\n try {\n // Variable specifier (not a string literal) so tsc does NOT try to resolve\n // a module the CLI package does not directly depend on — onnxruntime-node\n // is the context engine's dep and resolves through ITS node_modules at\n // runtime. Best-effort: hosts that hoist it resolve; otherwise not-loadable.\n const spec = 'onnxruntime-node';\n await import(spec);\n return { ok: true, reason: 'loadable' };\n } catch (e) {\n return { ok: false, reason: e instanceof Error ? e.message : String(e) };\n }\n}\n\n/** Human label for the onnx probe outcome (hides confusing module-not-found noise). */\nfunction describeOnnx(o: { ok: boolean; reason: string }): string {\n if (o.ok) return 'loadable';\n if (/Cannot find (module|package)/i.test(o.reason)) {\n return 'not resolvable from CLI probe (best-effort)';\n }\n const r = o.reason.length > 60 ? `${o.reason.slice(0, 59)}…` : o.reason;\n return `probe failed: ${r}`;\n}\n\nfunction fmtUptime(sec: number): string {\n if (!Number.isFinite(sec) || sec < 0) return 'unknown';\n if (sec < 60) return `${sec}s`;\n if (sec < 3600) return `${Math.floor(sec / 60)}m ${sec % 60}s`;\n return `${Math.floor(sec / 3600)}h ${Math.floor((sec % 3600) / 60)}m`;\n}\n\nfunction msg(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n\n// ---------------------------------------------------------------------------\n// Checks. Each appends to `checks`; project-dependent ones short-circuit to a\n// `warn` \"skipped\" row when the project isn't initialized (so the table still\n// accounts for them, but a pre-init `noir doctor` never exit-1s on them).\n// ---------------------------------------------------------------------------\n\nasync function checkRuntime(checks: CheckResult[]): Promise<void> {\n checks.push({\n name: 'runtime',\n status: 'ok',\n detail: `noir ${NOIR_VERSION} · node ${process.version} · ${process.platform}`,\n });\n}\n\nfunction checkConfig(root: string): { project: ProjectInfo | undefined; result: CheckResult } {\n const configPath = paths.config(root);\n if (!existsSync(configPath)) {\n return {\n project: undefined,\n result: {\n name: 'config',\n status: 'warn',\n detail: `no .noir/config.yml in ${root} — run \\`noir init\\``,\n },\n };\n }\n try {\n const project = loadProjectInfo(root);\n return {\n project,\n result: {\n name: 'config',\n status: 'ok',\n detail: `host=${project.config.host} · mode=${project.config.mode}`,\n },\n };\n } catch (e) {\n return {\n project: undefined,\n result: { name: 'config', status: 'fail', detail: `config parse error: ${msg(e)}` },\n };\n }\n}\n\nasync function checkDaemon(checks: CheckResult[], project: ProjectInfo | undefined): Promise<void> {\n // The record is per-project; without a project id there is no record to look\n // up, so report honestly rather than reading (and implicitly claiming) a\n // daemon that belongs to some unknown project.\n if (project === undefined) {\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: 'skipped — project not initialized',\n });\n return;\n }\n const rec = readProjectDaemonRecord(project.id);\n if (!rec) {\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: 'not running (start with `noir daemon start`)',\n });\n return;\n }\n if (!pidAlive(rec.pid)) {\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: `stale record (pid ${rec.pid} not alive)`,\n });\n return;\n }\n try {\n // Bounded probe — a stale record pointing at a blackhole socket (TCP\n // accepted, HTTP never answered) must not hang `noir doctor` for minutes.\n const res = await fetch(`http://127.0.0.1:${rec.port}/health`, {\n signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),\n });\n const body =\n res.status === 200\n ? ((await res.json()) as { ok?: boolean; pid?: number; uptimeSec?: number })\n : null;\n // PID-reuse guard (mirrors daemonStatus): require the responding /health to\n // carry OUR recorded pid — a foreign process serving /health is not our daemon.\n const pidOk = typeof body?.pid === 'number' && body.pid === rec.pid;\n if (body && body.ok === true && pidOk) {\n checks.push({\n name: 'daemon',\n status: 'ok',\n detail: `running (pid ${rec.pid}, port ${rec.port}, up ${fmtUptime(body.uptimeSec ?? 0)})`,\n });\n return;\n }\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: `record present but /health unreachable on port ${rec.port}`,\n });\n } catch {\n checks.push({\n name: 'daemon',\n status: 'warn',\n detail: `record present but /health unreachable on port ${rec.port}`,\n });\n }\n}\n\nasync function checkNativeDeps(checks: CheckResult[]): Promise<{ vecOk: boolean }> {\n // vecAvailability probes better-sqlite3 + sqlite-vec together (the store opens\n // a Database and loads sqlite-vec). Dynamic import keeps the native bindings\n // out of cold-start for every other command.\n const { vecAvailability } = await import('@noir-ai/store');\n const vec = vecAvailability();\n const onnx = await probeOnnx();\n const vecOk = vec.ok === true;\n const onnxOk = onnx.ok === true;\n // The sqlite side is CRITICAL (the store cannot open without it). onnx only\n // backs local embeddings, so its absence is a warning (search → BM25-only).\n const status: Severity = vecOk ? (onnxOk ? 'ok' : 'warn') : 'fail';\n checks.push({\n name: 'native deps',\n status,\n detail: `better-sqlite3+sqlite-vec: ${vecOk ? 'loadable' : vec.ok === false ? vec.reason : 'unknown'} · onnxruntime-node: ${describeOnnx(onnx)}`,\n });\n return { vecOk };\n}\n\nasync function checkStore(\n checks: CheckResult[],\n project: ProjectInfo | undefined,\n root: string,\n): Promise<void> {\n if (!project) {\n checks.push({ name: 'store', status: 'warn', detail: 'skipped — not initialized' });\n return;\n }\n const dbPath = paths.storeDb(root, project.id);\n if (!existsSync(dbPath)) {\n // Fresh project: the store DB is created LAZILY when the daemon first runs\n // (or when content is indexed) — `noir init`/`create` does not pre-create it.\n // Its absence on an otherwise-initialized project is EXPECTED, not a failure:\n // surfacing it as `fail` would alarm every new user on their first `noir doctor`.\n // Warn (never exit-1) with the action that materializes the store.\n checks.push({\n name: 'store',\n status: 'warn',\n detail: `not created yet — the store DB is created when the daemon first runs (\\`noir daemon start\\` or index content); expected at ${dbPath}`,\n });\n return;\n }\n const { openStore } = await import('@noir-ai/store');\n let store: { close(): Promise<void> | void } | undefined;\n try {\n store = await openStore({ projectId: project.id, root, readonly: true });\n checks.push({\n name: 'store',\n status: 'ok',\n detail: `opens readonly (db: ${paths.storeDb(root, project.id)})`,\n });\n } catch (e) {\n checks.push({ name: 'store', status: 'fail', detail: `open failed: ${msg(e)}` });\n } finally {\n if (store) {\n try {\n await Promise.resolve(store.close());\n } catch {\n /* a close error must not mask the open result */\n }\n }\n }\n}\n\n/**\n * The tracked-file refusal line emitted by `loadNoirEnv`. Matched by\n * its stable PREFIX: @noir-ai/core owns the wording, and this warning is the one\n * signal the loader returns that a `.noir/.env` was refused outright rather than\n * loaded (a refused file yields `overlay: {}` + `sources: {}` + this one line).\n */\nconst ENV_REFUSAL_PREFIX = '.noir/.env: refusing to load';\n\n/**\n * `.noir/.env` rows (names only, never values).\n *\n * Three concerns, in the order the user should read them:\n *\n * 1. REFUSAL — a git-tracked file contributes NOTHING. Reporting\n * only its permissions here would tell the user everything is fine while\n * every key in it is being ignored, which is precisely the \"I edited\n * `.noir/.env` and nothing changed\" confusion this check exists to kill.\n * The permissions advisory is skipped for a refused file, mirroring the\n * loader, which refuses before it even parses (so it emits no other\n * diagnostic either).\n * 2. PERMISSION advisory — a group/world-readable file can leak tokens\n * (ssh/aws-credentials convention). Warn, never fail.\n * 3. PROVENANCE — one `ok` row per key the file DEFINES, naming the side that\n * won. The file is the user's own short list, so this is not an\n * enumeration of the environment (see `noir env` for the curated view);\n * the value is never read, only the key name.\n */\nfunction checkNoirEnv(checks: CheckResult[], root: string, loaded: LoadedEnv): void {\n const path = join(paths.noirDir(root), '.env');\n let st: Stats;\n try {\n st = statSync(path);\n } catch {\n return; // no .env file — nothing to check\n }\n if (loaded.warnings.some((w) => w.startsWith(ENV_REFUSAL_PREFIX))) {\n checks.push({\n name: 'noir-env',\n status: 'warn',\n detail: `${path} is tracked by git — refused, so NONE of its keys are in effect. Fix: add \\`.noir/.env\\` to .gitignore and run \\`git rm --cached .noir/.env\\`.`,\n });\n return;\n }\n const mode = st.mode & 0o777;\n const tooOpen = (st.mode & 0o077) !== 0;\n checks.push(\n tooOpen\n ? {\n name: 'noir-env',\n status: 'warn',\n detail: `${path} is ${mode.toString(8)} — readable by others; run chmod 600`,\n }\n : { name: 'noir-env', status: 'ok', detail: `${path} permissions ok (${mode.toString(8)})` },\n );\n // Provenance rows. Named `noir-env:<KEY>` so they group under the permission\n // row in the table and stay uniquely addressable in the `--json` payload.\n for (const [key, source] of Object.entries(loaded.sources)) {\n if (source !== 'file') continue;\n checks.push({ name: `noir-env:${key}`, status: 'ok', detail: 'from .noir/.env' });\n }\n}\n\nfunction checkEmbedder(\n checks: CheckResult[],\n project: ProjectInfo | undefined,\n vecOk: boolean,\n): void {\n if (!project) {\n checks.push({ name: 'embedder', status: 'warn', detail: 'skipped — not initialized' });\n return;\n }\n const emb = project.config.context.embedder;\n const kind = emb.kind;\n const model = typeof emb.model === 'string' && emb.model.length > 0 ? emb.model : '<unset>';\n if (kind === 'none') {\n checks.push({\n name: 'embedder',\n status: 'ok',\n detail: `kind=none (BM25-only) · dim=${emb.dim}`,\n });\n return;\n }\n if (!vecOk) {\n // vec native layer is the embedder's hard prereq (vecs write into vec0).\n checks.push({\n name: 'embedder',\n status: 'warn',\n detail: `${kind}/${model} (${emb.dim}-dim) configured but vec native layer unavailable — search degrades to BM25`,\n });\n return;\n }\n checks.push({\n name: 'embedder',\n status: 'ok',\n detail: `${kind}/${model} (${emb.dim}-dim) · vec backend available`,\n });\n}\n\nfunction checkProvider(\n checks: CheckResult[],\n project: ProjectInfo | undefined,\n sources: LoadedEnv['sources'],\n): void {\n if (!project) {\n checks.push({ name: 'provider', status: 'warn', detail: 'skipped — not initialized' });\n return;\n }\n // PURE projection — resolveModelConfig reads config + env-var NAMES only; it\n // NEVER makes a live call and NEVER infers a provider from env-var presence.\n const resolved = resolveModelConfig(project.config.model);\n const names = Object.keys(resolved.providers);\n if (names.length === 0) {\n checks.push({\n name: 'provider',\n status: 'ok',\n detail: 'none configured (offline mode; drafting degrades to templates)',\n });\n return;\n }\n const parts: string[] = [];\n let missing = false;\n for (const name of names) {\n const p = resolved.providers[name];\n if (!p) continue;\n // Provenance: when the winning source for the key is\n // `.noir/.env`, say so — \"key present\" alone leaves the user staring at an\n // empty shell export wondering where the key came from. The NAME only; the\n // value is never read here, let alone printed.\n const fromFile =\n (p.apiKeyEnv !== undefined && sources[p.apiKeyEnv] === 'file') ||\n (p.authTokenEnv !== undefined && sources[p.authTokenEnv] === 'file');\n // A keyed provider names its credential via `apiKeyEnv` and/or\n // `authTokenEnv`; name whichever is present so a token-only provider is not\n // mislabeled \"anonymous\" when its token is missing.\n const credentialName = p.apiKeyEnv ?? p.authTokenEnv;\n const key = p.hasKey\n ? fromFile\n ? 'key present (from .noir/.env)'\n : 'key present'\n : credentialName\n ? `missing ${credentialName}`\n : 'anonymous';\n if (!p.hasKey && credentialName) missing = true;\n parts.push(`${name}/${p.model ?? '?'} (${key})`);\n }\n // Provider readiness is never CRITICAL — the model layer degrades to `null`\n // (templates) when a key is missing, so this is an ok/warn signal only.\n checks.push({\n name: 'provider',\n status: missing ? 'warn' : 'ok',\n detail: parts.join(' · '),\n });\n}\n\n// ---------------------------------------------------------------------------\n// Doctor install row (advisory; ok/warn only, never fail, cache-only).\n// ---------------------------------------------------------------------------\n\nexport interface InstallCheckOutcome {\n name: 'install';\n status: 'ok' | 'warn';\n detail: string;\n method: InstallMethod;\n installedVersion: string | null;\n latestKnown: string | null;\n}\n\n/** Pure: build the doctor `install` row. NEVER `fail` — an install method issue\n * is advisory, not a broken host. Reads the cache only; no network. */\nexport function buildInstallCheck(opts: {\n method: InstallMethod;\n version: string | null;\n latestKnown: string | null;\n}): InstallCheckOutcome {\n const { method, version, latestKnown } = opts;\n const parts: string[] = [`method=${method}`];\n if (version) parts.push(`v${version}`);\n const advisory: string[] = [];\n if (method !== 'native') advisory.push('native recommended');\n if (latestKnown && version && latestKnown !== version) advisory.push('update available');\n const status: 'ok' | 'warn' = advisory.length > 0 ? 'warn' : 'ok';\n if (advisory.length > 0) parts.push(advisory.join(' + '));\n return {\n name: 'install',\n status,\n detail: parts.join(' · '),\n method,\n installedVersion: version,\n latestKnown,\n };\n}\n\nfunction checkScaffoldVersion(\n checks: CheckResult[],\n root: string,\n): {\n onDisk: string | null;\n current: string;\n drift: boolean;\n} {\n // readScaffoldVersion never throws — it returns null on an absent/malformed\n // stamp, so doctor keeps reporting even on a partially-initialized project.\n const onDisk = readScaffoldVersion(root);\n const current = CURRENT_SCAFFOLD_VERSION;\n const drift = onDisk !== null && onDisk !== current;\n // Drift is NEVER critical: a stale scaffold still works (the manifest entries\n // are forward-compatible), it just means `noir init --upgrade` will run\n // pending migrations. Absent stamp on an uninitialized project → warn (parity\n // with the other project-dependent checks that skip-with-warn pre-init).\n const status: Severity = onDisk === null || drift ? 'warn' : 'ok';\n const detail =\n onDisk === null\n ? 'no .noir/scaffold-version stamp (run `noir init`)'\n : drift\n ? `on-disk ${onDisk} ≠ current ${current} (run \\`noir init --upgrade\\`)`\n : `${onDisk} (up to date)`;\n checks.push({ name: 'scaffold version', status, detail });\n return { onDisk, current, drift };\n}\n\n/**\n * Doc-seed drift. `.noir/.env.example` is a committable documentation seed the\n * scaffold writes once; a later release may change the text it ships. When the\n * bytes on disk are still an exact copy of a seed an OLDER Noir recorded (the\n * user never edited the file), doctor surfaces it the same way it surfaces a\n * stale scaffold stamp: a `warn` row naming the file and the command that\n * refreshes it. Absent, current, or user-edited files produce no row — the\n * comparison is exact, so even a whitespace change is treated as the user's.\n */\nfunction checkSeedDrift(checks: CheckResult[], root: string): void {\n const seedPath = join(root, '.noir', '.env.example');\n let bytes: string;\n try {\n bytes = readFileSync(seedPath, 'utf8');\n } catch {\n return; // absent — nothing to compare\n }\n // Render the current template exactly as the scaffold would. The env.example\n // seed carries no `{{...}}` placeholder, so the rendered text is the raw\n // template bytes and the empty context is byte-identical to a real render.\n const currentRender = render(loadTemplate('env.example.tmpl'), {});\n if (isStaleSeed('envExample', bytes, currentRender)) {\n checks.push({\n name: 'doc seed',\n status: 'warn',\n detail: `.noir/.env.example is an unedited copy of an older Noir — run \\`noir init --upgrade\\` to refresh it`,\n });\n }\n}\n\n/**\n * Soft RULES.md budget cap. Mirrors the Failure-Backed Rules doctrine:\n * an over-budget RULES.md tends to accumulate stale / speculative clauses, so\n * doctor warns (NEVER fails — purely a hygiene nudge) when the file exceeds\n * the configured `lengthBudgetKb` (default 6 KB) OR a 150-line ceiling. The\n * check is honest about the three \"nothing to measure\" cases:\n *\n * • project not initialized → skip-warn (parity with store/embedder/provider)\n * • RULES.md absent → ok informational (no file = no budget problem)\n * • RULES.md unreadable → warn (treat like a malformed-stamp: surface it)\n *\n * Returns the structured measurement for the `--json` envelope's `data.rules`,\n * or `null` when there is nothing to measure (absent / skipped).\n */\nfunction checkRulesMdBudget(\n checks: CheckResult[],\n root: string,\n project: ProjectInfo | undefined,\n): {\n onDisk: { bytes: number; lines: number };\n budget: { kb: number; maxLines: number };\n over: boolean;\n} | null {\n if (!project) {\n checks.push({ name: 'rules budget', status: 'warn', detail: 'skipped — not initialized' });\n return null;\n }\n const rulesPath = paths.rulesMd(root);\n if (!existsSync(rulesPath)) {\n // No RULES.md is fine — most projects don't have one yet. Informational ok\n // so a green run stays green without forcing a file creation.\n checks.push({\n name: 'rules budget',\n status: 'ok',\n detail: 'no .noir/rules/RULES.md',\n });\n return null;\n }\n // Read + measure. A read/decode failure surfaces as a warn (parallel to the\n // malformed-stamp handling in checkScaffoldVersion): the doctor keeps\n // reporting rather than crashing, and the user sees a clear hint.\n let content: string;\n try {\n content = readFileSync(rulesPath, 'utf8');\n } catch (e) {\n checks.push({\n name: 'rules budget',\n status: 'warn',\n detail: `RULES.md unreadable: ${msg(e)}`,\n });\n return null;\n }\n // bytes = UTF-8 file size on disk (the natural unit for a KB budget). lines\n // uses wc-style counting: count of '\\n' chars, +1 if the last line has no\n // trailing newline; 0 only for an empty file.\n const bytes = Buffer.byteLength(content, 'utf8');\n const lineCount =\n content.length === 0 ? 0 : content.split('\\n').length - (content.endsWith('\\n') ? 1 : 0);\n const lengthBudgetKb = project.config.rules.lengthBudgetKb;\n const budgetBytes = lengthBudgetKb * 1024;\n const maxLines = 150;\n const overBytes = bytes > budgetBytes;\n const overLines = lineCount > maxLines;\n const over = overBytes || overLines;\n const status: Severity = over ? 'warn' : 'ok';\n const kb = (n: number): string => (n / 1024).toFixed(1);\n const detail = over\n ? `OVER: ${kb(bytes)} KB / ${lengthBudgetKb} KB · ${lineCount}/${maxLines} lines — trim RULES.md (failure-backed clauses only) or raise rules.lengthBudgetKb`\n : `${kb(bytes)} KB / ${lengthBudgetKb} KB · ${lineCount}/${maxLines} lines (within budget)`;\n checks.push({ name: 'rules budget', status, detail });\n return {\n onDisk: { bytes, lines: lineCount },\n budget: { kb: lengthBudgetKb, maxLines },\n over,\n };\n}\n\nfunction summarize(checks: CheckResult[]): DoctorPayload['summary'] {\n let ok = 0;\n let warnN = 0;\n let fail = 0;\n for (const c of checks) {\n if (c.status === 'ok') ok++;\n else if (c.status === 'warn') warnN++;\n else fail++;\n }\n return { ok, warn: warnN, fail };\n}\n\n/**\n * Host-artifacts presence check. Reports the ACTIVE host (read from\n * `project.config.host`) + verifies the host's primary emission paths exist on\n * disk. The expected set mirrors `buildHostArtifacts` in @noir-ai/create:\n *\n * - AGENTS.md for the hosts that emit it (agents-md/cursor/opencode — their\n * native context surface IS AGENTS.md). claude/gemini do NOT emit AGENTS.md\n * (their CLAUDE.md/GEMINI.md @-import the canonical .noir/ sources; a root\n * AGENTS.md would double-import them, so neither emits it).\n * - The host's native context file when distinct from AGENTS.md\n * (claude → CLAUDE.md; gemini → GEMINI.md; agents-md/cursor/opencode →\n * none, AGENTS.md IS the context). Cursor's working-rules ride AGENTS.md's\n * `@.noir/rules/RULES.md` import — NO separate `.cursor/rules/noir-contract.mdc`\n * host-rules pointer (it was `noir-`-prefixed, so the cursor flat-skill\n * prune deleted it on every sync).\n * - The host's MCP config (.mcp.json / .gemini/mcp.json / .cursor/mcp.json /\n * opencode.json).\n *\n * Severity is `ok` when every expected path exists, `warn` when any are\n * missing (NEVER `fail` — a missing host artifact is restored by `noir sync`,\n * not a critical product failure). Mirrors the policy of the other\n * presence/projection checks (scaffold-version drift, RULES.md budget): warn,\n * surface the remediation, never block.\n *\n * Returns the structured payload for the `--json` envelope's `data.host`, or\n * `null` when the project isn't initialized (no resolved host).\n */\nfunction checkHostArtifacts(\n checks: CheckResult[],\n root: string,\n project: ProjectInfo | undefined,\n): { active: HostId; expected: string[]; missing: string[] } | null {\n if (!project) {\n checks.push({ name: 'host artifacts', status: 'warn', detail: 'skipped — not initialized' });\n return null;\n }\n const host = project.config.host;\n const adapter = resolveAdapter(host);\n const ectx = { root };\n\n // Build the expected repo-relative path list. Mirrors buildHostArtifacts in\n // @noir-ai/create so a missing row maps 1:1 to a manifest entry the user can\n // restore with `noir sync`. Kept inline (not imported) so doctor stays a\n // READ-ONLY health probe with zero write-side coupling.\n const expected: string[] = [];\n // AGENTS.md — only for hosts whose emitContext IS the AGENTS.md (claude/\n // gemini skip it — their native context file already covers .noir/).\n const emitsAgentsMd = host === 'agents-md' || host === 'cursor' || host === 'opencode';\n if (emitsAgentsMd) {\n expected.push(relOr(adapter.agentsMdPath?.(ectx) ?? 'AGENTS.md', root));\n }\n // Host-native context file (when distinct from AGENTS.md).\n if (host === 'claude') expected.push('CLAUDE.md');\n else if (host === 'gemini') expected.push('GEMINI.md');\n // Cursor has NO separate host-rules .mdc — the prior `noir-contract.mdc`\n // pointer was REMOVED (it was `noir-`-prefixed, so the cursor flat-skill\n // prune in emitSkillsToDir deleted it on every noir init/create/sync).\n // Cursor's rules ride AGENTS.md's `@.noir/rules/RULES.md` import instead.\n // Host MCP config (fallback .mcp.json for claude/agents-md).\n expected.push(relOr(adapter.mcpConfigPath?.(ectx) ?? '.mcp.json', root));\n\n const missing = expected.filter((p) => !existsSync(joinRoot(root, p)));\n const status: Severity = missing.length === 0 ? 'ok' : 'warn';\n const detail =\n missing.length === 0\n ? `host=${host} · all ${expected.length} primary artifacts present`\n : `host=${host} · missing ${missing.join(', ')} (run \\`noir sync\\` to restore)`;\n checks.push({ name: 'host artifacts', status, detail });\n return { active: host, expected, missing };\n}\n\n// ---------------------------------------------------------------------------\n// Nested-`.noir` detection (read-only).\n// ---------------------------------------------------------------------------\n\n/**\n * Detects the fingerprint of a `noir init`/`create` run from INSIDE `.noir/`\n * (now PREVENTED by `assertSafeRoot` in @noir-ai/create, but legacy/already-\n * nested projects still carry the damage): a nested `<root>/.noir/.noir/` store\n * and/or host artifacts emitted into `.noir/` as if it were a project root\n * (`.noir/CLAUDE.md`, `.noir/.mcp.json`, `.noir/.claude/`, and the ignore files\n * `.noir/.gitignore` / `.dockerignore` / `.npmignore` / `.prettierignore`).\n * Read-only `warn` —\n * doctor never mutates; remediation is manual removal (a follow-up\n * change could automate it). Never `fail`: a nested store wastes space + confuses\n * tooling but does not break the outer project.\n */\nexport function checkNestedNoir(\n checks: CheckResult[],\n root: string,\n): { detected: boolean; paths: string[] } {\n const candidates = [\n '.noir/.noir',\n '.noir/CLAUDE.md',\n '.noir/.mcp.json',\n '.noir/.claude',\n '.noir/.gitignore',\n '.noir/.dockerignore',\n '.noir/.npmignore',\n '.noir/.prettierignore',\n ];\n const found = candidates.filter((rel) => existsSync(join(root, rel)));\n const detected = found.length > 0;\n checks.push({\n name: 'nested .noir',\n status: detected ? 'warn' : 'ok',\n detail: detected\n ? `nested Noir artifacts inside .noir/ (${found.join(', ')}) — likely from running \\`noir init\\` inside .noir/. Remove them; the real project is at ${root}.`\n : 'no nested .noir/ artifacts',\n });\n return { detected, paths: found };\n}\n\n// ---------------------------------------------------------------------------\n// Deferred — semantic duplicate detection (`--dedup`; loads the embedder).\n// ---------------------------------------------------------------------------\n\n/** Local embedder shape (@noir-ai/context's `EmbedFn`). */\ntype EmbedLike = (text: string) => Promise<Float32Array>;\n\n/**\n * Collect dedup candidates: the host context files that exist at the root\n * (CLAUDE.md / AGENTS.md / GEMINI.md — the hand-mirrored-overlap case) plus\n * `.noir/rules/RULES.md`. `.noir/NOIR.md` is EXCLUDED because host context\n * files `@import` it by design (their similarity is intentional, not a dup).\n */\nexport function collectDedupCandidates(root: string): { path: string; text: string }[] {\n const rels = ['CLAUDE.md', 'AGENTS.md', 'GEMINI.md', '.noir/rules/RULES.md'];\n const out: { path: string; text: string }[] = [];\n for (const rel of rels) {\n const abs = join(root, rel);\n if (!existsSync(abs)) continue;\n let text: string;\n try {\n text = readFileSync(abs, 'utf8');\n } catch {\n continue;\n }\n if (text.trim().length === 0) continue;\n out.push({ path: rel, text });\n }\n return out;\n}\n\n/**\n * `noir doctor --dedup`: embed the host-context + RULES.md candidates and\n * report near-duplicate pairs (cosine ≥ 0.90). The ONLY mechanism that catches\n * cross-file SEMANTIC overlap (e.g. a hand-mirrored CLAUDE.md ≈ AGENTS.md) —\n * exact content-hash cannot. Opt-in so a default `noir doctor` never pays the\n * embedder-load cost; degrades to a `warn` skip when the embedder is\n * unavailable (kind=none / native layer missing). `opts.embed` is a testability\n * seam — tests inject a deterministic fake; production omits it and the local\n * embedder is lazy-loaded via `@noir-ai/context`.\n */\nexport async function checkSemanticDupDoctor(\n checks: CheckResult[],\n root: string,\n project: ProjectInfo | undefined,\n opts: { embed?: EmbedLike } = {},\n): Promise<void> {\n if (!project) {\n checks.push({ name: 'semantic dup', status: 'warn', detail: 'skipped — not initialized' });\n return;\n }\n const candidates = collectDedupCandidates(root);\n if (candidates.length < 2) {\n checks.push({\n name: 'semantic dup',\n status: 'ok',\n detail: `${candidates.length} candidate file(s); nothing to compare`,\n });\n return;\n }\n const ctx = await import('@noir-ai/context');\n let embed: EmbedLike | undefined = opts.embed;\n if (!embed) {\n const cfg = ctx.resolveEmbedderConfig(project.config.context);\n if (cfg.kind === 'none') {\n checks.push({\n name: 'semantic dup',\n status: 'warn',\n detail: 'embedder kind=none — set context.embedder.kind=local to enable semantic dedup',\n });\n return;\n }\n try {\n embed = ctx.createEmbedFn(cfg).embed as EmbedLike;\n } catch (e) {\n checks.push({\n name: 'semantic dup',\n status: 'warn',\n detail: `embedder unavailable — skipped (${msg(e)})`,\n });\n return;\n }\n }\n if (!embed) return;\n const pairs = await ctx.findSemanticDuplicates(candidates, embed, ctx.DEFAULT_DUP_THRESHOLD);\n checks.push({\n name: 'semantic dup',\n status: pairs.length > 0 ? 'warn' : 'ok',\n detail:\n pairs.length === 0\n ? `no near-duplicates across ${candidates.length} file(s)`\n : `${pairs.length} near-duplicate pair${pairs.length === 1 ? '' : 's'}: ${pairs\n .map((p) => `${p.a}≈${p.b} (${p.similarity.toFixed(2)})`)\n .join('; ')}`,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Publish-readiness check (advisory, repo-developer-facing).\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the workspace `packages/` directory from the CLI's own location —\n * walk up from `import.meta.url` to the nearest `package.json` named\n * `@noir-ai/cli`, then return its PARENT dir (the `packages/` root) IF that\n * dir's parent carries the monorepo marker (`pnpm-workspace.yaml`). Returns\n * `null` when not running from a monorepo checkout (a global `npm install -g`\n * has no `packages/*` workspace to validate → the publish check skips).\n *\n * The walk is robust across layouts: source (`packages/cli/src/commands/`),\n * built (`packages/cli/dist/`), and bundled-chunk (`packages/cli/dist/`) all\n * walk up to `packages/cli/package.json`. In a global install the same walk\n * lands on `node_modules/@noir-ai/cli/package.json`, whose parent\n * (`node_modules/@noir-ai/`) has NO `pnpm-workspace.yaml` parent → null.\n */\nfunction resolveWorkspacePackagesDir(): string | null {\n let here = dirname(fileURLToPath(import.meta.url));\n // Bound the walk so a pathological layout never loops the filesystem.\n for (let i = 0; i < 8; i++) {\n const pkgPath = join(here, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { name?: string };\n if (pkg.name === '@noir-ai/cli') {\n // `here` is the cli package dir; `dirname(here)` is `packages/`.\n const packagesDir = dirname(here);\n // Monorepo gate: the repo root (`dirname(packagesDir)`) carries\n // `pnpm-workspace.yaml`. A global install fails this gate.\n if (existsSync(join(dirname(packagesDir), 'pnpm-workspace.yaml'))) {\n return packagesDir;\n }\n return null;\n }\n } catch {\n // Malformed package.json — keep walking.\n }\n }\n const parent = dirname(here);\n if (parent === here) break; // filesystem root\n here = parent;\n }\n return null;\n}\n\n/**\n * Permissive semver-ish regex for the advisory check. Accepts release and\n * pre-release/build forms (`1.0.0`, `1.1.0-beta.1`, `1.2.0+build.4`). Strict\n * enough to catch the obvious foot-guns (a git SHA, a literal \"latest\", a\n * missing patch) without pulling in the `semver` package for a warn-level\n * check. `npm publish` itself enforces rigor at pack time.\n */\nconst SEMVER_ISH = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/;\n\n/** Best-effort `npm pack --dry-run` on the cli package — read-only (it does\n * NOT write the tarball). Returns `{ fileCount, distIncluded }` parsed from\n * npm's notice output, or `{ error }` when npm is unavailable / the pack\n * fails. Gated on npm being on PATH (the spec's expensive-but-optional leg):\n * we probe `npm --version` first and skip-with-note if it isn't resolvable. */\nfunction npmPackDryRun(\n cliPkgDir: string,\n): { fileCount: number | null; distIncluded: boolean | null } | { error: string } {\n // Probe npm availability first (cheap). shell:true so Windows finds npm.cmd.\n const probe = spawnSync('npm', ['--version'], { encoding: 'utf8', shell: true });\n if (probe.status !== 0 || probe.error) {\n return { error: probe.error ? probe.error.message : 'npm not on PATH' };\n }\n // `npm pack --dry-run` lists the tarball contents without writing the file.\n // It IS read-only (npm's own docs: \"--dry-run ... does not write anything\").\n const res = spawnSync('npm', ['pack', '--dry-run'], {\n cwd: cliPkgDir,\n encoding: 'utf8',\n shell: true,\n });\n if (res.status !== 0 || res.error) {\n return { error: res.error ? res.error.message : `npm pack exited ${res.status ?? '?'}` };\n }\n const out = `${res.stdout ?? ''}\\n${res.stderr ?? ''}`;\n // `npm notice total files: 42` — the canonical summary line.\n const totalMatch = out.match(/total files:\\s*(\\d+)/i);\n const fileCount = totalMatch?.[1] ? Number.parseInt(totalMatch[1], 10) : null;\n // `dist/` appears in the tarball contents (`npm notice <size> dist/<file>`).\n const distIncluded = /\\bdist\\//.test(out);\n return { fileCount, distIncluded };\n}\n\n/**\n * Publish-readiness check. For every workspace `package.json` under\n * `packagesDir`, validate publishability:\n * - `name` starts with `@noir-ai/`\n * - `version` matches {@link SEMVER_ISH}\n * - `files` is a non-empty array\n * - the cli package has a `bin` field\n *\n * Optionally runs `npm pack --dry-run` on the cli package (best-effort, gated\n * on npm being available) to report the file count + confirm `dist/` ships.\n *\n * Severity is ALWAYS `ok` or `warn` — NEVER `fail`: a missing/odd package.json\n * field does not break the local install (this is an advisory nudge to repo\n * developers, not a project-facing health check). The check runs ALWAYS (it\n * produces a row in every doctor report) but returns `null` from\n * `data.publish` when not running from a monorepo (`packagesDir === null`).\n */\nexport function checkPublish(\n checks: CheckResult[],\n packagesDir: string | null,\n opts: { skipNpmPack?: boolean } = {},\n): { checked: number; issues: string[] } | null {\n if (packagesDir === null) {\n checks.push({\n name: 'publish',\n status: 'ok',\n detail: 'skipped — not a monorepo checkout (no packages/* workspace)',\n });\n return null;\n }\n // Discover workspace package.json files (depth-1 dirs only — no glob dep).\n let entries: string[] = [];\n try {\n entries = readdirSync(packagesDir, { withFileTypes: true })\n .filter((d) => d.isDirectory())\n .map((d) => d.name);\n } catch (e) {\n checks.push({\n name: 'publish',\n status: 'warn',\n detail: `could not read ${packagesDir}: ${msg(e)}`,\n });\n return { checked: 0, issues: [`unreadable packages dir: ${msg(e)}`] };\n }\n\n const issues: string[] = [];\n let checked = 0;\n let cliDir: string | null = null;\n for (const name of entries) {\n const pkgPath = join(packagesDir, name, 'package.json');\n if (!existsSync(pkgPath)) continue;\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as Record<string, unknown>;\n } catch (e) {\n issues.push(`${name}/package.json: unparseable (${msg(e)})`);\n checked++;\n continue;\n }\n checked++;\n const pkgName = typeof pkg.name === 'string' ? pkg.name : '';\n if (!pkgName.startsWith('@noir-ai/')) {\n issues.push(`${name}/package.json: name '${pkgName}' is not @noir-ai/*`);\n }\n const version = typeof pkg.version === 'string' ? pkg.version : '';\n if (!SEMVER_ISH.test(version)) {\n issues.push(`${name}/package.json: version '${version}' is not semver`);\n }\n if (!Array.isArray(pkg.files) || pkg.files.length === 0) {\n issues.push(`${name}/package.json: files missing or empty`);\n }\n if (pkgName === '@noir-ai/cli') {\n if (pkg.bin == null || (typeof pkg.bin === 'object' && Object.keys(pkg.bin).length === 0)) {\n issues.push(`${name}/package.json: bin missing (cli package must declare a bin)`);\n }\n cliDir = join(packagesDir, name);\n }\n }\n\n // Best-effort npm pack --dry-run on the cli package. Gated on npm being\n // available; a failure here is surfaced in the detail but does NOT add an\n // issue (it is environmental, not a package.json defect). `skipNpmPack` is a\n // testability seam — synthetic test fixtures have no real `dist/`, so the\n // dist-absent signal would otherwise pollute the package.json-validation\n // assertions. Production (`doctor()`) leaves it unset.\n let packNote = '';\n if (cliDir && opts.skipNpmPack !== true) {\n const pack = npmPackDryRun(cliDir);\n if ('error' in pack) {\n packNote = ` · npm pack skipped (${pack.error})`;\n } else {\n const count = pack.fileCount == null ? '?' : String(pack.fileCount);\n const dist = pack.distIncluded ? 'dist/ included' : 'dist/ NOT in tarball';\n packNote = ` · npm pack: ${count} files, ${dist}`;\n if (pack.distIncluded === false) {\n issues.push('cli npm pack --dry-run: dist/ absent from the tarball');\n }\n }\n }\n\n const status: Severity = issues.length > 0 ? 'warn' : 'ok';\n const detail =\n issues.length === 0\n ? `${checked}/${entries.length} packages publish-ready${packNote}`\n : `${issues.length} issue${issues.length === 1 ? '' : 's'} across ${checked} package${checked === 1 ? '' : 's'}${packNote}`;\n checks.push({ name: 'publish', status, detail });\n return { checked, issues };\n}\n\n/** Repo-relative POSIX form of `abs` under `root`. Defensive: rejects\n * paths that escape `root` — mirrors `hostRel` in @noir-ai/create/manifest.ts.\n * A future adapter that returns a stray path fails loudly here instead of\n * producing a misleading \"missing\" row for a path doctor would never probe.\n * Relative literals (e.g. `'AGENTS.md'`, `'.mcp.json'`) pass through unchanged\n * AFTER the `..`-escape check so a buggy literal could not exfiltrate either. */\nfunction relOr(abs: string, root: string): string {\n const posix = abs.replace(/\\\\/g, '/');\n if (posix.startsWith('/')) {\n const rel = relative(root, abs).replace(/\\\\/g, '/');\n if (rel.length === 0 || rel.startsWith('..') || rel.startsWith('/')) {\n throw new Error(`doctor: host path '${abs}' is not under root '${root}'`);\n }\n return rel;\n }\n // Already-relative literal: still reject `..` segments (a `../etc/passwd`\n // literal from a buggy adapter must not silently pass through).\n if (posix.split('/').some((seg) => seg === '..')) {\n throw new Error(`doctor: host path '${abs}' escapes root '${root}'`);\n }\n return posix;\n}\n\n/** Join `root` with a repo-relative POSIX path for an existsSync probe. */\nfunction joinRoot(root: string, relPath: string): string {\n return `${root}/${relPath}`;\n}\n\nfunction severityBadge(s: Severity): string {\n // Doctor's vocabulary is ok/warn/fail; the badge palette is ok/warn/error/info.\n // `fail` is the CRITICAL state → red (`error`), so RED stays reserved for the\n // broken-host states. The TEXT label keeps doctor's own uppercase word so the\n // JSON envelope (`status: 'fail'`) and the human row agree on vocabulary.\n const state: BadgeState = s === 'fail' ? 'error' : s;\n return badge(state, s.toUpperCase());\n}\n\nfunction renderHuman(payload: DoctorPayload, opts: CliOptions): void {\n log(\n `noir doctor — ${payload.checks.length} check${payload.checks.length === 1 ? '' : 's'}`,\n opts,\n );\n table(\n payload.checks.map((c) => ({\n Check: c.name,\n Status: severityBadge(c.status),\n Detail: c.detail,\n })),\n ['Check', 'Status', 'Detail'],\n opts,\n );\n const { ok, warn: warnN, fail } = payload.summary;\n if (fail > 0) {\n err(\n `${fail} critical check${fail === 1 ? '' : 's'} failed (${warnN} warning${warnN === 1 ? '' : 's'}, ${ok} ok)`,\n opts,\n );\n } else if (warnN > 0) {\n warn(`all critical checks passed (${warnN} warning${warnN === 1 ? '' : 's'}, ${ok} ok)`, opts);\n } else {\n success(`all ${ok} check${ok === 1 ? '' : 's'} passed`, opts);\n }\n}\n\n/**\n * `noir doctor`: run all checks, render, and exit.\n *\n * Under `--json` writes `{ok:true, data: DoctorPayload}` to stdout (always —\n * the command produced a diagnosis). The exit code carries health: 0 when no\n * check failed, 1 when any CRITICAL (`fail`) check is unhealthy.\n */\nexport async function doctor(opts: DoctorOptions = {}): Promise<void> {\n const root = process.cwd();\n const checks: CheckResult[] = [];\n\n await checkRuntime(checks);\n const { project, result: configResult } = checkConfig(root);\n checks.push(configResult);\n await checkDaemon(checks, project);\n const { vecOk } = await checkNativeDeps(checks);\n await checkStore(checks, project, root);\n checkEmbedder(checks, project, vecOk);\n // `.noir/.env` is read ONCE and shared by the provider + noir-env rows — both\n // need \"which side won for this key\". Names only: the loader never opens a\n // value into anything doctor prints.\n const noirEnv = loadNoirEnv(root);\n checkProvider(checks, project, noirEnv.sources);\n checkNoirEnv(checks, root, noirEnv);\n const scaffold = checkScaffoldVersion(checks, root);\n checkSeedDrift(checks, root);\n const rules = checkRulesMdBudget(checks, root, project);\n const host = checkHostArtifacts(checks, root, project);\n checkNestedNoir(checks, root);\n if (opts.dedup === true) {\n await checkSemanticDupDoctor(checks, root, project);\n }\n const publish = checkPublish(checks, resolveWorkspacePackagesDir());\n\n // Install-method check (advisory, cache-only, never fail).\n const installCheck = buildInstallCheck({\n method: detectActiveMethod(),\n version: readInstallRecord()?.version ?? null,\n latestKnown: latestVersionFromCache(readUpdateCache(), 'latest'),\n });\n checks.push(installCheck);\n\n const summary = summarize(checks);\n const payload: DoctorPayload = {\n noir: NOIR_VERSION,\n checks,\n summary,\n scaffold,\n rules,\n host,\n publish,\n install: {\n method: installCheck.method,\n installedVersion: installCheck.installedVersion,\n latestKnown: installCheck.latestKnown,\n },\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: payload })}\\n`);\n } else {\n renderHuman(payload, opts);\n }\n\n if (summary.fail > 0) {\n // Under --json the data envelope is already on stdout; throw with an empty\n // message so handleError sets exitCode=1 without a redundant stderr line.\n // Under human mode the summary line above already named the failures.\n throw new NoirCliError(\n EXIT.ERROR,\n opts.json === true ? '' : `noir doctor: ${summary.fail} critical check(s) failed`,\n );\n }\n}\n","// `noir env [--json]` — which configuration is in effect, and where each value\n// comes from.\n//\n// The command exists to answer the one question the maintainer hit live: \"I\n// edited `.noir/.env` and nothing changed — so which value is actually in\n// effect, and who supplied it?\". The resolution rule itself is that\n// a key `.noir/.env` DEFINES wins and the real environment is the fallback for\n// the keys the file omits; this command renders that decision per key instead of\n// leaving the user to infer it.\n//\n// HARD RULE: NAMES AND SHAPES ONLY, NEVER A VALUE. Each row carries\n// the key, the side that won, and a REDACTED shape (`pk_…(42)` = the first three\n// characters + the length) so two tokens can be told apart without a secret ever\n// being printed. `--json` carries even less — `valueLength` only; the shape\n// belongs to the human table and never reaches stdout (see `env`, whose payload\n// is an EXPLICIT projection of the resolved rows).\n//\n// CURATION, NOT A DUMP. `loadNoirEnv().sources` records every key\n// that resolves to something, including every ambient one as `'env'` — under the\n// default argument that is the whole of `process.env` (PATH, npm_config_*, …).\n// Rendering that inventory would bury the answer this command exists to give, so\n// the report is exactly:\n//\n// (a) every key `.noir/.env` defines — the user's own short list, and the\n// thing they just edited; plus\n// (b) ambient keys named by {@link CURATED_AMBIENT_KEYS} — the Noir-relevant\n// names a user may have exported from a shell rc; plus\n// (c) ambient keys named by the project's OWN config\n// (`model.providers.<name>.apiKeyEnv`), read through the shipped\n// `resolveModelConfig` mapper so the CLI never re-derives the schema.\n//\n// READ-ONLY: nothing here writes, spawns, or opens the store.\n\nimport { loadNoirEnv, loadProjectInfo } from '@noir-ai/core';\nimport { resolveModelConfig } from '@noir-ai/model';\nimport { type CliOptions, info, json, table } from '../output.js';\n\n/**\n * Ambient (environment-only) names worth reporting: the Noir-relevant keys a\n * user might reasonably have exported from a shell rc. Deliberately a SHORT\n * allowlist — see the curation note above. `CLICKUP_API_TOKEN` is the\n * integration token, the four `*_API_KEY` names are the remote embedder /\n * provider keys the docs name, `OLLAMA_BASE_URL` backs a local Ollama embedder,\n * and `NOIR_PROFILE` selects a `run.profiles` entry.\n *\n * `ANTHROPIC_AUTH_TOKEN`, `ANTHROPIC_BASE_URL` and `API_TIMEOUT_MS` sit next to\n * `ANTHROPIC_API_KEY` because together they are the gateway set: a user who\n * points a run at a non-Anthropic endpoint configures exactly these, and\n * \"which side supplied my gateway value — the file or my shell?\" is the\n * question they came here to answer.\n *\n * An ambient key NOT in this list is still resolved (and still feeds the\n * precedence decision) — it is simply not reported, because \"every variable in\n * your shell\" is not an answer to \"which value is in effect\".\n */\nexport const CURATED_AMBIENT_KEYS: readonly string[] = [\n 'CLICKUP_API_TOKEN',\n 'OPENAI_API_KEY',\n 'VOYAGE_API_KEY',\n 'COHERE_API_KEY',\n 'OLLAMA_BASE_URL',\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_AUTH_TOKEN',\n 'ANTHROPIC_BASE_URL',\n 'API_TIMEOUT_MS',\n 'NOIR_PROFILE',\n];\n\n/**\n * The ambient environment as it was BEFORE `.noir/.env` was applied, captured by\n * the bin's `preAction` (see {@link captureAmbientEnv}).\n *\n * The loader APPLIES its overlay to `process.env` in place, and after that the\n * file's own values are indistinguishable from an inherited export for exactly\n * the keys where the question matters — so \"is my shell value being overridden?\"\n * (the `shadowed` column) can only be answered from a pre-overlay snapshot.\n *\n * A SHALLOW COPY of the environment, values included: the shadow comparison\n * needs the ambient VALUE to tell an override apart from an agreeing export.\n * The copy is in-memory and process-local for the lifetime of one invocation —\n * it is never persisted, never written back to `process.env`, and a value from\n * it reaches the user only as a redacted shape or a length (see\n * {@link redactShape} / {@link EnvVarRow.valueLength}).\n */\nlet ambientBaseline: Record<string, string | undefined> | undefined;\n\n/**\n * Snapshot the ambient environment before the bin applies `.noir/.env`. Called\n * from `bin.ts`'s `preAction` for `noir env` only — every other command just\n * applies the overlay and never needs the baseline.\n */\nexport function captureAmbientEnv(env: Record<string, string | undefined> = process.env): void {\n ambientBaseline = { ...env };\n}\n\n/** One resolved key: the name, the side that won, and a length — never a value. */\nexport interface EnvVarRow {\n /** The env-var NAME (safe to print; this is what the user types). */\n key: string;\n /** The side that won: `.noir/.env` (`'file'`) or the ambient environment. */\n source: 'file' | 'env';\n /** Present (true) only when `.noir/.env` defines the key AND the ambient\n * environment defined it with a DIFFERENT value — i.e. the file is\n * overriding something. Omitted otherwise (the JSON shape is additive). */\n shadowed?: boolean;\n /** Character count of the value in effect. A length is not a value. */\n valueLength: number;\n}\n\n/** The `data` of `noir env --json`. No value, and no fragment of one. */\nexport interface EnvPayload {\n vars: EnvVarRow[];\n /** Loader diagnostics (malformed lines, shadowed keys, a tracked-file\n * refusal). Names only — the loader never puts a value in a warning. */\n warnings: string[];\n}\n\n/** Resolved rows + the REDACTED display shapes for the human table. */\ninterface ResolvedEnv {\n vars: EnvVarRow[];\n warnings: string[];\n /** key → redacted shape (first three characters + length). Human-only: it is\n * never serialized (see the explicit projection in `env`). */\n shapes: Record<string, string>;\n}\n\n/**\n * A redacted DISPLAY shape for a value: the first three characters plus the\n * length (`pk_la…(42)`). Enough to tell two tokens apart in a table — the shape\n * a provider dashboard shows for a masked key — and never enough to use one.\n * The length is deliberate: it is what makes \"the file's value is in effect, not\n * the one I exported\" visible without printing either.\n *\n * SHORT VALUES ARE SHOWN AS LENGTH ONLY. For a value of `SHORT_VALUE_MAX` chars\n * or fewer the prefix would BE the whole value (`dev…(3)` for `NOIR_PROFILE=dev`\n * prints a complete value — and a short one is exactly the kind a user might\n * have picked, or a legacy token might be). The floor is 8 rather than the 3\n * the prefix needs, so a short-but-real value like a hostname or a base URL\n * cannot be reconstructed by guessing the last few characters.\n */\nconst SHORT_VALUE_MAX = 8;\n\nfunction redactShape(value: string): string {\n if (value.length <= SHORT_VALUE_MAX) return `…(${value.length})`;\n return `${value.slice(0, 3)}…(${value.length})`;\n}\n\n/**\n * The env-var NAMES the project's own model config requests\n * (`model.providers.<name>.apiKeyEnv`), read through the shipped\n * `resolveModelConfig` mapper rather than by reaching into the config shape —\n * so the CLI never re-derives the schema, and a config that names a key the\n * built-in allowlist would never guess still shows up.\n *\n * An uninitialized (or unparseable) project yields `[]`: `noir env` reports what\n * it can from the file + the environment instead of refusing to run — the file\n * is exactly what the user came here to inspect.\n */\nfunction configApiKeyEnvNames(root: string): string[] {\n try {\n const project = loadProjectInfo(root);\n const names = new Set<string>();\n for (const provider of Object.values(resolveModelConfig(project.config.model).providers)) {\n if (provider.apiKeyEnv !== undefined && provider.apiKeyEnv.length > 0) {\n names.add(provider.apiKeyEnv);\n }\n }\n return [...names];\n } catch {\n return [];\n }\n}\n\n/**\n * Resolve the reported keys: provenance from `loadNoirEnv` (the loader is the\n * single decision point — this command never re-implements the precedence),\n * filtered down to the curated report described at the top of this file.\n *\n * The resolved VALUE is used only for its length and its redacted shape, inside\n * the loop body below — it is never stored on a row, so it cannot leak into the\n * `--json` envelope.\n */\nfunction resolveEnv(root: string, ambientEnv: Record<string, string | undefined>): ResolvedEnv {\n const { overlay, warnings, sources } = loadNoirEnv(root, ambientEnv);\n const curated = new Set<string>(CURATED_AMBIENT_KEYS);\n for (const name of configApiKeyEnvNames(root)) curated.add(name);\n\n // Candidate keys: everything `sources` records (the loader's authority), plus\n // the curated names that are SET in the ambient environment. The second half\n // exists because `sources` is empty whenever the loader takes one of its two\n // no-op paths — no `.noir/.env` at all, or a git-tracked file refused outright\n // — and neither means \"nothing is in effect\": with no file\n // contributing, an exported `CLICKUP_API_TOKEN` IS the value in effect, and\n // reporting \"nothing to report\" while it sits in the user's shell would be a\n // lie. It cannot contradict the loader: those are exactly the keys the loader\n // would have recorded as `'env'` had it run its ambient pass.\n const candidates = new Set<string>(Object.keys(sources));\n for (const key of curated) {\n if (ambientEnv[key] !== undefined) candidates.add(key);\n }\n\n const vars: EnvVarRow[] = [];\n const shapes: Record<string, string> = {};\n // Sorted for a stable table (and a stable `--json` array) across runs.\n for (const key of [...candidates].sort()) {\n const source = sources[key] ?? 'env';\n // (b)/(c): an ambient key is reported ONLY when it is curated. `sources`\n // holds every ambient name, so this filter is what keeps the command from\n // enumerating the environment.\n if (source === 'env' && !curated.has(key)) continue;\n // The value in effect: the file's overlay when the file defined the key\n // (the file WINS), else the fallback the file fell through to.\n const value = source === 'file' ? (overlay[key] ?? '') : (ambientEnv[key] ?? '');\n // Shadowing is the same condition the loader warns on: the file defines a\n // key the ambient environment ALSO defines, with a different value. Equal\n // values are not a shadow — nothing the user exported is being overridden.\n const shadowed =\n source === 'file' && ambientEnv[key] !== undefined && ambientEnv[key] !== value;\n\n vars.push({\n key,\n source,\n ...(shadowed ? { shadowed: true } : {}),\n valueLength: value.length,\n });\n shapes[key] = redactShape(value);\n }\n return { vars, warnings, shapes };\n}\n\n/** Human label for the SOURCE column. */\nfunction describeSource(row: EnvVarRow): string {\n if (row.source !== 'file') return 'environment';\n return row.shadowed === true ? '.noir/.env (shadows environment)' : '.noir/.env';\n}\n\n/**\n * `noir env [--json]`: report every key that is in effect, its winning source,\n * and a redacted shape of its value.\n *\n * `--json` writes `{ok:true, data:{vars, warnings}}` to STDOUT — the explicit\n * projection below is the redaction boundary (`shapes` is deliberately dropped).\n * The human path renders a KEY/SOURCE/VALUE table to STDERR and writes nothing\n * to stdout.\n *\n * Loader warnings are NOT re-printed here: `bin.ts`'s `preAction` already\n * applies `.noir/.env` through `applyNoirEnv`, which writes every warning\n * (including the tracked-file refusal) to stderr before this action runs — so\n * they are visible in every real invocation, and printing them again would only\n * duplicate them. They are carried in the `--json` payload for consumers.\n *\n * Always exits 0: this is a report, and \"your file is not being loaded\" is\n * information, not a failure.\n */\nexport async function env(opts: CliOptions = {}): Promise<void> {\n const report = resolveEnv(process.cwd(), ambientBaseline ?? process.env);\n\n if (opts.json === true) {\n json({ ok: true, data: { vars: report.vars, warnings: report.warnings } });\n return;\n }\n\n if (report.vars.length === 0) {\n // \"no keys IN EFFECT\", not \"no keys\": a file can define keys that resolve to\n // nothing — a git-tracked file is refused outright, and a\n // process-injection key (NODE_OPTIONS, npm_*) is skipped by the loader. Both\n // are reported by the loader's own stderr warnings above this line.\n info(\n 'nothing to report — .noir/.env defines no keys in effect and no Noir-relevant environment variable is set',\n opts,\n );\n return;\n }\n\n table(\n report.vars.map((row) => ({\n KEY: row.key,\n SOURCE: describeSource(row),\n VALUE: report.shapes[row.key] ?? '',\n })),\n ['KEY', 'SOURCE', 'VALUE'],\n opts,\n );\n}\n","import { chmodSync, mkdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n atomicWriteFile,\n type DetectResult,\n detectActiveMethod,\n detectInstallMethods,\n ensureShimExecutable,\n loadProjectInfo,\n MANAGED_NODE_VERSION,\n NOIR_VERSION,\n noirHome,\n type ProvisionedNode,\n type ProvisionOptions,\n provisionManagedNode,\n readInstallRecord,\n runManagerCmd,\n writeInstallRecord,\n} from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail, info, spinner, success, warn } from '../output.js';\n\nexport interface InstallOptions extends CliOptions {\n list?: boolean;\n uninstallPrev?: boolean;\n spec?: string;\n /** Dismiss the migration banner for the current CLI version (persists in\n * install.json's `dismissedVersions`). Typically passed with `--list`. */\n dismiss?: boolean;\n}\n\nexport interface MigrationPlan {\n detected: DetectResult[];\n currentMethod: string;\n targetSpec: string;\n installedVersion: string | null;\n shouldMigrate: boolean;\n isDowngrade: boolean;\n /**\n * True when the resolved target is a concrete version below the configured\n * `update.minVersion` floor. Channel targets\n * (`latest`/`beta`) resolve to the newest at install time and never trip it.\n * Enforced in {@link install} with the same warn/refuse pattern as the\n * downgrade guard.\n */\n belowMinVersion: boolean;\n nativeVersion: string;\n prevUninstallCmd: string | null;\n autoUninstall: boolean; // always false unless --uninstall-prev\n}\n\n/** Per-segment numeric semver comparison. Returns true when `a < b`. */\nfunction semverLt(a: string, b: string): boolean {\n // Strip a leading `v` (npm-idiomatic exact-version spelling that\n // installManagedNode explicitly supports) — bare Number('v1.9.0') is NaN → 0.\n const seg = (s: string): number => Number(s.replace(/^v/, '')) || 0;\n const pa = a.split('.').map(seg);\n const pb = b.split('.').map(seg);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x < y;\n }\n return false;\n}\n\n/** Pure: build the migration plan from detection + target. No I/O. */\nexport function buildMigrationPlan(opts: {\n detected: DetectResult[];\n currentMethod: string;\n targetSpec: string;\n installedVersion: string | null;\n /**\n * The `update.minVersion` floor from config (default `'1.6.0'`). When\n * omitted, the config default applies. See the floor check in {@link install}.\n */\n minVersion?: string;\n}): MigrationPlan {\n const detected = opts.detected;\n const shouldMigrate = detected.length > 0 && opts.currentMethod !== 'native';\n const nativeVersion = opts.targetSpec;\n let isDowngrade = false;\n if (opts.installedVersion && nativeVersion !== 'latest' && nativeVersion !== 'beta') {\n isDowngrade = semverLt(nativeVersion, opts.installedVersion);\n }\n // minVersion floor: only a concrete version below the floor trips it;\n // 'latest'/'beta' resolve to the newest at install time (always >= floor).\n const floor = opts.minVersion ?? '1.6.0';\n const belowMinVersion =\n nativeVersion !== 'latest' && nativeVersion !== 'beta' && semverLt(nativeVersion, floor);\n return {\n detected,\n currentMethod: opts.currentMethod,\n targetSpec: opts.targetSpec,\n installedVersion: opts.installedVersion,\n shouldMigrate,\n isDowngrade,\n belowMinVersion,\n nativeVersion,\n prevUninstallCmd: detected[0]?.uninstallCmd ?? null,\n autoUninstall: false,\n };\n}\n\nexport interface InstallManagedNodeOptions {\n channel?: string;\n version?: string;\n env: NodeJS.ProcessEnv;\n /**\n * Forwarded into {@link provisionManagedNode}. Carries the offline mock\n * seams (`fetch`, `exec`, `target`, `signal`) so the install pipeline is\n * unit-testable without network — same pattern as `provisionManagedNode`'s\n * own suite in `packages/core/test/node-provision.test.ts`.\n */\n provision?: Pick<ProvisionOptions, 'fetch' | 'exec' | 'target' | 'signal'>;\n}\n\n/** Normalized record of which runtime backed an install. */\nexport interface InstallManagedNodeResult {\n ok: boolean;\n version: string | null;\n /** What runtime backed this install: `'managed'` (downloaded into ~/.noir)\n * or `'system'` (used the system-Node fallback). */\n runtimeSource?: ProvisionedNode['source'];\n error?: string;\n}\n\n/**\n * Provision the managed Node runtime and install `@noir-ai/cli` into an\n * isolated `~/.noir/cli` prefix, shimmed from `~/.noir/bin/noir`.\n *\n * Replaces the prior \"not provisioned\" fail branch: instead of requiring the\n * user to pre-run `install.sh`/`install.ps1`, this now delegates to\n * {@link provisionManagedNode} (download + verify + extract, fail-closed), then\n * installs the CLI with the provisioned npm. On a system-Node fallback the\n * fallback node/npm paths are used and a warning is emitted; the install\n * record's `managedRuntimeVersion` reflects which path was taken so `noir\n * doctor` can report it accurately.\n */\nexport async function installManagedNode(\n opts: InstallManagedNodeOptions,\n): Promise<InstallManagedNodeResult> {\n const spec = opts.version ?? opts.channel ?? 'latest';\n const home = noirHome();\n const cliDir = join(home, 'cli');\n const binDir = join(home, 'bin');\n mkdirSync(cliDir, { recursive: true });\n mkdirSync(binDir, { recursive: true });\n\n // Provision the managed Node runtime. Idempotent — re-runs are no-ops.\n // On any failure, provisionManagedNode either falls back to a system Node\n // >=22 (returns { source: 'system' }) or throws — we surface the throw as an\n // {ok:false} envelope rather than letting it kill the CLI.\n let runtime: ProvisionedNode;\n try {\n runtime = await provisionManagedNode({\n ...(opts.provision ?? {}),\n env: opts.env,\n });\n } catch (err) {\n const reason = err instanceof Error ? err.message : String(err);\n return { ok: false, version: null, error: `managed-Node provision failed: ${reason}` };\n }\n if (runtime.source === 'system') {\n warn(\n `managed-Node download failed; using system Node ${runtime.version} at ${runtime.nodeBin}.`,\n );\n }\n\n const { nodeBin, npmBin } = runtime;\n\n // Install @noir-ai/cli into the isolated prefix using the provisioned npm.\n const { code, stderr } = await runManagerCmd(\n [npmBin, 'install', '-g', `@noir-ai/cli@${spec}`, `--prefix=${cliDir}`],\n { env: opts.env, timeoutMs: 120_000 },\n );\n if (code !== 0) {\n return { ok: false, version: null, error: `npm install failed: ${stderr.slice(0, 300)}` };\n }\n\n // Shim: ~/.noir/bin/noir -> provisioned node + isolated prefix.\n const shim = join(binDir, 'noir');\n const shimBody = `#!/usr/bin/env bash\\n\"${nodeBin}\" \"${join(cliDir, 'lib', 'node_modules', '@noir-ai', 'cli', 'dist', 'bin.js')}\" \"$@\"\\n`;\n atomicWriteFile(shim, shimBody);\n chmodSync(shim, 0o755); // must be executable — atomicWriteFile sets 0o644 by default\n ensureShimExecutable(); // defense-in-depth: re-assert 0o755 (idempotent, never throws)\n // (POSIX shim; Windows uses a .cmd wrapper — install.sh/install.ps1.)\n\n // Resolve installed version via the provisioned node.\n const ver = await runManagerCmd(\n [\n nodeBin,\n join(cliDir, 'lib', 'node_modules', '@noir-ai', 'cli', 'dist', 'bin.js'),\n '--version',\n ],\n { env: opts.env, timeoutMs: 30_000 },\n );\n const version = ver.code === 0 ? ver.stdout.trim() : null;\n writeInstallRecord({\n method: 'native',\n version: version ?? '0.0.0',\n channel: opts.channel ?? 'latest',\n installedAt: new Date().toISOString(),\n // Reflect which runtime backs this install: the pinned managed version\n // (e.g. '22.23.2') for managed, or 'system-<v>' for the fallback so doctor\n // can distinguish a fallback install from a real managed one.\n managedRuntimeVersion:\n runtime.source === 'managed' ? MANAGED_NODE_VERSION : `system-${runtime.version}`,\n });\n return { ok: true, version, runtimeSource: runtime.source };\n}\n\nexport async function install(opts: InstallOptions = {}): Promise<void> {\n // --dismiss: append the current CLI version to install.json's\n // `dismissedVersions` so the migration banner stops showing for this\n // version. Idempotent (no duplicate entries). Typically combined with\n // `--list`; works standalone too. Requires an existing install record —\n // if there's none, there's nothing to dismiss (no banner was shown).\n // Track the dismiss outcome so `--dismiss --list --json` folds it into the\n // SINGLE list envelope — never two JSON documents on stdout.\n let dismissOutcome: { dismissed: boolean; version: string | null } | undefined;\n if (opts.dismiss === true) {\n const rec = readInstallRecord();\n if (!rec) {\n dismissOutcome = { dismissed: false, version: null };\n if (!opts.json) info('No install record found; nothing to dismiss.');\n } else {\n const dismissed = new Set(rec.dismissedVersions ?? []);\n if (!dismissed.has(NOIR_VERSION)) dismissed.add(NOIR_VERSION);\n writeInstallRecord({ ...rec, dismissedVersions: [...dismissed] });\n dismissOutcome = { dismissed: true, version: NOIR_VERSION };\n if (!opts.json) success(`Migration banner dismissed for ${NOIR_VERSION}.`);\n }\n }\n\n // --dismiss alone (no --list): emit the single result now.\n if (opts.dismiss === true && opts.list !== true) {\n if (opts.json) process.stdout.write(`${JSON.stringify({ ok: true, data: dismissOutcome })}\\n`);\n return;\n }\n\n if (opts.list === true) {\n const detected = await detectInstallMethods(process.env);\n if (opts.json) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { ...(dismissOutcome ?? {}), detected } })}\\n`,\n );\n return;\n }\n info('Detected installs:');\n for (const d of detected) info(` ${d.method} (${d.version ?? 'unknown'})`);\n return;\n }\n\n const currentMethod = detectActiveMethod();\n const detected = await detectInstallMethods(process.env);\n const installedRecord = readInstallRecord();\n // Read the `update.minVersion` floor from the project config (falls\n // back to the config default '1.6.0' when the project isn't initialized or\n // the block is absent). Same try/catch pattern as home.ts's update-config read.\n let minVersion = '1.6.0';\n try {\n minVersion = loadProjectInfo(process.cwd()).config.update.minVersion;\n } catch {\n // Not initialized yet — the config default applies.\n }\n const plan = buildMigrationPlan({\n detected,\n currentMethod,\n targetSpec: opts.spec ?? 'latest',\n installedVersion: installedRecord?.version ?? null,\n minVersion,\n });\n\n // Refuse to install a version below the configured minVersion floor, and\n // refuse a downgrade, UNCONDITIONALLY. The floor/downgrade-guard exist as\n // safety invariants; gating them on `--no-input` meant the common interactive\n // path (and `--json`, which does not set input:false) silently proceeded with\n // only a stderr warning — the invariant was never actually enforced.\n if (plan.belowMinVersion) {\n fail(\n EXIT.USAGE,\n `refusing install of ${plan.nativeVersion} (below minVersion ${minVersion})`,\n opts,\n );\n }\n\n if (plan.isDowngrade) {\n fail(\n EXIT.USAGE,\n `refusing downgrade to ${plan.nativeVersion} (installed ${plan.installedVersion})`,\n opts,\n );\n }\n\n const installSpin = spinner('Installing Noir via native installer...', opts).start();\n const result = await installManagedNode({\n channel: plan.nativeVersion === 'beta' ? 'beta' : undefined,\n version: plan.nativeVersion.startsWith('v')\n ? plan.nativeVersion\n : plan.nativeVersion === 'latest' || plan.nativeVersion === 'beta'\n ? undefined\n : plan.nativeVersion,\n env: process.env,\n });\n if (!result.ok) {\n installSpin.fail('Native install failed');\n fail(EXIT.ERROR, result.error ?? 'native install failed', opts);\n }\n installSpin.succeed(`Installed noir ${result.version}`);\n\n // Migration step runs FIRST, in BOTH modes — the --json early-return below\n // must not skip it (a scripted/CI migration relying on --uninstall-prev would\n // otherwise silently leave the stale install in place while reporting success).\n // Its diagnostics go to stderr (warn/success), which --json leaves untouched.\n if (plan.prevUninstallCmd && opts.uninstallPrev !== true) {\n warn(`To finish the migration, uninstall the previous install: ${plan.prevUninstallCmd}`);\n warn(\n ' Or re-run with --uninstall-prev to do it now. Rollback: reinstall via the previous manager.',\n );\n } else if (plan.prevUninstallCmd && opts.uninstallPrev === true) {\n const { code } = await runManagerCmd(plan.prevUninstallCmd.split(' '), { env: process.env });\n if (code === 0) success('Previous install removed.');\n else warn('Previous install NOT removed (non-zero exit). You can remove it manually.');\n }\n\n // Under --json: emit the canonical {ok:true,data} success envelope on stdout.\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { version: result.version, runtimeSource: result.runtimeSource } })}\\n`,\n );\n return;\n }\n\n info(`Installed native: ${result.version}.`);\n // Claude Code #41806/#27910 mitigation: the old npm bin may still resolve\n // first in $PATH after migration (the shell hashes command paths, and a\n // stale global install can shadow ~/.noir/bin). Hint the user to refresh.\n info('If `noir` still points at the old install, run: hash -r && which -a noir');\n success(`noir ${result.version} installed via native. Run \\`noir doctor\\` to verify.`);\n}\n","import {\n detectActiveMethod,\n ensureShimExecutable,\n fetchLatestVersion,\n loadProjectInfo,\n readInstallRecord,\n readUpdateCache,\n runManagerCmd,\n shouldCheckForUpdate,\n type UpdateConfigLike,\n writeUpdateCache,\n} from '@noir-ai/core';\nimport { type CliOptions, EXIT, fail, info, spinner, success, warn } from '../output.js';\nimport { installManagedNode } from './install.js';\n\nexport const DEFAULT_UPDATE_CONFIG: UpdateConfigLike = {\n checkEnabled: true,\n checkIntervalHours: 24,\n channel: 'latest',\n minVersion: '1.6.0',\n display: 'notice',\n};\n\nexport interface UpdateOptions extends CliOptions {\n check?: boolean;\n spec?: string;\n}\n\nexport interface UpdateTarget {\n method: string;\n targetSpec: string;\n currentVersion: string | null;\n latestKnown: string | null;\n isUpgrade: boolean;\n /**\n * True when the latest-known version the registry returned is below the\n * configured `update.minVersion` floor (e.g. a beta/prerelease channel lag,\n * or a yanked dist-tag). Enforced in {@link update} with the same\n * warn/refuse pattern as the downgrade guard.\n */\n belowMinVersion: boolean;\n}\n\nexport function buildUpdateTarget(opts: {\n method: string;\n channel: string;\n spec?: string;\n currentVersion: string | null;\n latestKnown: string | null;\n /**\n * The `update.minVersion` floor from config (default `'1.6.0'`). When\n * omitted, the config default applies. See the floor check in {@link update}.\n */\n minVersion?: string;\n}): UpdateTarget {\n const targetSpec = opts.spec ?? opts.channel;\n // The guard target: a concrete `spec` (an exact version) WINS over the\n // fetched registry version, because the downgrade and minVersion guards must\n // evaluate what we will ACTUALLY install — not what the registry happens to\n // offer. A pinned `spec 1.9.0` against a 1.12.0 registry is a DOWNGRADE,\n // and a pinned `spec 1.5.0` can trip the minVersion floor; both were\n // previously missed because the guards only ever looked at `latestKnown`.\n const concrete =\n opts.spec != null && opts.spec !== 'latest' && opts.spec !== 'beta'\n ? opts.spec\n : opts.latestKnown;\n // Semver downgrade guard (hardening): only treat the target as an upgrade\n // when it is STRICTLY NEWER than the current one. The prior inequality check\n // (`latestKnown !== currentVersion`) would treat a registry that returned an\n // OLDER version (e.g. beta/prerelease channel lag) as an \"upgrade\" and\n // silently downgrade the install.\n const isUpgrade =\n concrete != null && opts.currentVersion != null && semverGt(concrete, opts.currentVersion);\n // minVersion floor: refuse a target version below the floor. null\n // latestKnown is handled separately (registry-unreachable branch).\n const floor = opts.minVersion ?? '1.6.0';\n const belowMinVersion = concrete != null && semverLt(concrete, floor);\n return {\n method: opts.method,\n targetSpec,\n currentVersion: opts.currentVersion,\n latestKnown: opts.latestKnown,\n isUpgrade,\n belowMinVersion,\n };\n}\n\n/**\n * Per-segment numeric semver comparison (major.minor.patch). Returns true when\n * `a` is strictly greater than `b`. Pre-release suffixes are ignored — Noir\n * versioning is `MAJOR.MINOR.PATCH` only, and the registry's dist-tag is the\n * source of truth for \"latest\". Non-numeric segments coerce to 0. Used here\n * instead of a full semver library to keep the CLI's dependency surface\n * unchanged (the same helper lives in install.ts for the downgrade guard).\n */\nfunction semverGt(a: string, b: string): boolean {\n // Strip a leading `v` (npm-idiomatic) — bare Number('v1') is NaN.\n const seg = (s: string): number => Number(s.replace(/^v/, '')) || 0;\n const pa = a.split('.').map(seg);\n const pb = b.split('.').map(seg);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x > y;\n }\n return false;\n}\n\n/**\n * Per-segment numeric semver `<` (the minVersion floor comparison). Same comparison\n * discipline as {@link semverGt} / install.ts's downgrade guard: numeric per\n * segment, non-numeric → 0. Returns true when `a` is strictly less than `b`.\n */\nfunction semverLt(a: string, b: string): boolean {\n // Strip a leading `v` (npm-idiomatic) — bare Number('v1') is NaN.\n const seg = (s: string): number => Number(s.replace(/^v/, '')) || 0;\n const pa = a.split('.').map(seg);\n const pb = b.split('.').map(seg);\n for (let i = 0; i < 3; i++) {\n const x = pa[i] ?? 0;\n const y = pb[i] ?? 0;\n if (x !== y) return x < y;\n }\n return false;\n}\n\nexport async function update(opts: UpdateOptions = {}): Promise<void> {\n // NOIR_DISABLE_UPDATES is the hard kill-switch for the SELF-UPDATE surface\n // (distinct from NOIR_DISABLE_UPDATE_CHECK, which only gags the background\n // version check). When set, `noir update` refuses to run at all — enterprises\n // can pin this in the environment to enforce \"updates flow only through the\n // package manager / image rebuild, never from inside the CLI\". `fail` is\n // `: never` (always throws) — the explicit `return` is defensive.\n if (process.env.NOIR_DISABLE_UPDATES !== undefined) {\n fail(\n EXIT.USAGE,\n 'self-update is disabled (NOIR_DISABLE_UPDATES is set); update via your package manager or image rebuild.',\n opts,\n );\n return;\n }\n\n const method = detectActiveMethod();\n const rec = readInstallRecord();\n const currentVersion = rec?.version ?? null;\n\n if (opts.check === true) {\n const checkSpin = spinner('Checking for updates...', opts).start();\n const latest = await fetchLatestVersion('latest');\n if (latest) {\n checkSpin.succeed(`Latest: ${latest} (you have ${currentVersion ?? 'unknown'})`);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { latest, currentVersion } })}\\n`);\n }\n } else {\n checkSpin.fail('Could not reach the registry.');\n // fail() emits the canonical {ok:false,error} envelope under --json AND\n // exits 1 (a manual envelope here would leave the process at exit 0 while\n // the payload claims code 1).\n fail(EXIT.ERROR, 'Could not reach the registry.', opts);\n }\n return;\n }\n\n // Fetch latest (network; timeout-bound).\n const fetchSpin = spinner('Checking for updates...', opts).start();\n const latest = await fetchLatestVersion('latest');\n if (latest === null) {\n fetchSpin.fail('Could not reach the registry.');\n // Registry-unreachable is a FAILURE: route through fail() so --json emits\n // {ok:false,error} on stdout AND the process exits 1 (a silent return would\n // leave stdout empty + exit 0 — indistinguishable from success for scripts).\n fail(EXIT.ERROR, 'Could not reach the registry.', opts);\n }\n fetchSpin.succeed(`Latest: ${latest}`);\n // Read the `update.minVersion` floor from the project config (falls\n // back to the config default '1.6.0' when the project isn't initialized or\n // the block is absent). Same try/catch pattern as home.ts's update-config read.\n let minVersion = DEFAULT_UPDATE_CONFIG.minVersion;\n try {\n minVersion = loadProjectInfo(process.cwd()).config.update.minVersion;\n } catch {\n // Not initialized yet — the config default applies.\n }\n const target = buildUpdateTarget({\n method,\n channel: 'latest',\n spec: opts.spec,\n currentVersion,\n latestKnown: latest,\n minVersion,\n });\n\n // Refuse to install a registry-offered version below the configured\n // minVersion floor (same warn/refuse pattern as install.ts's downgrade guard).\n // Checked before the isUpgrade branch: a below-floor offer is unsafe even if\n // it happens to be newer than the (very old) current install.\n if (target.belowMinVersion) {\n // Name the ACTUAL target (a concrete positional spec), not the registry's latest.\n warn(`Target ${target.targetSpec} is below the minVersion floor (${minVersion}).`);\n fail(\n EXIT.USAGE,\n `refusing update to ${target.targetSpec} (below minVersion ${minVersion})`,\n opts,\n );\n }\n\n if (!target.isUpgrade) {\n fetchSpin.succeed(`noir ${currentVersion} is up to date`);\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: true, data: { version: currentVersion, upToDate: true } })}\\n`,\n );\n return;\n }\n info(`noir ${currentVersion} is up to date.`, opts);\n return;\n }\n\n if (method === 'native') {\n const updateSpin = spinner(\n `Updating to ${target.latestKnown} via native installer...`,\n opts,\n ).start();\n const res = await installManagedNode({\n version: target.targetSpec === 'latest' ? undefined : target.targetSpec,\n env: process.env,\n });\n if (!res.ok) {\n updateSpin.fail('Update failed');\n fail(EXIT.ERROR, res.error ?? 'update failed', opts);\n }\n // Chicken-egg safety net: the update was ORCHESTRATED by the OLD binary\n // (which may predate the chmod fix). Re-assert the freshly-written shim is\n // executable so the user's NEXT `noir …` doesn't fail with permission\n // denied. This is the ONLY line that closes the regression once 1.7.4+ is\n // installed — it runs in the NEW binary's process.\n ensureShimExecutable();\n updateSpin.succeed(`Updated to noir ${res.version}`);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { version: res.version } })}\\n`);\n return;\n }\n success(`Updated to ${res.version}.`);\n return;\n }\n\n // npm/pnpm/yarn/bun/homebrew/scoop -> reinstall via the same manager.\n const cmd = updateCmdFor(method, target.targetSpec);\n if (!cmd)\n fail(EXIT.USAGE, `cannot auto-update a ${method} install; use the manager directly`, opts);\n const mgrSpin = spinner(`Updating via ${method}...`, opts).start();\n const { code, stderr } = await runManagerCmd(cmd, { env: process.env });\n if (code !== 0) {\n mgrSpin.fail(`Update failed`);\n fail(EXIT.ERROR, `update failed: ${stderr.slice(0, 300)}`, opts);\n }\n mgrSpin.succeed('Updated');\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { version: target.targetSpec } })}\\n`);\n return;\n }\n success('Updated.');\n}\n\nfunction updateCmdFor(method: string, spec: string): string[] | null {\n switch (method) {\n case 'npm':\n return ['npm', 'install', '-g', `@noir-ai/cli@${spec}`];\n case 'pnpm':\n return ['pnpm', 'add', '-g', `@noir-ai/cli@${spec}`];\n case 'yarn':\n return ['yarn', 'global', 'add', `@noir-ai/cli@${spec}`];\n case 'bun':\n return ['bun', 'add', '-g', `@noir-ai/cli@${spec}`];\n case 'homebrew':\n return ['brew', 'upgrade', 'noir'];\n case 'scoop':\n return ['scoop', 'update', 'noir'];\n default:\n return null;\n }\n}\n\n/**\n * The human-readable update command for an install method. Mirrors\n * {@link updateCmdFor} (the machine command) with the manager the user should\n * actually run — `noir update` is the universal entry (it re-dispatches to the\n * right manager), but the package-manager spellings are shown for transparency.\n */\nexport function updateAdvice(method: string): string {\n switch (method) {\n case 'npm':\n return 'run `npm install -g @noir-ai/cli@latest`';\n case 'pnpm':\n return 'run `pnpm add -g @noir-ai/cli@latest`';\n case 'yarn':\n return 'run `yarn global add @noir-ai/cli@latest`';\n case 'bun':\n return 'run `bun add -g @noir-ai/cli@latest`';\n case 'homebrew':\n return 'run `brew upgrade noir`';\n case 'scoop':\n return 'run `scoop update noir`';\n case 'native':\n return 'run `noir update`';\n default:\n return 'run `noir update`';\n }\n}\n\n/** A \"new version available\" notice for the home menu (version + advice). */\nexport interface UpdateNotice {\n readonly latestVersion: string;\n readonly currentVersion: string;\n readonly method: string;\n readonly advice: string;\n}\n\n/**\n * Build the home-menu \"update available\" notice from the cached latest version.\n * Returns `null` when there is no cached latest, no current version, or the\n * latest is NOT strictly newer (the semver downgrade guard — a stale/older\n * cache entry is never advertised as an upgrade).\n */\nexport function buildUpdateNotice(opts: {\n method: string;\n currentVersion: string | null;\n latestKnown: string | null;\n}): UpdateNotice | null {\n const { method, currentVersion, latestKnown } = opts;\n if (currentVersion == null || latestKnown == null) return null;\n if (!semverGt(latestKnown, currentVersion)) return null;\n return { latestVersion: latestKnown, currentVersion, method, advice: updateAdvice(method) };\n}\n\n/** Non-blocking, time-boxed startup check. Writes the cache on success; silent on any failure. */\nexport async function runAsyncUpdateCheck(opts: {\n env: NodeJS.ProcessEnv;\n configUpdate: UpdateConfigLike;\n quiet: boolean;\n}): Promise<void> {\n try {\n const cache = readUpdateCache();\n if (!shouldCheckForUpdate({ env: opts.env, configUpdate: opts.configUpdate, cache })) return;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 2000);\n const latest = await fetchLatestVersion(opts.configUpdate.channel, controller.signal);\n clearTimeout(timer);\n if (latest != null) {\n writeUpdateCache({\n lastCheckAt: new Date().toISOString(),\n latestVersion: latest,\n channel: opts.configUpdate.channel,\n });\n }\n // Non-blocking: never print in quiet/CI/non-TTY; a mismatch only nudges.\n } catch {\n // silent on any failure — this is a non-blocking background check\n }\n}\n","// `noir status [--json]`.\n//\n// Aggregates a project + daemon + store + context + workflow + memory snapshot.\n// Project info (id/name/host/version) is assembled IN-PROCESS from\n// `loadProjectInfo` + `NOIR_VERSION` — no daemon round-trip just to name the\n// project. Daemon state comes from a read-only {@link probeDaemon} (liveness\n// probe of `~/.noir/daemon.json` + pid + GET /health) that NEVER starts a daemon\n// (deliberate: `status` is probe-only and works daemon-down — a down\n// daemon is reported honestly + exit 0, never auto-started). When the probe finds\n// a running daemon, the optional count tools are fetched over one connection via\n// {@link withRunningDaemon} (which reuses the running daemon and also never\n// starts one); if the daemon is down, those sections are simply `null` and the\n// snapshot still renders.\n//\n// Graceful degradation (every engine is optional): every count tool —\n// `host_status` (enriches `daemon.transport`), `store_status`, `context_status`,\n// `workflow_status`, `memory_sessions` — is wrapped in {@link tryTool} and\n// contributes `null` when absent or when its engine is not wired. One missing\n// engine never fails the snapshot; even the daemon being down doesn't (it is the\n// one piece of information `status` exists to report). A tool's own\n// logical-failure envelope (e.g. `workflow_status` returning\n// `{ok:false,error:'no active task'}`) is data, not a transport failure, and is\n// normalized to `null`.\n//\n// Active commands (`context *`, `memory *`, `task *`) do real work and KEEP using\n// `withDaemon` (auto-start acceptable for commands that perform writes/reads with\n// side-effects); their daemon-down path is the same clean exit-4 envelope. Only\n// `status` is probe-only — in-process read fallback for the active commands is\n// deferred to v1.x.\n//\n// Stream discipline: `--json` emits the versioned `{ok,data}` envelope\n// to STDOUT (the only stdout write); the human table + banner go to STDERR via\n// the centralized `table()` / `log()` helpers (auto-stripped under NO_COLOR /\n// non-TTY / --json).\n\nimport { loadProjectInfo, NOIR_VERSION, type ProjectInfo } from '@noir-ai/core';\nimport {\n type DaemonClientOptions,\n type DaemonProbe,\n type DaemonToolCaller,\n probeDaemon,\n withRunningDaemon,\n} from '../daemon-client.js';\nimport { type CliOptions, definitionList, EXIT, fail, log, tip } from '../output.js';\nimport { badge } from '../theme.js';\nimport { DEFAULT_UPDATE_CONFIG, runAsyncUpdateCheck } from './update.js';\n\n/** Options accepted by `status` (global flags + daemon-client knobs). */\nexport interface StatusOptions extends CliOptions, DaemonClientOptions {}\n\n// ---------------------------------------------------------------------------\n// Tool result shapes (the relevant slices of the daemon's payloads). The daemon\n// returns these as JSON text; daemon-client parses them to `unknown`, so each\n// reader below treats a missing/foreign field as `undefined` and the section is\n// reported `null`. Keeping these local (rather than importing the daemon's\n// internal types) means the CLI depends only on the MCP wire contract.\n// ---------------------------------------------------------------------------\ninterface HostStatusResult {\n noir: string;\n project: { id: string; name: string };\n host: string;\n transport: string;\n daemon: boolean;\n pid?: number;\n uptimeSec?: number;\n}\n\ninterface StoreStatusResult {\n ok: boolean;\n projectId: string;\n docCount: number;\n vecCount: number;\n dbPath: string | null;\n degraded: boolean;\n}\n\ninterface ContextStatusResult {\n ok: boolean;\n projectId: string;\n docCount: number;\n vecCount: number;\n indexedFiles: number;\n embedder: { kind: string; model?: string; dim: number };\n degraded: boolean;\n}\n\ninterface WorkflowStatusResult {\n ok: boolean;\n taskId: string;\n phase: string;\n state: string;\n mode: string;\n nextGate: string | null;\n degraded: boolean;\n}\n\ninterface MemorySessionsResult {\n ok: boolean;\n sessions: Array<{ id: string; count: number; lastTs: number }>;\n}\n\n// ---------------------------------------------------------------------------\n// Normalized payload (the `data` of the `--json` envelope + the source for the\n// human table). Every optional section is `null` when its engine is absent, so\n// a `--json` consumer can branch with a simple null check.\n// ---------------------------------------------------------------------------\nexport interface StatusPayload {\n noir: string;\n project: { id: string; name: string };\n host: string;\n daemon: {\n running: boolean;\n transport?: string;\n pid?: number;\n uptimeSec?: number;\n };\n store: {\n docCount: number;\n vecCount: number;\n dbPath: string | null;\n degraded: boolean;\n } | null;\n context: {\n docCount: number;\n vecCount: number;\n indexedFiles: number;\n embedder: string;\n degraded: boolean;\n } | null;\n workflow: {\n taskId: string;\n phase: string;\n state: string;\n mode: string;\n nextGate: string | null;\n degraded: boolean;\n } | null;\n memory: { sessions: number; observations: number } | null;\n}\n\n/**\n * Call an optional daemon tool, returning `null` on ANY failure. The daemon\n * registers `store_status` / `context_status` / `workflow_status` /\n * `memory_sessions` only when it wired the matching engine, so a missing engine\n * surfaces as a tool-call failure that we fold to `null` rather than failing\n * the snapshot. A tool's own `{ok:false,…}` envelope is returned as data (the\n * caller normalizes it) — only transport / parse / not-registered failures map\n * to `null` here.\n *\n * Exported so `noir handoff` reuses the SAME fold-to-null pattern for its\n * bounded `context_search` / `memory_recall` extraction — a missing embedder or\n * a daemon-down degrades to `null` instead of failing the handoff artifact.\n */\nexport async function tryTool<T>(\n caller: DaemonToolCaller,\n name: string,\n args?: Record<string, unknown>,\n): Promise<T | null> {\n try {\n return await caller.callTool<T>(name, args);\n } catch {\n // Engine absent (tool not registered), transport hiccup, or non-JSON text.\n // All are non-fatal for an optional section; report it as unavailable.\n return null;\n }\n}\n\nfunction pickStore(s: StoreStatusResult | null): StatusPayload['store'] {\n if (s?.ok !== true) return null;\n return {\n docCount: s.docCount,\n vecCount: s.vecCount,\n dbPath: s.dbPath,\n degraded: s.degraded === true,\n };\n}\n\nfunction describeEmbedder(e: ContextStatusResult['embedder']): string {\n if (!e || e.kind === 'none') return 'none (BM25-only)';\n const model = typeof e.model === 'string' && e.model.length > 0 ? e.model : '<unset>';\n return `${e.kind} · ${model} (${e.dim}-dim)`;\n}\n\nfunction pickContext(c: ContextStatusResult | null): StatusPayload['context'] {\n if (c?.ok !== true) return null;\n return {\n docCount: c.docCount,\n vecCount: c.vecCount,\n indexedFiles: c.indexedFiles,\n embedder: describeEmbedder(c.embedder),\n degraded: c.degraded === true,\n };\n}\n\nfunction pickWorkflow(w: WorkflowStatusResult | null): StatusPayload['workflow'] {\n // `workflow_status` returns `{ok:false,error:'no active task'}` (data) when no\n // task is active — normalize that to `null` so the payload's `workflow` field\n // cleanly means \"active task snapshot present\".\n if (w?.ok !== true) return null;\n return {\n taskId: w.taskId,\n phase: w.phase,\n state: w.state,\n mode: w.mode,\n nextGate: w.nextGate,\n degraded: w.degraded === true,\n };\n}\n\nfunction pickMemory(m: MemorySessionsResult | null): StatusPayload['memory'] {\n if (m?.ok !== true || !Array.isArray(m.sessions)) return null;\n let observations = 0;\n for (const s of m.sessions) observations += s.count;\n return { sessions: m.sessions.length, observations };\n}\n\n/**\n * Build the normalized payload from in-process project info + the daemon probe\n * + the (all-optional) count-tool results. Daemon `running`/`pid`/`uptimeSec`\n * come from the probe (the honest liveness source); `transport` is enriched from\n * `host_status` when that tool is reachable, else omitted. Every other section is\n * `null` when its engine is absent OR the daemon is down.\n */\nfunction buildPayload(\n project: ProjectInfo,\n probe: DaemonProbe,\n host: HostStatusResult | null,\n store: StoreStatusResult | null,\n context: ContextStatusResult | null,\n workflow: WorkflowStatusResult | null,\n memory: MemorySessionsResult | null,\n): StatusPayload {\n return {\n noir: NOIR_VERSION,\n project: { id: project.id, name: project.name },\n host: project.config.host,\n daemon: {\n running: probe.running,\n ...(probe.pid !== undefined ? { pid: probe.pid } : {}),\n ...(probe.uptimeSec !== undefined ? { uptimeSec: probe.uptimeSec } : {}),\n ...(host && typeof host.transport === 'string' ? { transport: host.transport } : {}),\n },\n store: pickStore(store),\n context: pickContext(context),\n workflow: pickWorkflow(workflow),\n memory: pickMemory(memory),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Human rendering (stderr). A two-column Field/Value table is the leanest\n// readable shape for a status snapshot; cli-table3 is auto-stripped under\n// NO_COLOR / non-TTY and the whole call is a no-op under --json.\n// ---------------------------------------------------------------------------\nfunction formatDuration(sec?: number): string {\n if (typeof sec !== 'number' || sec < 0) return 'unknown';\n if (sec < 60) return `${sec}s`;\n if (sec < 3600) return `${Math.floor(sec / 60)}m ${sec % 60}s`;\n return `${Math.floor(sec / 3600)}h ${Math.floor((sec % 3600) / 60)}m`;\n}\n\nexport function describeDaemon(d: StatusPayload['daemon']): string {\n if (!d.running) return 'not running (start with `noir daemon start`)';\n const pid = typeof d.pid === 'number' ? `pid ${d.pid}` : 'pid unknown';\n const up = typeof d.uptimeSec === 'number' ? `, up ${formatDuration(d.uptimeSec)}` : '';\n const transport = typeof d.transport === 'string' ? `, ${d.transport}` : '';\n return `running (${pid}${up}${transport})`;\n}\n\nexport function describeStore(s: StatusPayload['store']): string {\n if (!s) return 'unavailable (store engine not wired)';\n const flag = s.degraded ? ` ${badge('warn', 'degraded: read-only')}` : '';\n return `${s.docCount} docs / ${s.vecCount} vecs${flag}`;\n}\n\nexport function describeContext(c: StatusPayload['context']): string {\n if (!c) return 'unavailable (context engine not wired)';\n const flag = c.degraded ? ` ${badge('warn', 'degraded')}` : '';\n return `${c.docCount} docs, ${c.vecCount} vecs, ${c.indexedFiles} files · embedder ${c.embedder}${flag}`;\n}\n\nexport function describeWorkflow(w: StatusPayload['workflow']): string {\n if (!w) return 'no active task';\n const gate = w.nextGate ? ` → next gate: ${w.nextGate}` : '';\n const flag = w.degraded ? ` ${badge('warn', 'degraded')}` : '';\n return `${w.phase} (${w.state}, ${w.mode}) · task ${w.taskId}${gate}${flag}`;\n}\n\nexport function describeMemory(m: StatusPayload['memory']): string {\n if (!m) return 'unavailable (memory engine not wired)';\n return `${m.observations} observations across ${m.sessions} session${m.sessions === 1 ? '' : 's'}`;\n}\n\nfunction renderHuman(p: StatusPayload, opts: CliOptions): void {\n // Banner + definition list both go to stderr (rendering is a no-op under\n // --json; we only render human when opts.json is false, but passing opts\n // keeps --quiet honest).\n log(`noir status — ${p.project.name} (${p.project.id})`, opts);\n definitionList(\n [\n { label: 'Project', value: `${p.project.name} (${p.project.id})` },\n { label: 'Host', value: p.host },\n { label: 'Noir', value: p.noir },\n { label: 'Daemon', value: describeDaemon(p.daemon) },\n { label: 'Store', value: describeStore(p.store) },\n { label: 'Context', value: describeContext(p.context) },\n { label: 'Workflow', value: describeWorkflow(p.workflow) },\n { label: 'Memory', value: describeMemory(p.memory) },\n ],\n opts,\n );\n // Resume hint: when a resumable (non-terminal) task is\n // present, surface the resume command git-status-style. `--no-tips`/`--json`\n // silence it (renderHuman is only called when !json; tip() honors --no-tips).\n if (p.workflow && p.workflow.state !== 'done' && p.workflow.state !== 'abandoned') {\n tip('resume: `noir task resume`', opts);\n }\n}\n\n/**\n * Gather the live snapshot into the normalized {@link StatusPayload}. Extracted\n * so `noir handoff` reuses the SAME aggregation path as `noir status`\n * (single source for the multi-engine snapshot). Project info is read in-process\n * (uninitialized → exit 1 propagated to the caller); daemon state comes from a\n * {@link probeDaemon} (NEVER auto-starts). When the daemon is up, the optional\n * count tools are fetched over one {@link withRunningDaemon} connection; when it\n * is down, those sections are `null` and `daemon:{running:false}` — this function\n * never throws on a down daemon (the handoff command degrades gracefully).\n */\nexport async function gatherStatusPayload(opts: StatusOptions): Promise<StatusPayload> {\n // In-process project info — no daemon round-trip. Uninitialized → exit 1\n // (both `status` and `handoff` treat an uninitialized project as a hard error\n // — there is no project to snapshot). Route through fail() so --json emits the\n // canonical {ok:false,error} envelope (a plain Error would leave stdout empty).\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n // Probe — never starts a daemon. A down daemon is reported honestly.\n const probe = await probeDaemon(opts);\n\n let host: HostStatusResult | null = null;\n let store: StoreStatusResult | null = null;\n let context: ContextStatusResult | null = null;\n let workflow: WorkflowStatusResult | null = null;\n let memory: MemorySessionsResult | null = null;\n\n if (probe.running) {\n // Reuse the already-running daemon — withRunningDaemon NEVER auto-starts\n // (probe-only). Pass the probe we already computed so it doesn't GET /health\n // a second time. Every tool is optional (tryTool folds a missing engine /\n // logical-failure envelope to null); host_status now only enriches\n // daemon.transport. If the daemon dies mid-snapshot (probe→connect race),\n // withRunningDaemon returns null and the sections stay null while the probe\n // data still populates the daemon section.\n await withRunningDaemon(\n opts,\n async (caller: DaemonToolCaller): Promise<void> => {\n host = await tryTool<HostStatusResult>(caller, 'host_status');\n store = await tryTool<StoreStatusResult>(caller, 'store_status');\n context = await tryTool<ContextStatusResult>(caller, 'context_status');\n workflow = await tryTool<WorkflowStatusResult>(caller, 'workflow_status');\n memory = await tryTool<MemorySessionsResult>(caller, 'memory_sessions');\n },\n probe,\n );\n }\n\n return buildPayload(project, probe, host, store, context, workflow, memory);\n}\n\n/**\n * `noir status`: aggregate the snapshot and render it.\n *\n * Project info is read in-process; daemon state comes from a {@link probeDaemon}\n * (NEVER auto-starts). When the daemon is up, the optional count tools are\n * fetched over one {@link withRunningDaemon} connection; when it is down, those\n * sections are `null` and `daemon:{running:false}` is rendered with exit 0\n * (status is informational — a down daemon is not an error).\n *\n * `--json` emits `{ok:true, data: StatusPayload}` to stdout. The human path\n * renders a two-column table on stderr. Uninitialized project → exit 1 from\n * `loadProjectInfo` (same as every other command).\n */\nexport async function status(opts: StatusOptions): Promise<void> {\n // fire-and-forget; never blocks, never prints under --json/--quiet/CI/non-TTY.\n void runAsyncUpdateCheck({\n env: process.env,\n configUpdate: (() => {\n try {\n const info = loadProjectInfo(process.cwd());\n return info.config.update ?? DEFAULT_UPDATE_CONFIG;\n } catch {\n return DEFAULT_UPDATE_CONFIG;\n }\n })(),\n quiet: opts.json === true || opts.quiet === true || !process.stdout.isTTY,\n });\n\n const payload = await gatherStatusPayload(opts);\n if (opts.json === true) {\n // Single stdout write — the versioned success envelope.\n process.stdout.write(`${JSON.stringify({ ok: true, data: payload })}\\n`);\n return;\n }\n renderHuman(payload, opts);\n}\n","// `noir task {new,status,advance,next}`.\n//\n// Thin MCP-client commands over the daemon's workflow surface. The daemon\n// registers four workflow tools (packages/daemon/src/server.ts): `workflow_status`\n// (read the active or named task), `checkpoint` (save/restore in-flight state),\n// `workflow_start` (start a task), and `workflow_advance` (advance/jump a task).\n// All four CLI sub-commands are wired: `status`/`next` → `workflow_status`;\n// `new` → `workflow_start`; `advance` → `workflow_advance`.\n//\n// `new`/`advance` are writes. A read-only (daemon-down) store refuses them with\n// a `{ok:false,degraded:true}` envelope (surfaced as exit 1 here); any other\n// logical failure (unknown task, no active task, illegal transition) is likewise\n// exit 1 for these writes. Daemon-unreachable ⇒ exit 4 (DAEMON_DOWN) from\n// `callDaemonTool`, so transport vs logical failures stay distinguishable.\n//\n// `status` / `next` (reads) map the daemon's logical-failure envelopes\n// (`{ok:false,error:'no active task'}` / `{ok:false,error:'unknown task'}`)\n// onto exit 3 (NOT_FOUND) — a focused task read reporting absence is a\n// not-found condition, not an error (the broader `noir status` folds the same\n// envelope to `null` and stays exit 0).\n\nimport { spawnSync } from 'node:child_process';\nimport { writeFileSync } from 'node:fs';\nimport { loadProjectInfo, nextArtifactSequence, type ProjectInfo, sha256Hex } from '@noir-ai/core';\nimport {\n PHASES,\n type Phase,\n TASK_CLASSES,\n type TaskClass,\n writeChangelogStub,\n writeDecisionStub,\n} from '@noir-ai/workflow';\nimport {\n callDaemonTool,\n type DaemonClientOptions,\n type DaemonProbe,\n probeDaemon,\n withInProcessRead,\n} from '../daemon-client.js';\nimport {\n type CliOptions,\n definitionList,\n EXIT,\n fail,\n info,\n isInteractive,\n log,\n success,\n tip,\n warn,\n} from '../output.js';\nimport { badge } from '../theme.js';\n\n/** Options accepted by every `task` sub-command (globals + daemon knobs). */\nexport interface TaskOptions extends CliOptions, DaemonClientOptions {}\n\n// ---------------------------------------------------------------------------\n// Tool result shapes (slices of the daemon wire payloads; local types).\n// ---------------------------------------------------------------------------\n\n/** `workflow_status` success payload (WorkflowStatus). */\ninterface WorkflowStatusResult {\n ok: true;\n taskId: string;\n phase: string;\n state: string;\n mode: string;\n nextGate: string | null;\n history?: unknown;\n updatedAt?: number;\n degraded?: boolean;\n /** Task class (drives the soft PRD gate); absent on legacy tasks. */\n taskClass?: string;\n /** Block reason captured by `setBlocked` (absent unless blocked). */\n blockReason?: string;\n}\n\n/** `workflow_status` logical-failure envelopes (no active / unknown task). */\ninterface WorkflowNotFound {\n ok: false;\n error?: string;\n taskId?: string;\n}\n\n/** `workflow_resume` payload — a WorkflowStatus + a resumable flag. */\ninterface WorkflowResumeResult {\n ok: boolean;\n resumable?: boolean;\n taskId?: string;\n phase?: string;\n state?: string;\n mode?: string;\n nextGate?: string | null;\n history?: unknown;\n updatedAt?: number;\n degraded?: boolean;\n taskClass?: string;\n blockReason?: string;\n error?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Phase → skill suggestion (grounded in the real @noir-ai/skills builtin pack;\n// each phase maps to a shipped noir-* skill the host can invoke next).\n// Exported so `noir handoff` reuses the SAME phase→skill map when naming\n// the next gate's skill in the handoff artifact — single source.\n// ---------------------------------------------------------------------------\nexport const PHASE_SKILL: Readonly<Record<string, string>> = {\n // Skill curation: intake+clarify merged into brainstorming;\n // execute → executing-plans; verify → verifying; document → wrap.\n intake: 'noir-brainstorming',\n clarify: 'noir-brainstorming',\n spec: 'noir-spec',\n plan: 'noir-planning',\n execute: 'noir-executing-plans',\n verify: 'noir-verifying',\n document: 'noir-wrap',\n};\n\n/** Map a phase string to its shipped skill id (`null` if unknown / absent).\n * Exported for `noir handoff`. */\nexport function skillFor(phase: string | null | undefined): string | null {\n if (typeof phase !== 'string') return null;\n return PHASE_SKILL[phase] ?? null;\n}\n\n/**\n * Write the document-phase artifacts when a task\n * lands at `done`: a changelog entry + a pending decision-record stub. Uses the\n * artifact conflict seam with `preserve` policy (never clobber a user's edit).\n * The decision-record number is the next after the highest existing ADR.\n */\nfunction writeDoneArtifacts(opts: CliOptions, taskId: string): void {\n try {\n const project = loadProjectInfo(process.cwd());\n const conflict = { conflictPolicy: 'preserve' as const, interactive: false };\n writeChangelogStub(project.root, `- ${taskId}: completed (verify gate passed)`, conflict);\n // Decision-record numbering: the canonical per-type sequence over\n // `.noir/decisions/` (`ADR-<NNNN>-<slug>.md`), max+1. The CLI collapses\n // taskId == slug (`task new --slug`), so the task id doubles as the ADR slug.\n const nextN = nextArtifactSequence(project.root, 'adr');\n writeDecisionStub(project.root, nextN, taskId, undefined, conflict);\n } catch {\n // Artifact writes are best-effort — a failure must never mask the advance\n // result. Surface a tip so the user knows the artifacts didn't land.\n tip('document-phase artifacts could not be written (use --no-artifacts to skip)', opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared fetch + render\n// ---------------------------------------------------------------------------\n\n/**\n * Call `workflow_status` (active task when `taskId` is absent). Returns the\n * success payload or throws exit 3 (NOT_FOUND) for a logical-failure envelope\n * (no active task / unknown task) — a focused task read's \"absent\" is a\n * not-found condition.\n */\nasync function fetchStatus(opts: TaskOptions, taskId?: string): Promise<WorkflowStatusResult> {\n const args: Record<string, unknown> =\n typeof taskId === 'string' && taskId.length > 0 ? { taskId } : {};\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_status',\n args,\n );\n if (res.ok === true) return res;\n const detail = res.error ?? 'task not found';\n // exit 3 (NOT_FOUND): the daemon was reachable; there's just no such task.\n fail(EXIT.NOT_FOUND, `task: ${detail}`, opts);\n}\n\n/** Format a `WorkflowStatus.updatedAt` epoch-ms stamp as a readable UTC string. */\nfunction formatStamp(ms?: number): string {\n if (typeof ms !== 'number' || ms <= 0) return '-';\n return new Date(ms).toISOString().replace('T', ' ').replace(/\\..*$/, 'Z');\n}\n\nfunction renderStatusRow(s: WorkflowStatusResult, opts: CliOptions): void {\n const flag = s.degraded === true ? ` ${badge('warn', 'degraded')}` : '';\n log(`task status — ${s.taskId}${flag}`, opts);\n const rows: { label: string; value: string }[] = [\n { label: 'Task', value: s.taskId },\n { label: 'Phase', value: s.phase },\n { label: 'State', value: s.state },\n { label: 'Mode', value: s.mode },\n ];\n if (typeof s.taskClass === 'string' && s.taskClass.length > 0) {\n rows.push({ label: 'Class', value: s.taskClass });\n }\n if (typeof s.blockReason === 'string' && s.blockReason.length > 0) {\n rows.push({ label: 'Block reason', value: s.blockReason });\n }\n rows.push(\n { label: 'Next gate', value: s.nextGate ?? '—' },\n { label: 'Updated', value: formatStamp(s.updatedAt) },\n );\n definitionList(rows, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task status [<id>]`\n// ---------------------------------------------------------------------------\nexport interface TaskStatusOptions extends TaskOptions {\n /** Positional task id; omitted ⇒ the active task. */\n id?: string;\n}\n\n/** The gate phases in lifecycle order (mirrors the daemon's `nextGateAfter`). */\nconst GATE_PHASES = ['spec', 'plan', 'verify'] as const satisfies readonly Phase[];\n\n/** The next gate-phase strictly ahead of `phase`, or `null` (past verify). */\nfunction nextGateAfter(phase: string): Phase | null {\n // The verify gate is ASYMMETRIC: it fires on entering DONE, so a task AT the\n // verify phase still has it ahead (mirrors the daemon's nextGateAfter).\n if (phase === 'verify') return 'verify';\n const cur = PHASES.indexOf(phase as Phase);\n for (const p of GATE_PHASES) {\n if (PHASES.indexOf(p) > cur) return p;\n }\n return null;\n}\n\n/**\n * Run `task status` against the daemon when it is up, or fall back to the\n * IN-PROCESS read-only workflow engine when the daemon probe reports it down\n * (the daemon-down read fallback). A status read on a read-only store is a pure KV read, so instead\n * of exit 4 it resolves against the in-process engine (still exit 3 NOT_FOUND\n * for an unknown / absent task). Writes (`task new` / `advance`) keep the\n * daemon-required exit-4 path.\n */\nexport async function taskStatus(opts: TaskStatusOptions): Promise<void> {\n // Conservative probe (mirrors contextSearch/memoryRecall): only a CONFIRMED\n // `{running:false}` engages the in-process fallback; an unavailable probe\n // defaults to the daemon path.\n let probe: DaemonProbe;\n try {\n probe = await probeDaemon(opts);\n } catch {\n probe = { running: true };\n }\n if (!probe.running) {\n const s = await withInProcessRead(opts, async (engines) => {\n const id = typeof opts.id === 'string' && opts.id.length > 0 ? opts.id : null;\n const taskId = id ?? engines.workflow.activeTaskId();\n if (!taskId) fail(EXIT.NOT_FOUND, 'task: no active task', opts);\n const task = engines.workflow.status(taskId);\n if (!task) fail(EXIT.NOT_FOUND, `task: unknown task '${taskId}'`, opts);\n const stopped = task.state === 'blocked' || task.state === 'abandoned';\n return {\n ok: true as const,\n taskId: task.taskId,\n phase: task.phase,\n state: task.state,\n mode: task.mode,\n nextGate: stopped ? null : nextGateAfter(task.phase),\n history: task.history,\n updatedAt: task.updatedAt,\n degraded: true as const,\n };\n });\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: s })}\\n`);\n return;\n }\n renderStatusRow(s, opts);\n return;\n }\n const s = await fetchStatus(opts, opts.id);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: s })}\\n`);\n return;\n }\n renderStatusRow(s, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task next`\n// ---------------------------------------------------------------------------\nexport async function taskNext(opts: TaskOptions): Promise<void> {\n const s = await fetchStatus(opts);\n const suggestion = skillFor(s.phase);\n const nextSkill = skillFor(s.nextGate);\n const data = {\n taskId: s.taskId,\n phase: s.phase,\n state: s.state,\n mode: s.mode,\n nextGate: s.nextGate,\n suggestion,\n nextSkill,\n degraded: s.degraded === true,\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n const flag = data.degraded ? ` ${badge('warn', 'degraded')}` : '';\n log(`task next — ${s.taskId} · phase ${s.phase} (${s.state}, ${s.mode})${flag}`, opts);\n if (s.nextGate) {\n info(`next gate: ${s.nextGate}`, opts);\n } else {\n info('no further gate ahead (past verify, or stopped).', opts);\n }\n if (suggestion) info(`skill for current phase (${s.phase}): ${suggestion}`, opts);\n if (nextSkill) info(`skill for next gate (${s.nextGate}): ${nextSkill}`, opts);\n if (!suggestion && !nextSkill) {\n info('run `noir skills list` to see applicable skills.', opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir task new --slug <slug> [--mode full|quick]` → workflow_start\n// ---------------------------------------------------------------------------\nexport interface TaskNewOptions extends TaskOptions {\n slug: string;\n mode?: string;\n /** Task class — drives the soft PRD gate (prd.mandatoryFor). */\n taskClass?: string;\n}\n\nexport async function taskNew(opts: TaskNewOptions): Promise<void> {\n // Validate mode client-side so a typo is a clean usage error (exit 2) rather\n // than a server-side zod rejection that would mis-map onto exit 4.\n let mode: 'full' | 'quick' | undefined;\n if (opts.mode !== undefined) {\n if (opts.mode !== 'full' && opts.mode !== 'quick') {\n fail(EXIT.USAGE, `task new: invalid mode '${opts.mode}' (expected 'full' or 'quick')`, opts);\n }\n mode = opts.mode;\n }\n // Validate taskClass client-side (exit 2 on a typo).\n let taskClass: TaskClass | undefined;\n if (opts.taskClass !== undefined) {\n if (!TASK_CLASSES.includes(opts.taskClass as TaskClass)) {\n fail(\n EXIT.USAGE,\n `task new: invalid class '${opts.taskClass}' (expected one of: ${TASK_CLASSES.join(', ')})`,\n opts,\n );\n }\n taskClass = opts.taskClass as TaskClass;\n }\n // The CLI surface only takes a slug, so it doubles as the stable taskId — the\n // engine's taskId/slug distinction collapses to the slug here. Re-starting the\n // same slug overwrites (the KV is the source of truth, not a journal).\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_start',\n {\n taskId: opts.slug,\n slug: opts.slug,\n ...(mode === undefined ? {} : { mode }),\n ...(taskClass === undefined ? {} : { taskClass }),\n },\n );\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'start failed';\n fail(EXIT.ERROR, `task new --slug ${opts.slug}: ${detail}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderStatusRow(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task advance [--to <phase>] [--force <reason>]` → workflow_advance\n// ---------------------------------------------------------------------------\nexport interface TaskAdvanceOptions extends TaskOptions {\n to?: string;\n force?: string;\n /** Record the landing gate as `skipped` (advance continues). */\n skip?: boolean;\n /** Skip the document-phase artifact writes (changelog + decision stubs). */\n noArtifacts?: boolean;\n}\n\nexport async function taskAdvance(opts: TaskAdvanceOptions): Promise<void> {\n const args: Record<string, unknown> = {};\n if (typeof opts.to === 'string' && opts.to.length > 0) {\n // Validate the target phase client-side (exit 2 on a typo) — PHASE_SKILL's\n // keys ARE the valid phases, so reuse them rather than a second literal.\n if (!Object.keys(PHASE_SKILL).includes(opts.to)) {\n fail(EXIT.USAGE, `task advance: invalid phase '${opts.to}'`, opts);\n }\n args.to = opts.to;\n }\n if (typeof opts.force === 'string' && opts.force.length > 0) {\n args.force = { reason: opts.force };\n }\n if (opts.skip === true) {\n args.skip = true;\n }\n // Omit taskId → the daemon targets the active task (workflow:active).\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_advance',\n args,\n );\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'advance failed';\n fail(EXIT.ERROR, `task advance: ${detail}`, opts);\n }\n // Document-phase artifact wiring: when the task\n // lands at `done`, write a changelog entry + a pending decision-record stub\n // via the artifact conflict seam (preserve on conflict). `--no-artifacts`\n // skips; the memory consolidation hook is provider-gated and fires via the\n // daemon's existing memory engine when configured (not re-implemented here).\n if (res.state === 'done' && opts.noArtifacts !== true) {\n writeDoneArtifacts(opts, res.taskId);\n }\n // Surface (never auto-emit) the handoff command at the verify gate, the\n // natural handoff point (work moves from Noir's planning into the host's\n // execution). One-line stderr hint via `tip()` so `--no-tips` / `--json`\n // silence it in CI / pipes. Never blocks; never writes to stdout.\n if (typeof opts.to === 'string' && opts.to === 'verify') {\n tip('run `noir handoff` for a ready-to-paste host prompt', opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderStatusRow(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task resume [<id>] [--prompt '<continue instruction>']`\n// → workflow_resume\n// ---------------------------------------------------------------------------\nexport interface TaskResumeOptions extends TaskOptions {\n id?: string;\n prompt?: string;\n}\n\n/** Render a resume briefing: state, phase, next action + skill hint + artifacts. */\nfunction renderResumeBriefing(r: WorkflowResumeResult, opts: CliOptions): void {\n const flag = r.degraded === true ? ` ${badge('warn', 'degraded')}` : '';\n log(`resume — ${r.taskId} (${r.state}, ${r.mode})${flag}`, opts);\n const rows: { label: string; value: string }[] = [\n { label: 'Task', value: String(r.taskId) },\n { label: 'Phase', value: String(r.phase) },\n { label: 'State', value: String(r.state) },\n { label: 'Mode', value: String(r.mode) },\n ];\n if (typeof r.taskClass === 'string' && r.taskClass.length > 0) {\n rows.push({ label: 'Class', value: r.taskClass });\n }\n if (typeof r.blockReason === 'string' && r.blockReason.length > 0) {\n rows.push({ label: 'Block reason', value: r.blockReason });\n }\n rows.push({ label: 'Next gate', value: r.nextGate ?? '—' });\n definitionList(rows, opts);\n const suggestion = skillFor(r.phase ?? null);\n if (suggestion) info(`skill for current phase (${r.phase}): ${suggestion}`, opts);\n // A blocked task resumes via jump-to-phase (FSM edges from blocked → any in-flight).\n if (r.state === 'blocked') {\n info('resume a blocked task with: noir task advance --to <phase>', opts);\n }\n}\n\nexport async function taskResume(opts: TaskResumeOptions): Promise<void> {\n const args: Record<string, unknown> = {};\n // A positional id targets a specific task; omit it → the active task.\n if (typeof opts.id === 'string' && opts.id.length > 0) {\n args.taskId = opts.id;\n }\n const res = await callDaemonTool<WorkflowResumeResult>(opts, 'workflow_resume', args);\n // resume is a read; the \"nothing to resume\" envelope is exit 1 (not a crash).\n // Route through fail() in BOTH branches so --json also exits 1 (fail emits the\n // canonical {ok:false,error} envelope on stdout) — previously --json wrote a\n // non-canonical shape and exited 0.\n if (res.resumable !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'nothing to resume';\n fail(EXIT.ERROR, `task resume: ${detail}`, opts);\n }\n // `--prompt` records a resume intent (additive KV, observable — not an FSM\n // state change). Surface it in the briefing so the host sees the continue note.\n if (typeof opts.prompt === 'string' && opts.prompt.length > 0) {\n info(`resume prompt: ${opts.prompt}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderResumeBriefing(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task block <reason> [--task <id>]` → workflow_block\n// ---------------------------------------------------------------------------\nexport interface TaskBlockOptions extends TaskOptions {\n reason: string;\n task?: string;\n}\n\nexport async function taskBlock(opts: TaskBlockOptions): Promise<void> {\n if (typeof opts.reason !== 'string' || opts.reason.trim().length === 0) {\n fail(EXIT.USAGE, 'task block: a non-empty <reason> is required', opts);\n }\n const args: Record<string, unknown> = { reason: opts.reason };\n if (typeof opts.task === 'string' && opts.task.length > 0) args.taskId = opts.task;\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_block',\n args,\n );\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'block failed';\n fail(EXIT.ERROR, `task block: ${detail}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderStatusRow(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task abandon [--task <id>]` → workflow_abandon (destructive confirm)\n// ---------------------------------------------------------------------------\nexport interface TaskAbandonOptions extends TaskOptions {\n task?: string;\n}\n\nexport async function taskAbandon(opts: TaskAbandonOptions): Promise<void> {\n // Abandonment is terminal — confirm in interactive mode (mirrors the in-TUI\n // destructive-confirm pattern). --no-input / --json / CI skip the prompt.\n if (isInteractive(opts)) {\n const clack = await import('@clack/prompts');\n const confirm = await clack.confirm({\n message: 'Abandon this task? This is terminal and cannot be undone.',\n initialValue: false,\n });\n if (clack.isCancel(confirm)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n if (confirm !== true) {\n fail(EXIT.CANCELLED, 'abandon cancelled', opts);\n }\n }\n const args: Record<string, unknown> = {};\n if (typeof opts.task === 'string' && opts.task.length > 0) args.taskId = opts.task;\n const res = await callDaemonTool<WorkflowStatusResult | WorkflowNotFound>(\n opts,\n 'workflow_abandon',\n args,\n );\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'abandon failed';\n fail(EXIT.ERROR, `task abandon: ${detail}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n renderStatusRow(res, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task verify [--check <name> ...]` → runs checks + submits evidence\n// to workflow_advance.\n// ---------------------------------------------------------------------------\nexport interface TaskVerifyOptions extends TaskOptions {\n /** Restrict to a named subset of checks; defaults to all configured checks. */\n check?: string[];\n}\n\n/** A check definition resolved from the project config. */\ninterface VerifyCheck {\n name: string;\n command: string;\n tier: 'hard' | 'soft';\n}\n\n/** `workflow_advance` result with optional verify-gate pending/recovery. */\ninterface WorkflowAdvanceResult {\n ok: boolean;\n taskId?: string;\n phase?: string;\n state?: string;\n mode?: string;\n nextGate?: string | null;\n history?: unknown;\n updatedAt?: number;\n degraded?: boolean;\n error?: string;\n /** Present when the verify gate did not admit `done`. */\n pendingGate?: { gate: string; reason: string };\n recovery?: string[];\n}\n\n/** Run one shell command and capture {exitCode, outputDigest}. */\nfunction runCheck(cmd: string): { exitCode: number; digest: string } {\n // The CLI (user-invoked) owns shell access — the engine never shells out.\n // Synchronous spawn keeps `noir task verify` a single atomic step; failures\n // surface as non-zero exit codes (the evidence), never a thrown crash.\n try {\n const buf = spawnSync('sh', ['-c', cmd], {\n stdio: ['ignore', 'pipe', 'pipe'],\n encoding: 'utf8',\n });\n let out = '';\n if (buf.stdout) out += buf.stdout.toString();\n if (buf.stderr) out += buf.stderr.toString();\n const status = buf.status;\n return {\n exitCode: typeof status === 'number' ? status : 1,\n digest: sha256Hex(out),\n };\n } catch {\n return { exitCode: 1, digest: sha256Hex('') };\n }\n}\n\nexport async function taskVerify(opts: TaskVerifyOptions): Promise<void> {\n // Resolve the check set from the project config (workflow.gate.verify.checks).\n // No checks resolvable → exit 2 (USAGE) rather than inventing commands.\n // A throw from loadProjectInfo on an uninitialized project must route through\n // fail() — otherwise under --json stdout stays EMPTY (the raw error only\n // reaches stderr), violating the `{ok:false}` envelope contract.\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n const cfgChecks = project.config.workflow?.gate?.verify?.checks ?? [];\n const all: VerifyCheck[] = cfgChecks.map((c) => ({\n name: c.name,\n command: c.command,\n tier: (c.tier ?? 'hard') as 'hard' | 'soft',\n }));\n let checks = all;\n if (opts.check && opts.check.length > 0) {\n const want = new Set(opts.check);\n checks = all.filter((c) => want.has(c.name));\n if (checks.length === 0) {\n fail(\n EXIT.USAGE,\n `task verify: no configured checks match --check ${[...want].join(',')}`,\n opts,\n );\n }\n }\n if (checks.length === 0) {\n fail(\n EXIT.USAGE,\n 'task verify: no verify checks configured (set workflow.gate.verify.checks in .noir/config.yml)',\n opts,\n );\n }\n\n // Run each check, capture evidence.\n const ranAt = Date.now();\n const evidence = {\n ranAt,\n checks: checks.map((c) => {\n const r = runCheck(c.command);\n return {\n name: c.name,\n exitCode: r.exitCode,\n outputDigest: r.digest,\n command: c.command,\n tier: c.tier,\n };\n }),\n summary: '',\n };\n const passed = evidence.checks.filter((c) => c.exitCode === 0).length;\n const failed = evidence.checks.length - passed;\n evidence.summary = `${passed} passed, ${failed} failed`;\n\n // Submit evidence to workflow_advance (targets the active task → verify gate).\n const res = await callDaemonTool<WorkflowAdvanceResult>(opts, 'workflow_advance', { evidence });\n if (res.ok === true) {\n if (opts.json === true) {\n // `{ok:true, data}` — evidence nests INSIDE the payload (no ad-hoc\n // top-level sibling keys), matching every other command's envelope shape.\n process.stdout.write(`${JSON.stringify({ ok: true, data: { ...res, evidence } })}\\n`);\n return;\n }\n // Only a landing at `done` means the verify gate FIRED. If the advance\n // landed elsewhere (e.g. from `executing` → `verifying` before the engine's\n // evidence guard was added), be honest about it rather than claiming success.\n const landed = (res as WorkflowStatusResult).state;\n if (landed === 'done') {\n success(`verify — ${evidence.summary} → gate approved`, opts);\n } else {\n info(\n `verify — ${evidence.summary} → advanced to ${landed} (the verify gate fires at \\`done\\`; run \\`noir task verify\\` again when there)`,\n opts,\n );\n }\n renderStatusRow(res as WorkflowStatusResult, opts);\n return;\n }\n // Pending / failed: a failing verify gate is a FAILURE (exit non-zero), never\n // a silent exit-0 \"success\". Under --json emit the canonical {ok:false,error}\n // envelope (not a flattened `{evidence, ...res}`), preserving pendingGate +\n // recovery + evidence for consumers.\n const detail = res.error ?? res.pendingGate?.reason ?? 'verify failed';\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({\n ok: false,\n error: { code: EXIT.ERROR, message: `task verify: ${detail}` },\n // `{ok:false, error}` + a payload — recovery context + evidence nest\n // under `data`, not as top-level siblings (same envelope shape as the\n // success path; consumers read `data` for the full picture).\n data: {\n ...(res.pendingGate !== undefined ? { pendingGate: res.pendingGate } : {}),\n ...(res.recovery !== undefined ? { recovery: res.recovery } : {}),\n evidence,\n },\n })}\\n`,\n );\n process.exitCode = EXIT.ERROR;\n return;\n }\n if (res.pendingGate) {\n warn(`verify gate: ${res.pendingGate.reason} (${evidence.summary})`, opts);\n const recovery = res.recovery ?? ['retry', 'force', 'skip', 'block'];\n info(\n `recovery: ${recovery\n .map((r) =>\n r === 'retry'\n ? '`noir task verify`'\n : r === 'force'\n ? '`noir task advance --force <reason>`'\n : r === 'skip'\n ? '`noir task advance --skip`'\n : r === 'block'\n ? '`noir task block <reason>`'\n : `\\`${r}\\``,\n )\n .join(' | ')}`,\n opts,\n );\n }\n fail(EXIT.ERROR, `task verify: ${detail}`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir task research [<id>]` — list research findings\n// `noir task research record --type <t> --text \"...\" [--source <ref>] [--task <id>]`\n// → workflow_research_record\n// ---------------------------------------------------------------------------\nexport interface TaskResearchOptions extends TaskOptions {\n id?: string;\n}\n\nexport async function taskResearch(opts: TaskResearchOptions): Promise<void> {\n // List findings by reading the active task's status (which carries history\n // — the research records live in the engine's readResearch KV). For now,\n // surface through the daemon: call workflow_status and report the task's\n // Show status, then note that `noir task research record` writes findings.\n const s = await fetchStatus(opts, opts.id);\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: s })}\\n`);\n return;\n }\n renderStatusRow(s, opts);\n info('research findings are recorded via `noir task research record` (list + record)', opts);\n}\n\nexport interface TaskResearchRecordOptions extends TaskOptions {\n type: string;\n text: string;\n source?: string;\n task?: string;\n}\n\nexport async function taskResearchRecord(opts: TaskResearchRecordOptions): Promise<void> {\n const TYPES = ['assumption', 'discovery', 'decision', 'grounding-fact'];\n if (!TYPES.includes(opts.type)) {\n fail(\n EXIT.USAGE,\n `task research record: invalid type '${opts.type}' (expected one of: ${TYPES.join(', ')})`,\n opts,\n );\n }\n const args: Record<string, unknown> = { type: opts.type, text: opts.text };\n if (typeof opts.source === 'string' && opts.source.length > 0) args.source = opts.source;\n if (typeof opts.task === 'string' && opts.task.length > 0) args.taskId = opts.task;\n const res = await callDaemonTool<{\n ok: boolean;\n taskId?: string;\n entry?: unknown;\n error?: string;\n }>(opts, 'workflow_research_record', args);\n if (res.ok !== true) {\n const detail =\n typeof res.error === 'string' && res.error.length > 0 ? res.error : 'record failed';\n fail(EXIT.ERROR, `task research record: ${detail}`, opts);\n }\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: res })}\\n`);\n return;\n }\n success(\n `research recorded → ${opts.type}: ${opts.text.slice(0, 80)}${opts.text.length > 80 ? '…' : ''}`,\n opts,\n );\n}\n// ---------------------------------------------------------------------------\n// `noir task decompose <capability-id>` → draft a SlicePlan\n// ---------------------------------------------------------------------------\nexport interface TaskDecomposeOptions extends TaskOptions {\n capability: string;\n out?: string;\n}\n\nexport async function taskDecompose(opts: TaskDecomposeOptions): Promise<void> {\n const cap = opts.capability.trim();\n if (cap.length === 0) fail(EXIT.USAGE, 'task decompose: <capability-id> is required', opts);\n\n // Template SlicePlan (offline — mirrors draftPrd: single-shot, no provider\n // needed for a template, null/empty on no-provider). A provider-backed drafting\n // pass would fill richer fields; the offline template gives a valid skeleton.\n const plan = {\n capabilityId: cap,\n intent: `Capability ${cap} — offline template (run with a provider for an LLM-drafted plan)`,\n slices: [\n {\n id: 's1-walking-skeleton',\n title: 'Walking skeleton — thinnest end-to-end',\n type: 'feature' as const,\n rationale: `The first slice for capability ${cap}.`,\n scopeIn: 'End-to-end path linking the main architecture.',\n scopeOut: 'Polish, edge cases, full coverage.',\n dependsOn: [] as { id: string; mode: 'sequential' | 'parallel' }[],\n files: { create: [], modify: [], preserve: [] },\n acceptance: [`verify acceptance criteria for capability ${cap}`],\n doD: ['pnpm test', 'pnpm typecheck'],\n rollbackPlan: { procedure: 'revert the slice commit', verifyCommand: 'pnpm test' },\n },\n ],\n status: { 's1-walking-skeleton': 'planned' as const },\n };\n\n // `--out <path>`: persist the plan JSON (a silent no-op before — the flag was\n // declared + forwarded but never acted on).\n if (typeof opts.out === 'string' && opts.out.length > 0) {\n try {\n writeFileSync(opts.out, `${JSON.stringify(plan, null, 2)}\\n`, 'utf8');\n } catch (err) {\n fail(\n EXIT.ERROR,\n `task decompose: could not write ${opts.out}: ${err instanceof Error ? err.message : String(err)}`,\n opts,\n );\n }\n if (opts.json !== true) info(`wrote plan to ${opts.out}`, opts);\n }\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: plan })}\\n`);\n return;\n }\n log(`decompose — ${cap}`, opts);\n for (const s of plan.slices) {\n info(`${s.id}: ${s.title} (${s.type}) — rationale: ${s.rationale}`, opts);\n }\n tip('each piece enters the existing clarify→spec→plan→execute→verify→document workflow', opts);\n}\n","// `noir handoff` (alias `noir wrap`).\n//\n// The on-demand, pasteable HOST HANDOFF artifact that closes Noir's internal\n// loop and points the user at the right host CLI. Reuses the SAME aggregation\n// path as `noir status` (`gatherStatusPayload`) and the SAME phase→skill map as\n// `noir task` (`PHASE_SKILL` / `skillFor`), then does a BOUNDED in-process\n// `context_search` + `memory_recall` for the active task's domain to seed the\n// host prompt, and renders a structured MARKDOWN block to STDOUT for pasteability.\n//\n// DOCTRINE (hard constraint): Noir NEVER launches the host and NEVER writes the\n// artifact into CLAUDE.md/AGENTS.md (claude byte-parity risk + the double-`@`-\n// import hazard the manifest already fixed). The host-launch directive is TEXT\n// ONLY — `hostLaunchDirective(host)` / `HostAdapter.emitHandoff` produce the\n// \"Open `<host>` …\" wording; `noir handoff` never spawns anything.\n//\n// Graceful degradation (mirrors `status.ts`): a down daemon is reported with an\n// honest note (\"start `noir daemon start` for live task/memory\") and the\n// artifact still renders from in-process `.noir/` reads (project info + a\n// workflow snapshot if locally available) — exit 0. A missing embedder during\n// the bounded extraction folds to `null` (via `tryTool`); the seed section\n// renders a \"degraded\" note instead of failing.\n//\n// Stream discipline: the markdown artifact → STDOUT ONLY (the single\n// stdout write under the default path); `--json` emits the structured payload\n// via the versioned `{ok,data}` envelope instead; ALL diagnostics (the\n// \"wrote <path>\" confirmation, the daemon-down note) → STDERR via `info()`.\n// `--write` persists to `.noir/handoff/HO-<NNNN>-<id>.md` (gitignored — see\n// `@noir-ai/core` `syncIgnores`); the path is reported on stderr, never stdout.\n\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n defaultHandoffBlock,\n type EmitContext,\n type HandoffPayload,\n type HostId,\n hostLaunchDirective,\n resolveAdapter,\n} from '@noir-ai/adapters';\nimport { loadProjectInfo, NOIR_DIR, type ProjectInfo, resolveArtifactPath } from '@noir-ai/core';\nimport {\n type DaemonClientOptions,\n type DaemonProbe,\n probeDaemon,\n withRunningDaemon,\n} from '../daemon-client.js';\nimport { type CliOptions, EXIT, fail, info } from '../output.js';\nimport { gatherStatusPayload, tryTool } from './status.js';\nimport { PHASE_SKILL, skillFor } from './task.js';\n\n/** Options accepted by `noir handoff` (globals + daemon-client knobs). */\nexport interface HandoffOptions extends CliOptions, DaemonClientOptions {\n /** `--write`: persist the artifact to `.noir/handoff/HO-<NNNN>-<id>.md` (gitignored). */\n write?: boolean;\n}\n\n/** Bounded extraction limits — small enough to seed a prompt, never a dump. */\nconst SEED_LIMIT = 5;\n\n// ---------------------------------------------------------------------------\n// Wire result shapes (slices of the daemon payloads; local types — the CLI\n// depends only on the MCP wire contract, mirrors status.ts / context.ts).\n// ---------------------------------------------------------------------------\ninterface ContextSearchHit {\n path?: string;\n score?: number;\n snippet?: string;\n source?: string;\n}\ninterface ContextSearchResult {\n ok?: boolean;\n results?: unknown;\n consumedTokens?: number;\n truncated?: boolean;\n degraded?: boolean;\n mode?: string;\n}\ninterface MemoryRecallHit {\n id?: string;\n observation?: Record<string, unknown>;\n score?: number;\n}\ninterface MemoryRecallResult {\n ok?: boolean;\n results?: unknown;\n degraded?: boolean;\n}\n\n/** A normalized context_search seed entry rendered in the artifact. */\nexport interface HandoffContextHit {\n path: string;\n score: number;\n snippet: string;\n}\n/** A normalized memory_recall seed entry rendered in the artifact. */\nexport interface HandoffMemoryHit {\n id: string;\n summary: string;\n}\n\n/** The structured payload emitted by `--json` (and the source for the markdown\n * render). Every nullable field degrades cleanly (daemon-down / no active task\n * / missing embedder → null + a note), so the artifact NEVER hard-fails. */\nexport interface HandoffPayloadFull {\n project: { id: string; name: string };\n host: HostId;\n /** Host-launch directive text (single source: `hostLaunchDirective` or the\n * adapter's `emitHandoff` override). TEXT ONLY — never a spawn. */\n directive: string;\n task: {\n taskId: string;\n phase: string;\n nextGate: string | null;\n nextSkill: string | null;\n } | null;\n /** Bounded context_search seed (top hits for the task domain). Null when the\n * daemon is down OR the embedder is missing (degraded note rendered). */\n contextSeed: HandoffContextHit[] | null;\n /** Bounded memory_recall seed. Null when the daemon is down. */\n memorySeed: HandoffMemoryHit[] | null;\n /** Honest degradation flags so a `--json` consumer can branch. */\n degraded: {\n daemonDown: boolean;\n embedderMissing: boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Rendering — the markdown artifact (STDOUT) + the structured payload (--json).\n// ---------------------------------------------------------------------------\n\n/** Heading line: `# Noir handoff — <name> (<id>)`. */\nfunction heading(name: string, id: string): string {\n return `# Noir handoff — ${name} (${id})`;\n}\n\n/** Render the markdown artifact from the structured payload. STDOUT-safe (no\n * ANSI — the artifact is pasteable text; `--no-color` honored by virtue of\n * never coloring). */\nfunction renderMarkdown(p: HandoffPayloadFull): string {\n const lines: string[] = [];\n lines.push(heading(p.project.name, p.project.id));\n lines.push('');\n\n // Phase / gate line.\n if (p.task) {\n const gate = p.task.nextGate ?? '—';\n lines.push(`**Phase:** ${p.task.phase} — next gate: ${gate}`);\n } else {\n lines.push('**Phase:** no active task');\n }\n\n // Host directive (the pasteable \"open your host\" line).\n lines.push('');\n lines.push('## Open host');\n lines.push(p.directive);\n\n // Next step.\n lines.push('');\n lines.push('## Next step');\n if (p.task?.nextSkill) {\n lines.push(`Run \\`${p.task.nextSkill}\\` — the host's next gate skill.`);\n } else {\n lines.push('No further gate ahead (past verify, or no active task).');\n }\n\n // Extracted context seed.\n lines.push('');\n lines.push('## Extracted context (seed)');\n if (p.contextSeed && p.contextSeed.length > 0) {\n for (const h of p.contextSeed) {\n lines.push(`- \\`${h.path}\\` (score ${h.score.toFixed(4)})`);\n const snip = h.snippet.trim();\n if (snip.length > 0) lines.push(` > ${snip.replace(/\\n/g, '\\n > ')}`);\n }\n } else if (p.degraded.embedderMissing) {\n lines.push(\n '_Embedder unavailable — degraded to BM25-only or no hits. Start `noir daemon start` for live context._',\n );\n } else {\n lines.push('_No context hits (or daemon down). Start `noir daemon start` for live context._');\n }\n\n // Extracted memory seed.\n lines.push('');\n lines.push('## Extracted memory (seed)');\n if (p.memorySeed && p.memorySeed.length > 0) {\n for (const m of p.memorySeed) {\n lines.push(`- \\`${m.id}\\` — ${m.summary}`);\n }\n } else {\n lines.push(\n '_No memory observations (or daemon down). Start `noir daemon start` for live memory._',\n );\n }\n\n // Live data pointer.\n lines.push('');\n lines.push('## Live data — call these from the host (already wired via the host MCP config)');\n lines.push('- `noir.workflow_status` · `noir.context_search` · `noir.memory_recall`');\n\n // Daemon-down note (honest degradation, mirrors status.ts).\n if (p.degraded.daemonDown) {\n lines.push('');\n lines.push('> **Note:** daemon not running — start `noir daemon start` for live task/memory.');\n }\n\n lines.push('');\n return lines.join('\\n');\n}\n\n/** Normalize a raw context_search hit into the render shape. */\nfunction toContextHit(raw: unknown): HandoffContextHit {\n const h = (raw ?? {}) as ContextSearchHit;\n return {\n path: typeof h.path === 'string' ? h.path : '<unknown>',\n score: typeof h.score === 'number' ? h.score : 0,\n snippet: typeof h.snippet === 'string' ? h.snippet : '',\n };\n}\n\n/** Coerce a memory_recall observation's display summary from a raw hit. */\nfunction observationSummary(raw: MemoryRecallHit): string {\n const obs = raw.observation;\n if (obs && typeof obs === 'object') {\n const content = obs.content;\n if (typeof content === 'string' && content.length > 0) {\n return content.length > 160 ? `${content.slice(0, 157)}…` : content;\n }\n const type = obs.type;\n if (typeof type === 'string') return `(${type})`;\n }\n return '';\n}\n\n/** Normalize a raw memory_recall hit into the render shape. */\nfunction toMemoryHit(raw: unknown): HandoffMemoryHit {\n const h = (raw ?? {}) as MemoryRecallHit;\n return {\n id: typeof h.id === 'string' ? h.id : '<unknown>',\n summary: observationSummary(h),\n };\n}\n\n/** Build the host-facing {@link HandoffPayload} (the adapter hook's input) from\n * the full snapshot. Narrowed so the adapter never sees CLI-internal fields. */\nfunction toHostPayload(p: HandoffPayloadFull): HandoffPayload {\n return {\n project: p.project,\n host: p.host,\n task: p.task,\n };\n}\n\n/** Resolve the directive text: the adapter's `emitHandoff` override if present,\n * else the shared `defaultHandoffBlock` (which composes `hostLaunchDirective`).\n * Routes via `resolveAdapter` (no CLI branching — adding a host needs no CLI\n * edit, mirroring the host-adapter seam). */\nfunction resolveDirective(root: string, p: HandoffPayloadFull): string {\n const ctx: EmitContext = { root };\n const adapter = resolveAdapter(p.host);\n const hostPayload = toHostPayload(p);\n if (typeof adapter.emitHandoff === 'function') {\n return adapter.emitHandoff(ctx, hostPayload);\n }\n return defaultHandoffBlock(ctx, hostPayload);\n}\n\n/** Derive the on-disk filename for `--write`: `.noir/handoff/HO-<NNNN>-<id>.md`.\n * The id is the active task id when available, else the project id (stable +\n * unique per project). Reuses the existing artifact on rewrite. The path is\n * gitignored via `syncIgnores`. */\nfunction handoffFilePath(root: string, taskId: string | undefined, projectId: string): string {\n const id = typeof taskId === 'string' && taskId.length > 0 ? taskId : projectId;\n return resolveArtifactPath(root, 'handoff', { taskId: id });\n}\n\n// ---------------------------------------------------------------------------\n// Bounded extraction — `context_search` + `memory_recall` for the active task's\n// domain. Uses `withRunningDaemon` (probe-only — NEVER starts a daemon) so a\n// down daemon degrades to `null` (exit 0) instead of exit 4. A missing embedder\n// throws inside the tool call → `tryTool` folds to `null` → \"degraded\" note.\n// ---------------------------------------------------------------------------\n\n/** Run the bounded extraction over one daemon connection (if the daemon is up).\n * Returns `{context, memory}` where each is `null` on any failure path. The\n * `query` is the active task id (the task's domain); when no task is active,\n * the project name is used as a stable fallback. */\nasync function extractSeeds(\n opts: HandoffOptions,\n probe: DaemonProbe,\n query: string,\n): Promise<{ context: HandoffContextHit[] | null; memory: HandoffMemoryHit[] | null }> {\n // Guard: if the probe already said down, skip the connection entirely (the\n // artifact renders with a \"start daemon\" note — exit 0).\n if (!probe.running) return { context: null, memory: null };\n\n let context: HandoffContextHit[] | null = null;\n let memory: HandoffMemoryHit[] | null = null;\n\n await withRunningDaemon(\n opts,\n async (caller): Promise<void> => {\n // BOUNDED: small `limit` + a bounded `query` (the task id / project name)\n // on both tools — a seed, never a dump. tryTool folds ANY failure (missing\n // engine, embedder throw, transport hiccup) to null so the artifact never\n // hard-fails on extraction.\n const ctxRes = await tryTool<ContextSearchResult>(caller, 'context_search', {\n query,\n limit: SEED_LIMIT,\n });\n if (ctxRes && ctxRes.ok !== false && Array.isArray(ctxRes.results)) {\n context = (ctxRes.results as unknown[])\n .slice(0, SEED_LIMIT)\n .map(toContextHit)\n .filter((h) => h.path !== '<unknown>' || h.snippet.length > 0);\n }\n const memRes = await tryTool<MemoryRecallResult>(caller, 'memory_recall', {\n query,\n limit: SEED_LIMIT,\n });\n if (memRes && memRes.ok !== false && Array.isArray(memRes.results)) {\n memory = (memRes.results as unknown[])\n .slice(0, SEED_LIMIT)\n .map(toMemoryHit)\n .filter((h) => h.summary.length > 0);\n }\n },\n probe,\n );\n\n return { context, memory };\n}\n\n/** Probe helper — re-probe (gatherStatusPayload already probed once, but we need\n * the probe object here for the bounded extraction). Mirrors status.ts: never\n * starts a daemon, returns `{running:false}` on any miss. Static import matches\n * status.ts — vi.mock hoisting makes the mock apply to static imports too, so a\n * dynamic import is unnecessary (and it would trigger tsup code-splitting, which\n * re-chunks the CLI bin entry and breaks its `isMainModule` guard). */\nasync function probeOnly(opts: HandoffOptions): Promise<DaemonProbe> {\n return probeDaemon(opts);\n}\n\n// ---------------------------------------------------------------------------\n// The command.\n// ---------------------------------------------------------------------------\n\n/**\n * `noir handoff` (alias `noir wrap`): gather the live snapshot + a bounded\n * context/memory seed and render a pasteable host-handoff artifact.\n *\n * - Default: markdown → STDOUT (the single stdout write).\n * - `--write`: persist to `.noir/handoff/HO-<NNNN>-<id>.md` (gitignored); confirm on stderr.\n * - `--json`: emit the structured `{ok:true, data: HandoffPayloadFull}` envelope.\n *\n * Never hard-fails on a down daemon or a missing embedder — degrades to a note\n * (mirrors `status.ts`). Uninitialized project → exit 1 from `loadProjectInfo`.\n */\nexport async function handoff(opts: HandoffOptions): Promise<void> {\n // In-process project info — no daemon round-trip. Uninitialized → exit 1\n // (same as status / every other command). Route through fail() so --json\n // emits the canonical {ok:false,error} envelope (not empty stdout).\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(process.cwd());\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n const host = project.config.host;\n\n // Snapshot — the SAME path `noir status` uses (single source). gatherStatusPayload\n // probes + folds every optional engine to null. We re-probe here for the extraction\n // step (the probe is cheap — one GET /health).\n const snapshot = await gatherStatusPayload(opts);\n const probe = await probeOnly(opts);\n\n // Active task + next-skill (reuse task.ts's PHASE_SKILL — single source).\n const task = snapshot.workflow\n ? {\n taskId: snapshot.workflow.taskId,\n phase: snapshot.workflow.phase,\n nextGate: snapshot.workflow.nextGate,\n nextSkill: skillFor(snapshot.workflow.nextGate) ?? skillFor(snapshot.workflow.phase),\n }\n : null;\n\n // Bounded extraction — the query is the task id (the task's domain), else the\n // project name as a stable fallback.\n const query = task?.taskId ?? project.name;\n const { context, memory } = await extractSeeds(opts, probe, query);\n\n const full: HandoffPayloadFull = {\n project: { id: project.id, name: project.name },\n host,\n directive: '', // filled after the host payload is assembled (below)\n task,\n contextSeed: context,\n memorySeed: memory,\n degraded: {\n daemonDown: !probe.running,\n embedderMissing: context === null && probe.running,\n },\n };\n full.directive = resolveDirective(project.root, full);\n\n // --write persists the markdown artifact BEFORE the --json early-return so\n // `--write --json` writes the file too (previously --json silently skipped it).\n if (opts.write === true) {\n const md = renderMarkdown(full);\n const path = handoffFilePath(project.root, task?.taskId, project.id);\n mkdirSync(join(project.root, NOIR_DIR, 'handoff'), { recursive: true });\n writeFileSync(path, md, 'utf8');\n // Diagnostic → STDERR (stream discipline: stdout is reserved for the\n // artifact/envelope; --write reports its path here, never on stdout).\n info(`handoff written to ${path}`, opts);\n }\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: full })}\\n`);\n return;\n }\n\n // Default: markdown → STDOUT ONLY (pasteability — the user pipes this into a\n // file or straight into the host). No trailing diagnostics on stdout. When\n // --write was set (and not --json), the file is already persisted and its\n // path reported to stderr — do not ALSO dump the artifact to stdout.\n if (opts.write !== true) process.stdout.write(renderMarkdown(full));\n}\n\n// Re-export so bin.ts can wire `noir wrap` as a sibling alias dispatching the\n// same handler, and so PHASE_SKILL stays usable from the test surface.\nexport { hostLaunchDirective, PHASE_SKILL };\n","// Noir banner.\n//\n// Pre-rendered \"noir\" ASCII wordmark (figlet \"ANSI Shadow\", generated offline\n// and VERIFIED letter-by-letter to read N-O-I-R — an earlier hand-rolled\n// version had a malformed R that rendered as \"NOHA\") with a SMOOTH gradient via\n// gradient-string. (A per-row rainbow was garish; a smooth vertical gradient is\n// the modern look — cf. the GitHub Copilot CLI banner engineering post.)\n//\n// Guardrails: only the interactive home arm calls this (isInteractive already\n// gates TTY/CI/NO_COLOR/--json/--no-input); shouldShowBanner additionally\n// skips --quiet and NOIR_NO_BANNER. color:false → zero ANSI for snapshots/CI.\n// Responsive: ≥50 cols → block wordmark; <50 → compact `◆ noir` mark.\nimport gradient from 'gradient-string';\nimport type { CliOptions } from './output.js';\nimport { accessibleMode, c, useColor } from './theme.js';\n\n// figlet \"ANSI Shadow\" — regenerated 2026-07-26 from the standard per-letter\n// glyphs and verified (N, O, I, R) to read \"NOIR\". Do NOT hand-edit — regenerate\n// via `figlet -f \"ANSI Shadow\" NOIR` if a glyph changes.\nconst NOIR_BLOCK = [\n '███╗ ██╗ ██████╗ ██╗ ██████╗ ',\n '████╗ ██║ ██╔═══██╗ ██║ ██╔══██╗',\n '██╔██╗ ██║ ██║ ██║ ██║ ██████╔╝',\n '██║╚██╗██║ ██║ ██║ ██║ ██╔══██║',\n '██║ ╚████║ ╚██████╔╝ ██║ ██║ ██║',\n '╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝',\n];\n\n// \"noir\" aesthetic — Midnight Cobalt: a smooth gradient-string gradient (dark\n// cobalt → bright blue → sky) applied per-character across the wordmark\n// (vertical, top → bottom). All stops are bright enough to read on a dark\n// terminal; the deep-blue start evokes \"midnight\" without disappearing.\nconst NOIR_GRADIENT = gradient('#2c5282', '#3b82f6', '#7dd3fc');\n\n/** One-line product tagline (shown under the wordmark). */\nexport const NOIR_TAGLINE = 'discipline, context, and memory layer for agentic CLIs';\n\ninterface BannerOptions {\n /** Terminal width in columns (defaults to process.stdout.columns, else 80). */\n width?: number;\n /** Apply the gradient. Default true. */\n color?: boolean;\n}\n\n/**\n * Render the Noir banner. Wide terminals (≥50 cols) get the full block\n * wordmark; narrow terminals get a compact `◆ noir` mark.\n *\n * Color gating: decoration is on only when the caller did not pass\n * `color:false` AND `useColor()` agrees (honoring NO_COLOR / CLICOLOR_FORCE /\n * TTY uniformly via the theme — the single color authority). Under\n * `NOIR_ACCESSIBLE` the gradient is replaced with a solid accent render\n * (maximum legibility); the wordmark stays recognizable either way.\n */\nexport function renderBanner(opts: BannerOptions = {}): string {\n const width = opts.width ?? process.stdout.columns ?? 80;\n const colorOn = opts.color !== false && useColor();\n if (width < 50) {\n return colorOn ? `${c.accent('◆')} noir` : '◆ noir';\n }\n const block = NOIR_BLOCK.join('\\n');\n if (!colorOn) return block;\n // Accessible mode: skip the multi-stop gradient in favor of a single solid\n // accent color (still the Midnight Cobalt blue role, just without the\n // per-character hue drift that can be harder to read).\n if (accessibleMode()) {\n return block\n .split('\\n')\n .map((line) => c.accent(line))\n .join('\\n');\n }\n return NOIR_GRADIENT.multiline(block);\n}\n\n/**\n * Gate: skip the banner under `--quiet` / `--json` or `NOIR_NO_BANNER`. The TTY\n * / CI / NO_COLOR guards are already handled by `isInteractive` (the only path\n * that calls the banner is the interactive home arm); this adds the\n * banner-specific opt-outs a user can set even inside an interactive session.\n */\nexport function shouldShowBanner(opts: CliOptions): boolean {\n if (opts.quiet === true || opts.json === true) return false;\n const v = process.env.NOIR_NO_BANNER;\n if (v !== undefined && v !== '') return false;\n return true;\n}\n","// Derive {@link PaletteCommand} entries from a commander program.\n//\n// The TUI command palette needs a flat, searchable list of the `noir`\n// subcommands a user can dispatch. Rather than maintain a second hand-curated\n// command table (which would drift from the real tree in `bin.ts`), this module\n// walks the commander tree at palette-build time and projects each LEAF command\n// into a {@link PaletteCommand}.\n//\n// Leaf contract (mirrors the palette's intent — only dispatchable commands\n// surface, never bare groups):\n// - has an action handler (`.action()` was called), AND\n// - has no child commands.\n// Groups — which own children and carry a usage-throwing fallback action in the\n// real CLI — are excluded by the \"no children\" clause regardless of their action\n// state. The program root name ('noir' / '') is never part of the argv path.\n\nimport type { Command } from 'commander';\nimport type { PaletteCommand } from '../palette/types.js';\n\n/**\n * argv-prefix table for commands that mutate the store / workflow / host\n * artifacts. The palette uses this to require confirmation before dispatch.\n * A prefix matches when `commandArgv` starts with every token of the entry\n * (e.g. `['context', 'index']` matches the `context index` leaf).\n */\nconst DESTRUCTIVE_PREFIXES: readonly (readonly string[])[] = [\n ['context', 'index'],\n ['memory', 'save'],\n ['memory', 'forget'],\n ['memory', 'consolidate'],\n ['task', 'new'],\n ['task', 'advance'],\n ['skills', 'sync'],\n ['daemon', 'start'],\n ['daemon', 'stop'],\n ['daemon', 'restart'],\n ['init'],\n ['create'],\n ['sync'],\n ['install'],\n ['update'],\n ['run'],\n];\n\n/**\n * True when `argv` matches one of the {@link DESTRUCTIVE_PREFIXES}. A prefix\n * matches iff `argv` is at least as long as the prefix and every prefix token\n * equals the corresponding argv token — so `['context']` does NOT match the\n * `['context', 'index']` prefix (a bare group isn't destructive), but\n * `['context', 'index']` does.\n */\nexport function isDestructive(argv: readonly string[]): boolean {\n return DESTRUCTIVE_PREFIXES.some((prefix) => {\n if (argv.length < prefix.length) return false;\n return prefix.every((tok, i) => argv[i] === tok);\n });\n}\n\n/**\n * The prompt an argv carries, when it is a plain `run <prompt>` the run screen\n * can take over, or `null` when it is not. A run with a flag (`--profile`,\n * `--json`, `--list-profiles`) stays on the ordinary dispatch path: those are\n * one-shot invocations with their own output discipline, not the live screen.\n */\nexport function runPromptFrom(argv: readonly string[]): string | null {\n if (argv[0] !== 'run') return null;\n const rest = argv.slice(1);\n if (rest.length === 0) return null; // bare `run` is a usage error, reported by the CLI\n if (rest.some((token) => token.startsWith('-'))) return null;\n const prompt = rest.join(' ').trim();\n return prompt.length > 0 ? prompt : null;\n}\n\n/** Capitalize the first character of `s`, leaving the rest untouched. */\nfunction capitalizeFirst(s: string): string {\n if (s.length === 0) return s;\n return `${s[0]?.toUpperCase() ?? ''}${s.slice(1)}`;\n}\n\n/**\n * Commander keeps the registered action handler on a private `_actionHandler`\n * field (no public accessor exists). This narrow cast reads only that field so\n * we can tell \"a dispatchable command\" apart from \"a bare group container\".\n */\ntype CommandWithAction = Command & { _actionHandler: ((...args: unknown[]) => unknown) | null };\n\n/** True when commander has an `.action()` handler registered on `cmd`. */\nfunction hasAction(cmd: Command): boolean {\n return (cmd as CommandWithAction)._actionHandler != null;\n}\n\n/**\n * The label of the value `cmd` cannot run without, or undefined when it runs\n * bare. Commander already tracks this: an argument declared as `<name>` is\n * required, `[name]` is optional, and a variadic `<name...>` still needs at\n * least one value. Reading that flag keeps the palette aligned with the CLI\n * definition instead of a second, hand-maintained list of commands.\n */\nfunction requiredArgLabel(cmd: Command): string | undefined {\n const required = cmd.registeredArguments.find((arg) => arg.required);\n return required?.name();\n}\n\n/**\n * Walk `cmd`'s subtree depth-first, appending one {@link PaletteCommand} per\n * leaf (has an action handler AND no child commands). `path` accumulates the\n * argv tokens excluding the program root name; the caller seeds it as `[]` so\n * the root contributes nothing.\n */\nfunction collectLeaves(cmd: Command, path: string[], out: PaletteCommand[]): void {\n const children = cmd.commands;\n const isLeaf = hasAction(cmd) && children.length === 0;\n\n if (isLeaf) {\n const argv = path;\n const id = argv.join(' ');\n const description = cmd.description() || argv.join(' ');\n out.push({\n id,\n label: argv.map(capitalizeFirst).join(': '),\n argv,\n description,\n category: argv[0] ?? '',\n keywords: argv,\n destructive: isDestructive(argv),\n needsArg: requiredArgLabel(cmd),\n });\n return;\n }\n\n // Not a leaf — recurse into each child, extending the path with its name.\n // A child `name()` is always a non-empty token (commander rejects empty\n // command names), so `path` only grows with real argv segments.\n for (const child of children) {\n collectLeaves(child, [...path, child.name()], out);\n }\n}\n\n/**\n * Derive the palette entries for `program`: one {@link PaletteCommand} per leaf\n * subcommand, in commander's registration order. The program root name\n * ('noir' / '') is never part of any entry's argv.\n */\nexport function buildPaletteCommands(program: Command): PaletteCommand[] {\n const out: PaletteCommand[] = [];\n // Seed the path empty so the root's own name is excluded; its children become\n // the top-level argv segments (e.g. `context`, `doctor`, `init`).\n collectLeaves(program, [], out);\n return out;\n}\n","// The shared, React-free curated-section module.\n//\n// The bare `noir` home menu (packages/cli/src/commands/home.ts) and the Ink TUI\n// palette's `commands` corpus (App.tsx → palette/rows.ts) both render a curated\n// set of quick actions. This module is the SINGLE source of that curation so\n// the two renderers cannot drift — the same one-table-two-renderers idea that\n// keeps the TUI palette aligned with the commander tree.\n//\n// No-drift contract: every {@link HomeAction} references a palette-registry\n// `id` (e.g. 'context search'), NOT a hand-written argv. {@link resolveSections}\n// re-derives the live palette commands and drops any action whose id no longer\n// exists — so a future commander change degrades the menu to what exists\n// instead of crashing it.\n//\n// This module is deliberately React-free (no ink import) so `home.ts` (the lean\n// @clack path) can consume it without pulling React/Ink into the main CLI graph\n// (NF6 lazy-import constraint). Callers pass the live palette commands\n// explicitly (bin.ts injects them via HomeDeps / TuiDeps), so this module has\n// no import edge back to bin.ts (home.ts must stay circular-import-free).\n\nimport type { PaletteCommand } from '../palette/types.js';\n\n/** A single curated quick action: friendly copy + a palette-registry id. */\nexport interface HomeAction {\n /** The palette-registry id this action runs (e.g. `context search`). */\n readonly id: string;\n /** Friendly title-cased label for the menu (may differ from the leaf). */\n readonly label: string;\n /** One-line dim hint shown under/near the label (exact subcommand + purpose). */\n readonly hint: string;\n /**\n * True when this action mutates the store / workflow / host artifacts (the\n * same prefix table the TUI palette uses). The menu gates it behind a confirm\n * before dispatch.\n */\n readonly destructive?: boolean;\n /**\n * When set, the action collects this argument inline (via clack.text in the\n * menu, or the palette's inline input) before dispatch. Mirrors the existing\n * recall-query pattern in home.ts.\n */\n readonly needsArg?: {\n /**\n * Short name of the collected value ('query', 'content'), shown as the\n * palette's input placeholder. Set it when the value is not a plain\n * positional argument — a flag-carried value, or a prompt the CLI accepts\n * but does not require — so the palette still knows to ask for it.\n */\n readonly label?: string;\n readonly prompt: string;\n readonly placeholder: string;\n };\n /**\n * The exact argv to dispatch. Defaults to the palette entry's `argv`; a\n * `needsArg` action appends the collected value. Always resolves through the\n * registry so it cannot drift.\n */\n readonly dispatch?: readonly string[];\n}\n\n/** A named group of quick actions shown as one home-menu section. */\nexport interface HomeSection {\n /** Stable id (used as a section header + the select value). */\n readonly id: string;\n /** Section title, e.g. 'Status & context'. */\n readonly label: string;\n /** One-line dim hint for the section (its subcommands). */\n readonly hint: string;\n /** Ordered quick actions in this section. */\n readonly items: readonly HomeAction[];\n}\n\n/**\n * The curated home sections, in display order. Each action's `id` must exist in\n * the palette registry (verified by {@link resolveSections}); the friendly copy\n * is authored here once and rendered by both surfaces.\n */\nexport const HOME_SECTIONS: readonly HomeSection[] = [\n {\n id: 'status',\n label: 'Status & context',\n hint: 'snapshot · index · search · doctor',\n items: [\n {\n id: 'status',\n label: 'Status',\n hint: 'project + daemon + workflow + store snapshot (noir status)',\n },\n {\n id: 'context index',\n label: 'Index project',\n hint: '(re)index files into context · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'context search',\n label: 'Search context',\n hint: 'hybrid search over indexed files',\n needsArg: { prompt: 'Context search query:', placeholder: 'e.g. auth flow, deploy steps' },\n },\n {\n id: 'doctor',\n label: 'Doctor',\n hint: 'environment + project health checks',\n },\n {\n id: 'context status',\n label: 'Context status',\n hint: 'index freshness + counts',\n },\n ],\n },\n {\n id: 'memory',\n label: 'Memory',\n hint: 'recall · save · sessions · forget · consolidate',\n items: [\n {\n id: 'memory recall',\n label: 'Recall memory',\n hint: 'search cross-session memory',\n needsArg: { prompt: 'Recall query:', placeholder: 'e.g. auth flow, deploy steps' },\n },\n {\n id: 'memory save',\n label: 'Save memory',\n hint: 'save an observation · ⚠ destructive',\n destructive: true,\n // The value travels as `--content <text>` (memory save declares no\n // positional), so the flag rides in the dispatch prefix and the\n // collected value is appended after it.\n dispatch: ['memory', 'save', '--content'],\n needsArg: {\n label: 'content',\n prompt: 'Memory content:',\n placeholder: 'what you want to remember',\n },\n },\n {\n id: 'memory sessions',\n label: 'Sessions',\n hint: 'list recent memory sessions',\n },\n {\n id: 'memory forget',\n label: 'Forget memory',\n hint: 'forget observations by id · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'memory consolidate',\n label: 'Consolidate',\n hint: 'fold observations into lessons · ⚠ destructive',\n destructive: true,\n },\n ],\n },\n {\n id: 'workflow',\n label: 'Workflow',\n hint: 'run · next · status · advance · handoff · wrap',\n items: [\n {\n id: 'run',\n label: 'Ask the host',\n hint: 'ask the host agent a question · ⚠ destructive',\n destructive: true,\n // The prompt is not a required argument (the command also takes flags\n // like --list-profiles), so the palette only learns to ask for it here.\n needsArg: {\n label: 'prompt',\n prompt: 'Prompt for the host:',\n placeholder: 'e.g. summarise the auth flow',\n },\n },\n {\n id: 'task next',\n label: 'Next task',\n hint: 'suggest next phase + applicable skill',\n },\n {\n id: 'task status',\n label: 'Task status',\n hint: 'active task status',\n },\n {\n id: 'task advance',\n label: 'Advance task',\n hint: 'advance to next phase · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'handoff',\n label: 'Handoff',\n hint: 'ready-to-paste host prompt',\n },\n {\n id: 'wrap',\n label: 'Wrap (end session)',\n hint: 'session-end handoff',\n dispatch: ['wrap', '--write'],\n },\n ],\n },\n {\n id: 'setup',\n label: 'Setup & maintenance',\n hint: 'init · create · sync · skills · install · update',\n items: [\n {\n id: 'init',\n label: 'Init',\n hint: 'scaffold Noir in this project · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'create',\n label: 'Create',\n hint: 'bootstrap in a new/empty dir · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'sync',\n label: 'Sync',\n hint: 're-emit managed files + skills · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'skills list',\n label: 'Skills list',\n hint: 'list installed skills',\n },\n {\n id: 'skills sync',\n label: 'Sync skills',\n hint: 're-emit builtin skills · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'update',\n label: 'Update',\n hint: 'check for a new version · ⚠ destructive',\n destructive: true,\n },\n {\n id: 'install',\n label: 'Install / migrate',\n hint: 'native install / migrate · ⚠ destructive',\n destructive: true,\n },\n ],\n },\n {\n id: 'dashboard',\n label: 'Dashboard (full-screen)',\n hint: 'noir tui · noir palette · home',\n items: [\n {\n id: 'tui',\n label: 'Dashboard (full-screen)',\n hint: 'live status · /command · Ctrl+K palette (noir tui)',\n },\n {\n id: 'palette',\n label: 'All commands (fuzzy palette)',\n hint: 'fuzzy search over every command (noir palette)',\n },\n ],\n },\n];\n\n/**\n * Resolve the curated sections against a LIVE palette command list. Drops any\n * action whose `id` does not exist (graceful degradation) and any section left\n * empty. `dispatch` defaults to the palette entry's argv.\n *\n * @param commands The live palette commands (from\n * `buildPaletteCommands(createProgram())`). bin.ts supplies them to both\n * home.ts and the TUI — this module stays free of a bin.ts import edge (and\n * of the load-time cycle a dynamic `import('../../bin.js')` would create,\n * since bin.ts imports home.ts which imports this module).\n */\nexport async function resolveSections(commands: readonly PaletteCommand[]): Promise<HomeSection[]> {\n const byId = new Map(commands.map((c) => [c.id, c]));\n const out: HomeSection[] = [];\n for (const section of HOME_SECTIONS) {\n const items: HomeAction[] = [];\n for (const action of section.items) {\n const entry = byId.get(action.id);\n if (!entry) continue; // id removed from the tree — degrade, don't crash\n items.push({\n ...action,\n // Resolve the dispatch argv from the registry (never hand-written).\n dispatch: action.dispatch ?? [...entry.argv],\n });\n }\n if (items.length === 0) continue; // section went empty — drop it\n out.push({ ...section, items });\n }\n return out;\n}\n","// `noir` (bare) home menu.\n//\n// The interactive entry point. Dispatch rules:\n// • interactive (TTY && !CI && !NO_COLOR && !--json && !--no-input) → the\n// @clack/prompts grouped home menu (intro + sections + quick actions),\n// dispatching the chosen action through the SAME commander program the bin\n// uses, so the menu automatically inherits those implementations as they\n// land.\n// • non-interactive + --json → run `status --json` (machine snapshot).\n// • non-interactive + no --json → run `status` (human snapshot). Safe now that\n// `status` is probe-only: it NEVER auto-starts a daemon and exits 0\n// even when the daemon is down, so bare `noir` in CI / a pipe is a useful\n// no-op snapshot instead of a noisy failure or a help dump.\n//\n// The interactive arm is a two-level grouped menu —\n// a section picker (select, arrow-navigable) then per-section action lists\n// (select with per-option hints). Navigation is smooth: Esc / backspace /\n// ← return to the section picker, → jumps to the next section, ← to the\n// previous, so the user can move across sections without re-entering level 1\n// each time. The section content comes from the SHARED React-free\n// {@link resolveSections} module (tui/commands/sections.ts), which references\n// palette-registry ids — so the menu cannot drift from the commander tree.\n// `deps.commands` is injected by bin.ts (the same list the TUI palette uses).\n//\n// Both levels use `clack.select` (NOT `selectKey`): `selectKey` is a\n// select-by-typed-letter prompt with no arrow/enter/esc handling, and its\n// `_track=false` value stays `undefined` until a letter is pressed — Enter\n// crashes with `Cannot read properties of undefined (reading 'label')` (the\n// 1.9.0 home-menu bug, fixed by `select` on @clack ≥1.7).\n//\n// `@clack/prompts` is imported lazily inside the interactive branch so the\n// non-interactive paths (the common CI/script case) never pay for it and never\n// touch stdin/stdout rendering (NF6). The menu never blocks when input is\n// disabled — `isInteractive` already gated that — and a Ctrl+C at any prompt\n// maps to exit 5 (CANCELLED) via `fail`.\n//\n// Decoupling: `home` takes its `dispatch` + `commands` as injected callbacks\n// (provided by bin.ts) so this module has NO import edge back to bin.ts (which\n// would be circular: bin imports home). That also makes the menu unit-testable\n// without commander — tests pass a fake callback + mock @clack.\n\nimport { type HostId, hostLaunchDirective } from '@noir-ai/adapters';\nimport {\n detectActiveMethod,\n type InstallRecord,\n latestVersionFromCache,\n loadProjectInfo,\n NOIR_VERSION,\n readInstallRecord,\n readUpdateCache,\n} from '@noir-ai/core';\nimport { NOIR_TAGLINE, renderBanner, shouldShowBanner } from '../banner.js';\nimport { type CliOptions, EXIT, fail, isInteractive } from '../output.js';\nimport { c } from '../theme.js';\nimport { isDestructive } from '../tui/commands/registry.js';\nimport { type HomeAction, type HomeSection, resolveSections } from '../tui/commands/sections.js';\nimport type { PaletteCommand } from '../tui/palette/types.js';\nimport { buildUpdateNotice, DEFAULT_UPDATE_CONFIG, runAsyncUpdateCheck } from './update.js';\n\n/** Callbacks home needs from the bin (injected → no circular import). */\nexport interface HomeDeps {\n /**\n * Run a sub-command by user-form argv (e.g. `['status','--json']`) on a fresh\n * commander program. Must NOT throw — it owns its own error → exit-code\n * mapping (bin's `handleError`) and leaves the outcome on `process.exitCode`\n * so it becomes the final exit code of the whole `noir` invocation.\n */\n dispatch: (argv: readonly string[]) => Promise<void>;\n /**\n * The live palette commands (derived from `createProgram()` by bin.ts) — the\n * same source the TUI palette uses. The grouped home menu resolves its\n * sections against this so it cannot drift. Optional for backward-compat\n * (older tests omit it); when absent, sections fall back to the internal\n * curated ids that resolve against a fresh registry.\n */\n readonly commands?: readonly PaletteCommand[];\n}\n\n/** Try to read the project for a friendlier intro banner; never throws. */\nfunction tryProject(): { id: string; name: string; host: HostId } | null {\n try {\n const info = loadProjectInfo(process.cwd());\n return { id: info.id, name: info.name, host: info.config.host };\n } catch {\n // Not initialized yet (no `.noir/project-id`) — the menu still works; it\n // just shows the generic intro. `Status` will surface the real error.\n return null;\n }\n}\n\n/**\n * One-time nudge: shows only for non-native installs, once per version. The\n * \"dismissed for this version\" flag is stored in install.json's record\n * (`dismissedVersions`); absence ⇒ show. Once the user dismisses (via\n * `noir install --dismiss`), the current CLI version is appended and the\n * banner stays silent until they next upgrade Noir.\n */\nexport function shouldShowMigrationBanner(rec: InstallRecord, currentVersion: string): boolean {\n if (rec.method === 'native') return false;\n if (rec.dismissedVersions?.includes(currentVersion)) return false;\n return true;\n}\n\n/** One-line summary of the CLI command surface (shown under the banner). */\nconst COMMANDS_HINT =\n 'Commands: status · context · memory · task · skills · daemon · doctor · install · update · tui · palette';\n\n/**\n * Bare-`noir` router. See module header for the three dispatch arms. Never\n * blocks when input is disabled (the interactive branch is unreachable then).\n *\n * TUI policy: `--no-tui` (`opts.tui === false`) forces the non-interactive\n * `status` path even when {@link isInteractive} would be true (a TTY). `--tui`\n * (`opts.tui === true`) is advisory only — it still requires a TTY, so the\n * `isInteractive(opts)` gate already encodes that. Auto (no flag) preserves the\n * prior behavior. Advisory only — `home` never hard-gates any subcommand.\n */\nexport async function home(opts: CliOptions, deps: HomeDeps): Promise<void> {\n if (isInteractive(opts) && opts.tui !== false) {\n await runMenu(opts, deps);\n return;\n }\n if (opts.json === true) {\n // Machine snapshot — same data `noir status --json` produces.\n await deps.dispatch(['status', '--json']);\n return;\n }\n // Non-interactive, human: run `status` (human snapshot). status is probe-only\n // — it never auto-starts a daemon and exits 0 even when down, so this is\n // a useful bare-`noir` snapshot in CI / pipes instead of a help dump.\n await deps.dispatch(['status']);\n}\n\n// ---------------------------------------------------------------------------\n// The grouped home menu (interactive arm).\n//\n// Two levels driven by a small state machine so navigation is smooth:\n// LEVEL 1 — section picker (clack.select): 1-6 sections + Exit.\n// LEVEL 2 — per-section action list (clack.select with hints).\n//\n// Navigation keys are handled by @clack itself at each level (Esc cancels that\n// prompt). We return a sentinel from each level and branch on it, so \"back\"\n// from level 2 loops back to level 1, and \"next\"/\"previous\" from level 2 jump\n// to the adjacent section's action list. Cancel anywhere → exit 5 (CANCELLED).\n// ---------------------------------------------------------------------------\n\n/** @clack's `select` result is a value | cancel-symbol. */\ntype ClackChoice = string | symbol;\n\n/** The @clack/prompts module (lazy). */\ntype Clack = typeof import('@clack/prompts');\n\n/**\n * Level 1 — the section picker. Returns which section to open, or a control\n * outcome. Uses `select` (NOT `selectKey`) so arrow navigation, Enter-to-submit,\n * and Esc/Ctrl+C cancel all work. `selectKey` is a select-by-typed-letter prompt\n * with no arrow/enter/esc handling and a `_track=false` value that stays\n * `undefined` until a matching letter is pressed — pressing Enter there crashes\n * with `Cannot read properties of undefined (reading 'label')` (the 1.9.0 home\n * menu bug, fixed by switching to `select` on @clack ≥1.7).\n */\nasync function pickSection(\n clack: Clack,\n sections: readonly HomeSection[],\n opts: CliOptions,\n): Promise<ClackChoice> {\n const result = await clack.select({\n message: 'What would you like to do?',\n options: [\n ...sections.map((s) => ({\n value: s.id,\n label: s.label,\n hint: s.hint,\n })),\n { value: 'exit', label: 'Exit', hint: 'leave the home menu' },\n ],\n });\n if (clack.isCancel(result)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n return result;\n}\n\n/**\n * Level 2 — the action list for `section`. Returns which action to run, or a\n * control outcome. `select` renders each action with a one-line `hint`; the\n * final \"Back\" option returns to the section picker. Cancel → back too.\n */\nasync function pickAction(\n clack: Clack,\n section: HomeSection,\n sections: readonly HomeSection[],\n opts: CliOptions,\n): Promise<ClackChoice> {\n const idx = sections.findIndex((s) => s.id === section.id);\n const hasPrev = idx > 0;\n const hasNext = idx !== -1 && idx < sections.length - 1;\n const result = await clack.select({\n message: section.label,\n initialValue: section.items[0]?.id,\n options: [\n ...section.items.map((a) => ({\n value: a.id,\n label: a.label,\n hint: a.hint,\n })),\n // Navigation options so the user can move across sections without\n // re-entering the section picker (the \"lancar\" navigation requirement).\n ...(hasPrev\n ? [{ value: 'prev' as const, label: '← Prev section', hint: sections[idx - 1]?.label }]\n : []),\n ...(hasNext\n ? [{ value: 'next' as const, label: '→ Next section', hint: sections[idx + 1]?.label }]\n : []),\n { value: 'back', label: '↑ Back to sections', hint: 'return to the section picker' },\n ],\n });\n if (clack.isCancel(result)) {\n // Esc / Ctrl+C at the action list → back to the section picker (not exit).\n clack.cancel('Back to the home menu.');\n return 'back';\n }\n if (opts.verbose) {\n process.stderr.write(`noir: home: section '${section.id}' selected\\n`);\n }\n return result;\n}\n\n/**\n * Collect an inline argument for an action that needs one (generalizes the\n * existing recall-query pattern). Cancel → exit 5.\n */\nasync function collectArg(\n clack: Clack,\n action: HomeAction,\n opts: CliOptions,\n): Promise<string | null> {\n if (!action.needsArg) return null;\n const value = await clack.text({\n message: action.needsArg.prompt,\n placeholder: action.needsArg.placeholder,\n validate: (v: string | undefined) =>\n !v || v.trim().length === 0 ? 'Please enter a value.' : undefined,\n });\n if (clack.isCancel(value)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n return String(value);\n}\n\n/**\n * Resolve the argv an action dispatches: the registry-derived `dispatch`,\n * plus the collected inline arg (when the action needs one). A `destructive`\n * action gates behind a clack.confirm first (reusing the palette registry's\n * destructive table). Returns null when the user declined a destructive action.\n */\nasync function argvForAction(\n clack: Clack,\n action: HomeAction,\n opts: CliOptions,\n): Promise<string[] | null> {\n const base = action.dispatch ?? [action.id];\n let argv = [...base];\n // Collect an inline arg ONLY when the action needs one; a non-arg action\n // skips the prompt entirely (this is what distinguishes \"no arg needed\"\n // from \"cancelled\" — both used to collide on `null` and infinite-loop).\n if (action.needsArg) {\n const arg = await collectArg(clack, action, opts);\n if (arg === null) return null; // cancelled → back to the section\n argv = [...base, arg];\n }\n\n // Gate destructive actions behind an explicit confirm (matches the TUI\n // palette's destructive-confirm overlay — same table, isDestructive()).\n if (action.destructive || isDestructive(argv)) {\n const confirmed = await clack.confirm({\n message: `Run \\`noir ${argv.join(' ')}\\`? This may modify project files / the store.`,\n initialValue: false,\n });\n if (clack.isCancel(confirmed) || !confirmed) return null;\n }\n return argv;\n}\n\n/**\n * The grouped home menu loop. Drives the two levels and the back/next/previous\n * navigation. Dispatch through the shared seam; a selected action returns so\n * `runMenu` can print the outro.\n */\nasync function runGroupedMenu(\n clack: Clack,\n opts: CliOptions,\n deps: HomeDeps,\n sections: readonly HomeSection[],\n): Promise<void> {\n // Start on the first section's action list if the user just wants to move\n // fast; otherwise start at the section picker. We begin at the picker so\n // first-run users see the grouping.\n let currentSection: number | null = null; // null = level 1 (section picker)\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // LEVEL 1 — section picker\n if (currentSection === null) {\n const picked = await pickSection(clack, sections, opts);\n if (clack.isCancel(picked) || picked === 'exit') {\n clack.outro('bye');\n return;\n }\n const idx = sections.findIndex((s) => s.id === picked);\n if (idx === -1) {\n // Unknown section value — defensive: re-show the picker.\n continue;\n }\n currentSection = idx;\n continue;\n }\n\n // LEVEL 2 — the current section's action list\n const section = sections[currentSection];\n if (!section) {\n // Defensive: index out of range — back to the picker.\n currentSection = null;\n continue;\n }\n const chosen = await pickAction(clack, section, sections, opts);\n if (clack.isCancel(chosen)) {\n // Esc at the action list → back to the picker.\n currentSection = null;\n continue;\n }\n\n switch (chosen) {\n case 'back': {\n // ← Back → level 1.\n currentSection = null;\n continue;\n }\n case 'next': {\n // → Next section (progressive: no need to re-pick at level 1).\n currentSection = (currentSection + 1) % sections.length;\n continue;\n }\n case 'prev': {\n // ← Previous section.\n currentSection = (currentSection - 1 + sections.length) % sections.length;\n continue;\n }\n default: {\n const action = section.items.find((a) => a.id === chosen);\n if (!action) {\n // Defensive: unknown action id — back to the picker.\n currentSection = null;\n continue;\n }\n const argv = await argvForAction(clack, action, opts);\n if (argv === null) {\n // Destructive declined (or arg cancelled) → stay in the section.\n continue;\n }\n await deps.dispatch(argv);\n clack.outro('done');\n return; // one action per `noir` run, then the process exits\n }\n }\n }\n}\n\n/** The @clack select menu (interactive arm). */\nasync function runMenu(opts: CliOptions, deps: HomeDeps): Promise<void> {\n // Lazy import: the non-interactive paths never load @clack (NF6).\n const clack = await import('@clack/prompts');\n\n const project = tryProject();\n\n // fire-and-forget; never blocks, never prints under --json/--quiet/CI/non-TTY.\n void runAsyncUpdateCheck({\n env: process.env,\n configUpdate: (() => {\n // Try to read the project's update config block; fall back to defaults\n // if the project isn't initialized or the block is absent.\n try {\n const info = loadProjectInfo(process.cwd());\n return info.config.update ?? DEFAULT_UPDATE_CONFIG;\n } catch {\n return DEFAULT_UPDATE_CONFIG;\n }\n })(),\n quiet: opts.json === true || opts.quiet === true || !process.stdout.isTTY,\n });\n\n if (shouldShowBanner(opts)) {\n const host = project?.host ?? 'claude';\n process.stderr.write(\n `\\n${renderBanner()}\\n${NOIR_TAGLINE}\\n\\n${hostLaunchDirective(host)}\\n${COMMANDS_HINT}\\n\\n`,\n );\n }\n\n // One-time migration nudge for non-native installs.\n const rec = readInstallRecord();\n if (rec && shouldShowMigrationBanner(rec, NOIR_VERSION)) {\n process.stderr.write(\n `\\n ${c.warn(`noir installed via ${rec.method}`)} — consider \\`noir install\\` for the native path (auto-update, no npm prefix/PATH issues). Dismiss with: \\`noir install --dismiss\\` (persisted per version).\\n\\n`,\n );\n }\n\n // Update-available notice (version + install-type-specific advice). Reads the\n // CACHED latest version (the async check above refreshes it for next time) and\n // advertises it only when strictly newer (semver downgrade guard).\n const updateNotice = buildUpdateNotice({\n method: detectActiveMethod(),\n currentVersion: NOIR_VERSION,\n latestKnown: latestVersionFromCache(readUpdateCache(), 'latest'),\n });\n if (updateNotice) {\n process.stderr.write(\n `\\n ${c.warn(`noir ${updateNotice.latestVersion} is available`)} (you have ${updateNotice.currentVersion}) — ${updateNotice.advice}.\\n\\n`,\n );\n }\n\n clack.intro(project ? `noir — ${project.name}` : 'noir');\n\n // Resolve the curated sections against the LIVE palette registry. bin.ts\n // injects `deps.commands` (the same list the TUI palette uses). Tests that\n // omit it (backward-compat) pass an empty list → sections degrade to those\n // whose ids exist (none) — the menu would be empty, but those tests don't\n // drive the interactive arm's content. bin.ts always injects the real list.\n const sections = await resolveSections(deps.commands ?? []);\n\n await runGroupedMenu(clack, opts, deps, sections);\n}\n","// `noir memory {recall,save,sessions,forget,consolidate}`.\n//\n// Thin MCP-client commands over the running daemon's memory engine. Each is\n// a `callDaemonTool` round-trip (or `withDaemon` when capability discovery is\n// needed); the daemon owns the sole store handle, so the CLI never opens it\n// in-process. Daemon-unreachable ⇒ exit 4 (DAEMON_DOWN) from daemon-client.\n//\n// Scriptability (hard rule): `memory save --content` may be supplied by\n// flag; only when it's missing AND the session is interactive do we prompt via\n// @clack (lazy-imported so non-interactive paths never load it). Missing\n// content under non-interactive / --no-input / --json / CI / NO_COLOR ⇒ exit 2\n// (USAGE) naming the missing flag — NO blocking prompt on a pipe. A cancel at\n// the prompt ⇒ exit 5 (CANCELLED).\n//\n// `memory consolidate` is opt-in + provider-explicit: the\n// daemon registers the `memory_consolidate` tool ONLY when the user set\n// `memory.consolidation.enabled: true` AND a provider+model resolved. Calling a\n// tool the daemon doesn't register would mis-map onto exit 4 (daemon-down), so\n// we discover the daemon's tool list first (caller.listTools) and, if the tool\n// is absent, emit a clear \"not exposed\" message + exit 1 (honest — the daemon IS\n// up, consolidation just isn't enabled). A provider refusal\n// (`{ok:false,reason:'no-provider'|'model-unavailable'|'no-candidates'}`) is\n// honored as exit 1 with the reason; it is NEVER a silent paid call.\n\nimport { readFileSync } from 'node:fs';\nimport { loadProjectInfo, type ProjectInfo } from '@noir-ai/core';\nimport {\n callDaemonTool,\n type DaemonClientOptions,\n type DaemonProbe,\n type DaemonToolCaller,\n probeDaemon,\n probeWorkspaceDaemon,\n type WorkspaceRouting,\n withDaemon,\n withInProcessRead,\n withWorkspaceDaemon,\n withWorkspaceMemoryRead,\n workspaceRoutingTarget,\n} from '../daemon-client.js';\nimport {\n type CliOptions,\n definitionList,\n EXIT,\n fail,\n info,\n isInteractive,\n log,\n table,\n} from '../output.js';\nimport { badge } from '../theme.js';\n\n/** Options accepted by every `memory` sub-command (globals + daemon knobs). */\nexport interface MemoryOptions extends CliOptions, DaemonClientOptions {}\n\n/**\n * Resolve this repo's workspace membership (marker → routing + project), or\n * `null` when it is not joined. Memory commands route to the WORKSPACE daemon\n * when joined; context/workflow/task stay per-project.\n */\nfunction resolveWorkspace(): {\n routing: WorkspaceRouting;\n project: ProjectInfo;\n} | null {\n const routing = workspaceRoutingTarget(process.cwd());\n if (routing === null) return null;\n try {\n return { routing, project: loadProjectInfo(process.cwd()) };\n } catch {\n return null;\n }\n}\n\n/** Call a memory tool — workspace daemon when joined, else the project daemon. */\nasync function callMemory<T>(\n opts: MemoryOptions,\n name: string,\n args: Record<string, unknown> = {},\n): Promise<T> {\n const ws = resolveWorkspace();\n if (ws === null) return callDaemonTool<T>(opts, name, args);\n return withWorkspaceDaemon<T>(opts, ws.routing, (caller) => caller.callTool<T>(name, args));\n}\n\n/** Multi-call memory path (capability discovery): workspace daemon when joined. */\nasync function withMemoryDaemon<T>(\n opts: MemoryOptions,\n fn: (caller: DaemonToolCaller) => Promise<T>,\n): Promise<T> {\n const ws = resolveWorkspace();\n if (ws === null) return withDaemon(opts, fn);\n return withWorkspaceDaemon<T>(opts, ws.routing, fn);\n}\n\n// ---------------------------------------------------------------------------\n// Tool result shapes (slices of the daemon wire payloads; CLI depends only on\n// the MCP contract — local types, mirrors context.ts / status.ts).\n// ---------------------------------------------------------------------------\n\n/** A daemon logical-failure envelope, read generically. */\ninterface ToolFailure {\n ok: false;\n degraded?: boolean;\n error?: string;\n reason?: string;\n logged?: boolean;\n}\n\n/** Normalized memory hit (MemoryHit) rendered to humans + JSON. */\nexport interface MemoryHitData {\n id: string;\n type: string;\n score: number;\n content: string;\n concepts: string[];\n files: string[];\n ts: number;\n importance: number;\n source: string;\n}\n\n/** `memory_recall` success payload. */\ninterface MemoryRecallResult {\n ok: true;\n results?: unknown;\n degraded?: boolean;\n}\n\n/** `memory_save` success payload. */\ninterface MemorySaveResult {\n ok: true;\n id?: string;\n observation?: Record<string, unknown>;\n}\n\n/** One row of `memory_sessions`. */\nexport interface SessionRow {\n id: string;\n count: number;\n lastTs: number;\n}\n\n/** `memory_sessions` success payload. */\ninterface MemorySessionsResult {\n ok: true;\n sessions?: unknown;\n}\n\n/** `memory_forget` success payload (ForgetResult). */\ninterface MemoryForgetResult {\n ok: true;\n deleted?: number;\n ids?: unknown;\n}\n\n/** `memory_consolidate` success payload (ConsolidationResult success branch). */\ninterface MemoryConsolidateOk {\n ok: true;\n lessons?: unknown;\n from?: unknown;\n}\n\n/** `memory_consolidate` refusal (ConsolidationResult failure branch — data). */\ninterface MemoryConsolidateRefusal {\n ok: false;\n reason: 'no-provider' | 'model-unavailable' | 'no-candidates';\n logged?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Small shared helpers\n// ---------------------------------------------------------------------------\n\n/** Coerce `--limit` (commander string) to a positive int, else exit 2. */\nfunction parseLimit(raw: string | undefined, label: string, opts: CliOptions): number | undefined {\n if (raw === undefined) return undefined;\n const n = Number(raw);\n if (!Number.isInteger(n) || n <= 0) {\n fail(EXIT.USAGE, `${label}: --limit must be a positive integer (got '${raw}')`, opts);\n }\n return n;\n}\n\n/** Split a comma-separated flag value (`--files a,b,c`) into a trimmed list. */\nfunction splitCsv(raw: string | undefined): string[] | undefined {\n if (raw === undefined) return undefined;\n const out = raw\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n return out.length > 0 ? out : undefined;\n}\n\n/** Narrow a raw memory hit (MemoryHit) into the rendered {@link MemoryHitData}. */\nfunction toHit(raw: unknown): MemoryHitData {\n const h = (raw ?? {}) as Record<string, unknown>;\n const arr = (v: unknown): string[] =>\n Array.isArray(v) ? (v.filter((x) => typeof x === 'string') as string[]) : [];\n return {\n id: typeof h.id === 'string' ? h.id : '',\n type: typeof h.type === 'string' ? h.type : '',\n score: typeof h.score === 'number' ? h.score : 0,\n content: typeof h.content === 'string' ? h.content : '',\n concepts: arr(h.concepts),\n files: arr(h.files),\n ts: typeof h.ts === 'number' ? h.ts : 0,\n importance: typeof h.importance === 'number' ? h.importance : 0,\n source: typeof h.source === 'string' ? h.source : '',\n };\n}\n\n/** Surface a daemon logical-failure envelope as exit 1 (daemon was reachable). */\nfunction failTool(label: string, envelope: ToolFailure, opts: CliOptions): never {\n const detail = envelope.error ?? envelope.reason ?? 'unknown failure';\n fail(EXIT.ERROR, `${label}: ${detail}`, opts);\n}\n\n/** Render an epoch-ms as a short ISO-ish stamp for human tables. */\nfunction stamp(ts: number): string {\n if (typeof ts !== 'number' || ts <= 0) return '-';\n // UTC ISO (no tz surprises); clipped for table density.\n return new Date(ts).toISOString().replace('T', ' ').replace(/\\..*$/, 'Z');\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory recall <query> [--limit N]`\n// ---------------------------------------------------------------------------\nexport interface MemoryRecallOptions extends MemoryOptions {\n query: string;\n limit?: string;\n}\n\n/**\n * Run `memory_recall` against the daemon when it is up, or fall back to the\n * IN-PROCESS read-only memory engine when the daemon probe reports it down\n * (the daemon-down read fallback). Reads degrade to the read-only store\n * (never exit 4); writes (`memory save` / `forget` / `consolidate`) keep the\n * daemon-required path.\n * The probe is conservative (mirrors contextSearch): only a CONFIRMED\n * `{running:false}` engages the fallback; an unavailable probe defaults to the\n * daemon path.\n */\nexport async function memoryRecall(opts: MemoryRecallOptions): Promise<void> {\n const limit = parseLimit(opts.limit, 'memory recall', opts);\n // Workspace mode: route to the workspace daemon / workspace read-only store.\n const ws = resolveWorkspace();\n if (ws !== null) {\n const probe = await probeWorkspaceDaemon(ws.routing);\n if (!probe.running) {\n await withWorkspaceMemoryRead(opts, ws.routing, ws.project, async (memory) => {\n const { hits, degraded, mode } = await memory.recallWithMeta(opts.query, {\n ...(limit === undefined ? {} : { limit }),\n });\n const data = { query: opts.query, hits, degraded, mode };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderRecall(data.query, data.hits, data.degraded, opts);\n });\n return;\n }\n const res = await callMemory<MemoryRecallResult | ToolFailure>(opts, 'memory_recall', {\n query: opts.query,\n ...(limit === undefined ? {} : { limit }),\n });\n if (res.ok !== true) failTool('memory recall', res, opts);\n const hits = Array.isArray(res.results) ? (res.results as unknown[]).map(toHit) : [];\n const data = { query: opts.query, hits, degraded: res.degraded === true };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderRecall(data.query, hits, data.degraded, opts);\n return;\n }\n let probe: DaemonProbe;\n try {\n probe = await probeDaemon(opts);\n } catch {\n probe = { running: true };\n }\n if (!probe.running) {\n await withInProcessRead(opts, async (engines) => {\n const { hits, degraded, mode } = await engines.memory.recallWithMeta(opts.query, {\n ...(limit === undefined ? {} : { limit }),\n });\n // Honest per-call outcome: this branch runs because the daemon is DOWN\n // (read-only fallback), AND the recall itself reports its own degraded\n // (BM25-only) state — both feed the rendered flag.\n const data = { query: opts.query, hits, degraded, mode };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderRecall(data.query, data.hits, data.degraded, opts);\n });\n return;\n }\n const res = await callDaemonTool<MemoryRecallResult | ToolFailure>(opts, 'memory_recall', {\n query: opts.query,\n ...(limit === undefined ? {} : { limit }),\n });\n if (res.ok !== true) failTool('memory recall', res, opts);\n\n const hits = Array.isArray(res.results) ? (res.results as unknown[]).map(toHit) : [];\n const data = { query: opts.query, hits, degraded: res.degraded === true };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderRecall(data.query, hits, data.degraded, opts);\n}\n\nfunction renderRecall(\n query: string,\n hits: MemoryHitData[],\n degraded: boolean,\n opts: CliOptions,\n): void {\n const flag = degraded ? ` ${badge('warn', 'degraded: BM25-only')}` : '';\n log(\n `memory recall — ${hits.length} hit${hits.length === 1 ? '' : 's'} for '${query}'${flag}`,\n opts,\n );\n if (hits.length === 0) {\n info('(no memories matched)', opts);\n return;\n }\n // Full content per hit (never truncate the DATA; display shows it whole\n // in a readable block rather than a cramped table cell). The block-list shape\n // is load-bearing — do NOT sweep this into the responsive table().\n for (let i = 0; i < hits.length; i++) {\n const h = hits[i];\n if (h === undefined) continue;\n const head = `[${i + 1}] ${h.type} · score ${h.score.toFixed(4)} · importance ${h.importance.toFixed(2)} · ${h.id}`;\n log(head, opts);\n if (h.files.length > 0) log(` files: ${h.files.join(', ')}`, opts);\n if (h.concepts.length > 0) log(` tags: ${h.concepts.join(', ')}`, opts);\n log(` ${h.content}`, opts);\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory save [--content] [--type] [--files]`\n// ---------------------------------------------------------------------------\nexport interface MemorySaveOptions extends MemoryOptions {\n content?: string;\n type?: string;\n files?: string;\n}\n\nexport async function memorySave(opts: MemorySaveOptions): Promise<void> {\n const content = await resolveContent(opts);\n const files = splitCsv(opts.files);\n const args: Record<string, unknown> = { content };\n if (typeof opts.type === 'string' && opts.type.length > 0) args.type = opts.type;\n if (files !== undefined) args.files = files;\n\n const res = await callMemory<MemorySaveResult | ToolFailure>(opts, 'memory_save', args);\n if (res.ok !== true) failTool('memory save', res, opts);\n\n const id = typeof res.id === 'string' ? res.id : '';\n const observation = res.observation ?? {};\n const data = { id, observation };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(`Saved memory ${id}.`, opts);\n renderObservation(observation, opts);\n}\n\n/**\n * Resolve `--content`: from the flag when given; otherwise prompt interactively\n * (only when the session is interactive); otherwise exit 2 naming the flag. The\n * @clack import is lazy so a scripted / CI / --json run never loads it, and a\n * cancel at the prompt maps to exit 5.\n */\nasync function resolveContent(opts: MemorySaveOptions): Promise<string> {\n if (typeof opts.content === 'string' && opts.content.length > 0) return opts.content;\n if (!isInteractive(opts)) {\n fail(\n EXIT.USAGE,\n 'memory save requires --content <text> (or re-run in an interactive terminal to be prompted)',\n opts,\n );\n }\n const clack = await import('@clack/prompts');\n const value = await clack.text({\n message: 'Memory to save:',\n placeholder: 'the insight / decision / pattern to remember',\n validate: (v: string | undefined) =>\n !v || v.trim().length === 0 ? 'content cannot be empty' : undefined,\n });\n if (clack.isCancel(value)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n return String(value);\n}\n\nfunction renderObservation(obs: Record<string, unknown>, opts: CliOptions): void {\n const rows: Array<{ label: string; value: unknown }> = [];\n for (const key of ['id', 'type', 'importance', 'ts', 'source', 'project', 'sessionId']) {\n if (obs[key] !== undefined) rows.push({ label: key, value: obs[key] });\n }\n if (rows.length > 0) definitionList(rows, opts);\n const content = obs.content;\n if (typeof content === 'string') log(`\\n${content}`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory capture [file]` — manual transcript/notes distill\n// ---------------------------------------------------------------------------\nexport interface MemoryCaptureOptions extends MemoryOptions {\n /** A transcript/notes file to distill (or pipe stdin). */\n file?: string;\n /** Inline distilled content (overrides file/stdin). */\n content?: string;\n /** Capture hook label for provenance (defaults to 'Stop'). */\n eventType?: string;\n}\n\nexport async function memoryCapture(opts: MemoryCaptureOptions): Promise<void> {\n const content = await resolveCaptureContent(opts);\n const args: Record<string, unknown> = { content };\n if (typeof opts.eventType === 'string' && opts.eventType.length > 0)\n args.eventType = opts.eventType;\n\n const res = await callMemory<MemorySaveResult | ToolFailure>(opts, 'memory_capture', args);\n if (res.ok !== true) failTool('memory capture', res, opts);\n\n const id = typeof res.id === 'string' ? res.id : '';\n const observation = res.observation ?? {};\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data: { id, observation } })}\\n`);\n return;\n }\n log(`Captured memory ${id}.`, opts);\n renderObservation(observation, opts);\n}\n\n/**\n * Resolve capture content: `--content` > `--file` (read the file) > piped stdin\n * > interactive prompt. Missing all sources under non-interactive ⇒ exit 2.\n */\nasync function resolveCaptureContent(opts: MemoryCaptureOptions): Promise<string> {\n if (typeof opts.content === 'string' && opts.content.length > 0) return opts.content;\n if (typeof opts.file === 'string' && opts.file.length > 0) {\n try {\n return readFileSync(opts.file, 'utf8');\n } catch {\n fail(EXIT.USAGE, `memory capture could not read --file ${opts.file}`, opts);\n }\n }\n if (!process.stdin.isTTY) {\n // A non-TTY stdin can still be unreadable (closed fd 0, EAGAIN under CI).\n // Route that through fail() so `--json` gets its {ok:false} envelope instead\n // of a raw readFileSync error on stderr.\n try {\n return readFileSync(0, 'utf8');\n } catch {\n fail(\n EXIT.USAGE,\n 'memory capture could not read piped stdin — pass --content <text> or --file <path>.',\n opts,\n );\n }\n }\n if (!isInteractive(opts)) {\n fail(\n EXIT.USAGE,\n 'memory capture requires --content <text> or --file <path> (or piped stdin).',\n opts,\n );\n }\n const clack = await import('@clack/prompts');\n const value = await clack.text({ message: 'Captured memory content:' });\n if (clack.isCancel(value)) {\n clack.cancel('Cancelled.');\n fail(EXIT.CANCELLED, 'cancelled', opts);\n }\n return String(value);\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory sessions`\n// ---------------------------------------------------------------------------\nexport async function memorySessions(opts: MemoryOptions): Promise<void> {\n const ws = resolveWorkspace();\n if (ws !== null) {\n const probe = await probeWorkspaceDaemon(ws.routing);\n if (!probe.running) {\n await withWorkspaceMemoryRead(opts, ws.routing, ws.project, async (memory) => {\n const sessions: SessionRow[] = memory.sessions().map((s) => ({\n id: s.id,\n count: s.count,\n lastTs: s.lastTs,\n }));\n const data = { sessions };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSessions(sessions, opts);\n });\n return;\n }\n const res = await callMemory<MemorySessionsResult | ToolFailure>(opts, 'memory_sessions');\n if (res.ok !== true) failTool('memory sessions', res, opts);\n const sessions: SessionRow[] = Array.isArray(res.sessions)\n ? (res.sessions as unknown[]).map((raw) => {\n const s = (raw ?? {}) as Record<string, unknown>;\n return {\n id: typeof s.id === 'string' ? s.id : '',\n count: typeof s.count === 'number' ? s.count : 0,\n lastTs: typeof s.lastTs === 'number' ? s.lastTs : 0,\n };\n })\n : [];\n const data = { sessions };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSessions(sessions, opts);\n return;\n }\n let probe: DaemonProbe;\n try {\n probe = await probeDaemon(opts);\n } catch {\n probe = { running: true };\n }\n if (!probe.running) {\n await withInProcessRead(opts, async (engines) => {\n const raw = engines.memory.sessions();\n const sessions: SessionRow[] = raw.map((s) => ({\n id: s.id,\n count: s.count,\n lastTs: s.lastTs,\n }));\n const data = { sessions };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSessions(sessions, opts);\n });\n return;\n }\n const res = await callDaemonTool<MemorySessionsResult | ToolFailure>(opts, 'memory_sessions');\n if (res.ok !== true) failTool('memory sessions', res, opts);\n\n const sessions: SessionRow[] = Array.isArray(res.sessions)\n ? (res.sessions as unknown[]).map((raw) => {\n const s = (raw ?? {}) as Record<string, unknown>;\n return {\n id: typeof s.id === 'string' ? s.id : '',\n count: typeof s.count === 'number' ? s.count : 0,\n lastTs: typeof s.lastTs === 'number' ? s.lastTs : 0,\n };\n })\n : [];\n const data = { sessions };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n renderSessions(sessions, opts);\n}\n\n/** Render the sessions list (human table to stderr; --json handled by caller). */\nfunction renderSessions(sessions: SessionRow[], opts: CliOptions): void {\n log(`memory sessions — ${sessions.length} session${sessions.length === 1 ? '' : 's'}`, opts);\n table(\n sessions.map((s) => ({ Session: s.id, Observations: s.count, 'Last seen': stamp(s.lastTs) })),\n ['Session', 'Observations', 'Last seen'],\n opts,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory forget <id> [<id> ...]`\n// ---------------------------------------------------------------------------\nexport interface MemoryForgetOptions extends MemoryOptions {\n /** Positional observation ids. */\n ids: string[];\n}\n\nexport async function memoryForget(opts: MemoryForgetOptions): Promise<void> {\n if (opts.ids.length === 0) {\n fail(EXIT.USAGE, 'memory forget requires at least one <id>', opts);\n }\n const res = await callMemory<MemoryForgetResult | ToolFailure>(opts, 'memory_forget', {\n ids: opts.ids,\n });\n if (res.ok !== true) failTool('memory forget', res, opts);\n\n const deleted = typeof res.deleted === 'number' ? res.deleted : 0;\n const data = { deleted, ids: opts.ids };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(`Forgot ${deleted} observation${deleted === 1 ? '' : 's'}.`, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir memory consolidate [--types <csv>] [--limit N]`\n// ---------------------------------------------------------------------------\nexport interface MemoryConsolidateOptions extends MemoryOptions {\n /** Comma-separated type filter (mapped to the daemon's `types[]`). */\n types?: string;\n limit?: string;\n}\n\nexport async function memoryConsolidate(opts: MemoryConsolidateOptions): Promise<void> {\n // Capability discovery (spec: \"only if the daemon exposes it — else a clear\n // message\"): a daemon that didn't opt into consolidation registers no\n // `memory_consolidate` tool. Detecting that via listTools lets us say so\n // honestly instead of calling a missing tool (which would mis-map to exit 4).\n const exposed = await withMemoryDaemon(opts, async (caller) => caller.listTools());\n if (!exposed.includes('memory_consolidate')) {\n fail(\n EXIT.ERROR,\n resolveWorkspace() !== null\n ? 'memory consolidate is not supported on shared workspaces (the workspace memory store has no consolidation model).'\n : 'memory consolidate: the daemon does not expose the memory_consolidate tool. Enable it in .noir/config under memory.consolidation (enabled: true + a provider + model), then restart the daemon.',\n opts,\n );\n }\n\n const types = splitCsv(opts.types);\n const limit = parseLimit(opts.limit, 'memory consolidate', opts);\n const args: Record<string, unknown> = {};\n if (types !== undefined) args.types = types;\n if (limit !== undefined) args.limit = limit;\n\n const res = await callMemory<MemoryConsolidateOk | MemoryConsolidateRefusal | ToolFailure>(\n opts,\n 'memory_consolidate',\n args,\n );\n if (res.ok === true) {\n const lessons = Array.isArray(res.lessons) ? (res.lessons as unknown[]) : [];\n const from = Array.isArray(res.from) ? (res.from as unknown[]) : [];\n const data = { lessons, from };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(\n `Consolidated ${lessons.length} lesson${lessons.length === 1 ? '' : 's'} from ${from.length} observation${from.length === 1 ? '' : 's'}.`,\n opts,\n );\n for (let i = 0; i < lessons.length; i++) {\n const l = lessons[i];\n if (l === undefined) continue;\n const obs = (l ?? {}) as Record<string, unknown>;\n const id = typeof obs.id === 'string' ? obs.id : `<lesson ${i + 1}>`;\n const content = typeof obs.content === 'string' ? obs.content : '';\n log(`- ${id}: ${content}`, opts);\n }\n return;\n }\n\n // Refusal (no-provider / model-unavailable / no-candidates) or a degraded\n // envelope. Both are honest exit-1 outcomes — consolidation did NOT run. Read\n // via a record cast so the picks are valid across the refusal + ToolFailure\n // union members (they don't all carry `error` / `degraded`).\n const env = res as unknown as Record<string, unknown>;\n const reason =\n (typeof env.reason === 'string' ? env.reason : undefined) ??\n (typeof env.error === 'string' ? env.error : undefined) ??\n 'unknown';\n const logged = env.logged === true || env.degraded === true;\n const suffix = logged ? ' (logged)' : '';\n fail(EXIT.ERROR, `memory consolidate did not run: ${reason}${suffix}`, opts);\n}\n","// `noir release [<version>] [--channel beta|stable]`.\n// A guided orchestrator over the existing patch-release flow (CLAUDE.md).\n// Walks the checklist; hands off at human-approval gates. The FSM release\n// phase (optional, done→released) is designed but not\n// yet wired — this CLI is the release tool that work called for.\n// Build-once / idempotent: bump-version.mjs is idempotent; release:tag\n// refuses dirty/unpushed; tags are immutable (never reuse — deprecate+patch).\n\nimport { execSync, spawnSync } from 'node:child_process';\nimport { type CliOptions, EXIT, fail, info, log, success, tip, warn } from '../output.js';\n\nexport interface ReleaseOptions extends CliOptions {\n version?: string;\n channel?: string;\n dryRun?: boolean;\n}\n\ntype Step =\n | 'preflight'\n | 'bump'\n | 'gate'\n | 'commit'\n | 'ci-develop'\n | 'beta-tag'\n | 'human-approve-beta'\n | 'merge-main'\n | 'ci-main'\n | 'stable-tag'\n | 'human-approve-stable'\n | 'dist-bump'\n | 'sync';\n\n/** Report what step does and who runs it. */\nfunction describe(step: Step, who: 'noir' | 'human'): string {\n const m: Record<Step, string> = {\n preflight: 'check clean tree, on develop, HEAD pushed, gate green, no version collision',\n bump: 'run bump-version.mjs + update CHANGELOG + roadmap docs',\n gate: 're-run the full gate (lint/build/typecheck/test/docs:validate)',\n commit: 'commit chore(release) + push develop',\n 'ci-develop': 'wait for CI on develop → green',\n 'beta-tag': 'pnpm release:tag → beta tag + push',\n 'human-approve-beta': 'approve the release workflow publish job in GitHub Actions',\n 'merge-main': 'merge develop → main (fast-forward) + push',\n 'ci-main': 'wait for CI on main → green',\n 'stable-tag': 'pnpm release:tag → stable tag + push',\n 'human-approve-stable': 'approve the release workflow publish job in GitHub Actions (again)',\n 'dist-bump':\n 'update Homebrew (packaging/homebrew/noir.rb) + Scoop (packaging/scoop/noir.json) from npm',\n sync: 'merge main → develop + verify both branches at same SHA',\n };\n return `${m[step]} [${who}]`;\n}\n\nconst ALL: Step[] = [\n 'preflight',\n 'bump',\n 'gate',\n 'commit',\n 'ci-develop',\n 'beta-tag',\n 'human-approve-beta',\n 'merge-main',\n 'ci-main',\n 'stable-tag',\n 'human-approve-stable',\n 'dist-bump',\n 'sync',\n];\n\nexport async function release(opts: ReleaseOptions): Promise<void> {\n if (opts.dryRun) {\n log('noir release --dry-run (no changes will be made)', opts);\n for (const s of ALL) {\n const who: 'noir' | 'human' = s.startsWith('human-approve') ? 'human' : 'noir';\n info(`${s}: ${describe(s, who)}`, opts);\n }\n tip('full release flow documented in CLAUDE.md \"Patch release flow (beta → stable)\"', opts);\n return;\n }\n\n const version = opts.version?.trim();\n if (!version) {\n fail(EXIT.USAGE, 'release: <version> is required (e.g. noir release 1.10.0)', opts);\n }\n const channel = opts.channel === 'stable' ? 'stable' : 'beta';\n\n log(`noir release ${version} (${channel} channel)`, opts);\n\n // Run each step; hand off at human gates. Failures surface clearly.\n try {\n // 1. Preflight\n info(`[1/13] preflight: ${describe('preflight', 'noir')}`, opts);\n execSync('git diff --quiet', { encoding: 'utf8' });\n const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();\n if (branch !== 'develop') fail(EXIT.ERROR, 'release: must be on develop', opts);\n const status = execSync('git status --porcelain', { encoding: 'utf8' }).trim();\n if (status) fail(EXIT.ERROR, 'release: tree must be clean', opts);\n success('preflight: ✓ clean, on develop', opts);\n\n // 2. Bump\n info(`[2/13] bump: ${describe('bump', 'noir')}`, opts);\n spawnSync('node', ['scripts/bump-version.mjs', version], {\n encoding: 'utf8',\n stdio: 'inherit',\n });\n success(`bump: ✓ version ${version}`, opts);\n\n // 3. Gate\n info(`[3/13] gate: ${describe('gate', 'noir')}`, opts);\n execSync('pnpm lint && pnpm build && pnpm typecheck && pnpm test && pnpm docs:validate', {\n encoding: 'utf8',\n stdio: 'inherit',\n });\n success('gate: ✓ full gate green', opts);\n\n // 4. Commit (spawnSync — no shell, no injection).\n info(`[4/13] commit/push: ${describe('commit', 'noir')}`, opts);\n spawnSync('git', ['add', '-A'], { encoding: 'utf8', stdio: 'inherit' });\n spawnSync('git', ['commit', '-m', `chore(release): v${version} + docs sync`], {\n encoding: 'utf8',\n stdio: 'inherit',\n });\n execSync('git push origin develop', { encoding: 'utf8' });\n success('commit: ✓ pushed to develop', opts);\n\n // 5-6. CI wait + beta tag\n info(`[5/13] ci-develop: waiting for CI on develop…`, opts);\n info(`[6/13] beta-tag: ${describe('beta-tag', 'noir')}`, opts);\n tip('after CI green: run `pnpm release:tag` and push the beta tag', opts);\n\n // 7. Human handoff\n warn(`[7/13] ${describe('human-approve-beta', 'human')}`, opts);\n tip('open GitHub Actions → approve the publish job, then continue', opts);\n tip('after approval: verify `npm view @noir-ai/cli dist-tags beta` = beta version', opts);\n\n // 8-13 (remaining steps — documented for human continuation)\n info('remaining steps (merge→stable→dist→sync): see CLAUDE.md', opts);\n for (const s of ALL.slice(7)) {\n const who: 'noir' | 'human' = s.startsWith('human-approve') ? 'human' : 'noir';\n info(` ${s}: ${describe(s, who)}`, opts);\n }\n\n success(`release ${version}: preflight→bump→gate→commit done. Resume after approval.`, opts);\n } catch (err) {\n fail(EXIT.ERROR, `release failed: ${err instanceof Error ? err.message : String(err)}`, opts);\n }\n}\n","// Run timing constants shared by the orchestrator and the interrupt ladder.\n//\n// Kept out of `run-interrupt.ts` and `orchestrator.ts` on purpose: the two\n// already form an import edge (run-interrupt imports the child-signalling\n// helper from the orchestrator), so a second edge in the other direction would\n// be a cycle. A run-timing leaf breaks that cycle while giving every consumer\n// one source of truth for how long the polite-stop grace lasts.\n\n/**\n * How long the polite signal gets before the forceful one. A host that is\n * mid-request needs a moment to wind down and finish writing its own record of\n * the session; a host that ignores the signal entirely must not hold the\n * terminal past this.\n */\nexport const INTERRUPT_GRACE_MS = 5000;\n","// ENOENT fallback for `noir run --command <name>`: resolves a command name\n// through the user's interactive shell. `child_process.spawn()` cannot see shell\n// aliases or functions (they only exist inside an interactive shell that has\n// sourced .zshrc/.bashrc/config.fish), and PATH entries exported only in rc\n// files are invisible to non-interactive children — both surface as ENOENT.\n//\n// Safety model (the whole point of this module):\n// - the NAME is validated against a strict charset and passed ONLY via argv\n// ($1), never interpolated into the -c string;\n// - the host's flags + prompt travel only as \"$@\" / $argv positional\n// parameters, so no user-controlled text ever reaches the shell parser;\n// - the probe is a one-shot lookup with a Node-side timeout killed via the\n// process group (rc files can hang, exit, or spawn orphaned children).\n// On Windows the fallback is disabled entirely (cmd/PowerShell aliases are\n// session-scoped and unspawnable).\n\nimport { spawn } from 'node:child_process';\nimport { basename } from 'node:path';\n\nconst PROBE_TIMEOUT_MS = 3000;\n/** Strict command-name charset — anything else is never handed to a shell. */\nconst NAME_RE = /^[A-Za-z0-9][A-Za-z0-9._+-]*$/;\nconst SUPPORTED_SHELLS = new Set(['zsh', 'bash', 'fish']);\n\nexport interface ShellBridgeEnv {\n SHELL?: string;\n PATH?: string;\n HOME?: string;\n [key: string]: string | undefined;\n}\n\n/** What the probe found for a name, or why it bailed. */\nexport type ShellResolution =\n | { readonly kind: 'path'; readonly path: string; readonly shell: string }\n | { readonly kind: 'alias'; readonly shell: string }\n | { readonly kind: 'function'; readonly shell: string }\n | { readonly kind: 'none'; readonly shell: string }\n | { readonly kind: 'error'; readonly message: string }\n | { readonly kind: 'unsupported' };\n\nexport interface ResolveOptions {\n readonly env?: ShellBridgeEnv;\n readonly platform?: NodeJS.Platform;\n readonly timeoutMs?: number;\n}\n\n/**\n * Guard: only plain command names on a POSIX platform with a supported\n * interactive shell (zsh/bash/fish) are eligible for the shell fallback.\n */\nexport function isEligibleForShellFallback(name: string, opts: ResolveOptions = {}): boolean {\n const { env = process.env, platform = process.platform } = opts;\n if (platform === 'win32') return false;\n if (name.length === 0 || name.includes('/') || !NAME_RE.test(name)) return false;\n const shell = env.SHELL;\n if (!shell || shell.length === 0) return false;\n return SUPPORTED_SHELLS.has(basename(shell).toLowerCase());\n}\n\n/** Extract the LAST `N:` (resolved path) and `K:` (kind) sentinel lines. */\nexport function parseProbeOutput(stdout: string): { path: string; kind: string } {\n let path = '';\n let kind = '';\n for (const line of stdout.split('\\n')) {\n if (line.startsWith('N:')) path = line.slice(2).trim();\n else if (line.startsWith('K:')) kind = line.slice(2).trim();\n }\n return { path, kind };\n}\n\n/** Map probe output onto a spawnable outcome (PATH wins over kind). */\nexport function classifyResolution(\n path: string,\n kind: string,\n): 'path' | 'alias' | 'function' | 'none' {\n if (path.trim().startsWith('/')) return 'path';\n const k = kind.trim().toLowerCase();\n if (k.includes('alias')) return 'alias';\n if (k.includes('function')) return 'function';\n return 'none';\n}\n\nfunction shellKind(shell: string): 'zsh' | 'bash' | 'fish' | null {\n const kind = basename(shell).toLowerCase();\n return (SUPPORTED_SHELLS.has(kind) ? kind : null) as 'zsh' | 'bash' | 'fish' | null;\n}\n\n/** Login+interactive flags per shell. fish has no separate login mode — `-i -c`\n * sources config.fish; zsh/bash use `-lic` (login+interactive) so rc files that\n * only load in login shells (PATH via .zprofile / /etc/zprofile) are visible. */\nconst PROBE_FLAGS: Record<'zsh' | 'bash' | 'fish', string> = {\n zsh: '-lic',\n bash: '-lic',\n fish: '-ic',\n};\n\n/**\n * One-shot probe scripts. The NAME is always read from `$1`/`$argv[1]` (argv),\n * never written into the script. bash login shells skip .bashrc, so it is\n * sourced explicitly (double-source is harmless). stdout noise (rc banners,\n * \"no job control\") is ignored by sentinel parsing.\n */\nconst PROBE_SCRIPTS: Record<'zsh' | 'bash' | 'fish', string> = {\n zsh: 'printf \"\\\\nN:%s\" \"$(whence -p \"$1\")\"; printf \"\\\\nK:%s\" \"$(whence -w \"$1\")\"',\n bash: 'shopt -s expand_aliases; [ -r \"$HOME/.bashrc\" ] && . \"$HOME/.bashrc\" 2>/dev/null; printf \"\\\\nN:%s\" \"$(type -P \"$1\")\"; printf \"\\\\nK:%s\" \"$(type -t \"$1\")\"',\n fish: 'printf \"\\\\nN:%s\" (type -P \"$argv[1]\"); printf \"\\\\nK:%s\" (type -t \"$argv[1]\")',\n};\n\n/** Run the probe; always resolves (never rejects), reporting a timeout. */\nfunction probeCommand(\n name: string,\n shell: string,\n opts: ResolveOptions,\n): Promise<{ stdout: string; timedOut: boolean }> {\n const kind = shellKind(shell);\n const timeoutMs = opts.timeoutMs ?? PROBE_TIMEOUT_MS;\n return new Promise((resolve) => {\n const script = kind ? PROBE_SCRIPTS[kind] : '';\n const flags = kind ? PROBE_FLAGS[kind] : '-lic';\n // bash/zsh consume a positional `$0` (the `noirbridge` dummy) and read the\n // command name from `$1`; fish has NO `$0` — its positional args ARE `$argv`\n // starting at $argv[1] — so the dummy must be omitted there or $argv[1] would\n // resolve the literal \"noirbridge\" and the probe would always come back empty.\n const probeArgs = kind === 'fish' ? [name] : ['noirbridge', name];\n const child = spawn(shell, [flags, script, ...probeArgs], {\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: true, // new process group so the timeout can kill rc-spawned children\n ...(opts.env ? { env: opts.env as NodeJS.ProcessEnv } : {}),\n });\n let stdout = '';\n child.stdout.setEncoding('utf8');\n child.stdout.on('data', (c: string) => {\n stdout += c;\n });\n child.stderr.resume(); // rc noise (e.g. \"no job control\") — ignored\n const timer = setTimeout(() => {\n if (child.pid) {\n try {\n process.kill(-child.pid, 'SIGKILL'); // orphan guard: group, not just shell\n } catch {\n /* already gone */\n }\n }\n resolve({ stdout, timedOut: true });\n }, timeoutMs);\n child.on('error', () => {\n clearTimeout(timer);\n resolve({ stdout, timedOut: false });\n });\n child.on('close', () => {\n clearTimeout(timer);\n resolve({ stdout, timedOut: false });\n });\n });\n}\n\n/** Cache resolved absolute paths per (shell, name) so repeated runs skip the probe. */\nconst pathCache = new Map<string, string>();\n\n/**\n * Resolve `name` through the user's interactive shell. Returns `path` for a\n * PATH executable (respawn directly, clean stream-json pipe), `alias`/`function`\n * for shell entities (bridge execution needed), `none` for a genuine miss,\n * `error` for a probe timeout, or `unsupported` when the guards reject.\n */\nexport async function resolveCommandViaShell(\n name: string,\n opts: ResolveOptions = {},\n): Promise<ShellResolution> {\n const { env = process.env } = opts;\n if (!isEligibleForShellFallback(name, opts)) return { kind: 'unsupported' };\n const shell = env.SHELL as string;\n const cached = pathCache.get(`${shell}:${name}`);\n if (cached) return { kind: 'path', path: cached, shell };\n const probe = await probeCommand(name, shell, opts);\n if (probe.timedOut) {\n return {\n kind: 'error',\n message: `shell resolution timed out after ${opts.timeoutMs ?? PROBE_TIMEOUT_MS}ms`,\n };\n }\n const { path, kind } = parseProbeOutput(probe.stdout);\n const cls = classifyResolution(path, kind);\n if (cls === 'path') {\n pathCache.set(`${shell}:${name}`, path);\n return { kind: 'path', path, shell };\n }\n if (cls === 'alias' || cls === 'function') return { kind: cls, shell };\n return { kind: 'none', shell };\n}\n\nexport interface BridgeSpec {\n readonly binary: string;\n readonly args: readonly string[];\n}\n\n/**\n * Build the bridge spawn for an alias/function: run the user's interactive\n * shell and execute `NAME \"$@\"` (fish: `NAME $argv`). The host's flags + prompt\n * are appended as positional args after the dummy `$0` (`noirbridge`) — they are\n * forwarded verbatim by the shell and never parsed. bash re-sources .bashrc\n * (login shells skip it) so the alias/function defined there is available.\n */\nexport function buildBridgeArgs(\n name: string,\n hostArgs: readonly string[],\n shell: string,\n): BridgeSpec {\n const kind = shellKind(shell) ?? 'zsh';\n if (kind === 'fish') {\n // fish has no `$0`; positional args ARE `$argv` from $argv[1], so no dummy.\n return { binary: shell, args: ['-ic', `${name} $argv`, ...hostArgs] };\n }\n if (kind === 'bash') {\n return {\n binary: shell,\n args: [\n '-lic',\n `shopt -s expand_aliases; [ -r \"$HOME/.bashrc\" ] && . \"$HOME/.bashrc\" 2>/dev/null; ${name} \"$@\"`,\n 'noirbridge',\n ...hostArgs,\n ],\n };\n }\n return { binary: shell, args: ['-lic', `${name} \"$@\"`, 'noirbridge', ...hostArgs] };\n}\n","// The host orchestrator. Drives the host agentic CLI as a\n// headless subprocess and consumes its `stream-json` event stream, rather than\n// Noir running its own model + tool loop (impossible by construction — the\n// model request type has no tools/stream parameter).\n//\n// This module is the PURE, testable core:\n// - `resolveHostRun` — the spawn command for a host (custom binary wins over\n// the host default, so users with multiple profiles — e.g. `claude` vs\n// `claude-work` — can point Noir at their own binary without a restart).\n// - `parseStreamLine` / `normalizeStreamEvent` — map a raw `stream-json` line\n// to a small `NoirEvent` union (the `init`/`assistant`/`result` payloads are\n// the stable, shared contract across hosts).\n// - `UsageReducer` — accumulates token/cost with the `max usage per\n// message.id` rule (Claude emits one JSONL line PER content block of an\n// assistant message, each line's `usage` a CUMULATIVE snapshot of that\n// message — summing lines over-counts ~2.5-3x).\n// - `runHost` — the spawn + readline integration over that core.\n\nimport { spawn } from 'node:child_process';\nimport { constants } from 'node:os';\nimport { createInterface } from 'node:readline';\nimport type { HostId } from '@noir-ai/adapters';\nimport { INTERRUPT_GRACE_MS } from './run-timing.js';\nimport { buildBridgeArgs, resolveCommandViaShell } from './shell-bridge.js';\n\n/** A resolved spawn command: binary + the headless flags appended before prompt. */\nexport interface HostRunSpec {\n readonly binary: string;\n readonly flags: readonly string[];\n}\n\n/** Default headless binary per host. `null` = not a spawnable CLI. */\nconst HOST_BINARY: Record<HostId, string | null> = {\n claude: 'claude',\n gemini: 'gemini',\n opencode: 'opencode',\n cursor: 'cursor-agent',\n 'agents-md': null, // emits AGENTS.md — a file host, not a spawnable CLI\n};\n\n/**\n * Headless flags per host. `claude` is the regression anchor (fully specified);\n * the others use the same `-p --output-format stream-json` contract where the\n * host supports it, and remain overridable via the custom-binary path.\n *\n * `--include-partial-messages` is what turns the stream from \"one frame per\n * content block\" into \"one frame per token\": the host then emits `stream_event`\n * frames carrying each tool call as it starts and each stretch of assistant text\n * as it is written. A caller that renders live (the TUI's run screen, the\n * terminal status line) needs those frames to show progress while the answer is\n * still being produced, rather than only once a whole block has landed.\n */\nconst HOST_FLAGS: Record<HostId, readonly string[]> = {\n claude: ['-p', '--output-format', 'stream-json', '--verbose', '--include-partial-messages'],\n gemini: ['-p', '--output-format', 'stream-json'],\n opencode: ['run'],\n cursor: ['-p'],\n 'agents-md': [],\n};\n\n/**\n * Resolve the spawn command for `host`. `customBinary` (the user's own profile\n * command, e.g. `claude-work`) wins over the host default. Returns `null` when\n * the host has no spawnable CLI (agents-md).\n */\nexport function resolveHostRun(host: HostId, customBinary?: string): HostRunSpec | null {\n const binary = customBinary && customBinary.length > 0 ? customBinary : HOST_BINARY[host];\n if (!binary) return null;\n return { binary, flags: HOST_FLAGS[host] };\n}\n\n// ---------------------------------------------------------------------------\n// Event normalization\n// ---------------------------------------------------------------------------\n\n/** A token-usage snapshot (the fields of the stream-json `usage` object). */\nexport interface TokenUsage {\n readonly inputTokens?: number;\n readonly outputTokens?: number;\n readonly cacheReadTokens?: number;\n}\n\n/** The normalized event union — the shared, host-agnostic payload. */\nexport type NoirEvent =\n | { readonly kind: 'init'; readonly sessionId?: string; readonly model?: string }\n | {\n readonly kind: 'assistant';\n readonly messageId?: string;\n readonly text?: string;\n /**\n * The tool calls the message made alongside its text, in the order it\n * made them. A consumer that renders tools must show these: a host that\n * streams no partial messages has no other way to say that a message\n * which said something also called something.\n */\n readonly tools?: readonly string[];\n readonly usage?: TokenUsage;\n /** Set when the host flags the assistant message as an API error\n * (`is_api_error_message:true` or an `error` category string) — its text\n * is a diagnostic, never the answer. */\n readonly isError?: boolean;\n /** The host's error category string (e.g. \"authentication_failed\") when\n * the assistant event carries one — used for actionable auth guidance. */\n readonly errorCategory?: string;\n }\n | {\n readonly kind: 'result';\n readonly isError: boolean;\n readonly durationMs?: number;\n readonly numTurns?: number;\n readonly totalCostUsd?: number;\n readonly usage?: TokenUsage;\n }\n /**\n * A tool call the host started. Carries no result text: it exists so a live\n * render can show WHAT the host is doing while it does it, which is the only\n * progress signal available between two stretches of assistant text.\n * `messageId`/`usage` are present when the tool call was read out of a\n * completed assistant message (a host that reports no partial stream), so\n * that message's tokens are still accounted for.\n */\n | {\n readonly kind: 'tool';\n readonly name: string;\n readonly messageId?: string;\n readonly usage?: TokenUsage;\n }\n /**\n * A stretch of assistant text as it is written. Provisional by construction:\n * the host re-reports the same words in the completed `assistant` message\n * that closes the block, so a consumer that renders both must reconcile them\n * rather than concatenating.\n */\n | { readonly kind: 'delta'; readonly text: string }\n /** A frame the normalizer does not model. `subtype` names what was dropped. */\n | { readonly kind: 'other'; readonly subtype?: string };\n\nfunction asObj(v: unknown): Record<string, unknown> | null {\n return typeof v === 'object' && v !== null ? (v as Record<string, unknown>) : null;\n}\n\nfunction str(v: unknown): string | undefined {\n return typeof v === 'string' ? v : undefined;\n}\n\nfunction num(v: unknown): number | undefined {\n return typeof v === 'number' && Number.isFinite(v) ? v : undefined;\n}\n\n/** Map a POSIX signal name (e.g. \"SIGKILL\") to its numeric value, else undefined. */\nfunction signalNumber(signal: NodeJS.Signals): number | undefined {\n const n = (constants.signals as Record<string, number>)[signal];\n return typeof n === 'number' ? n : undefined;\n}\n\nfunction usageFrom(v: unknown): TokenUsage | undefined {\n const o = asObj(v);\n if (!o) return undefined;\n const input = num(o.input_tokens);\n const output = num(o.output_tokens);\n const cache = num(o.cache_read_input_tokens) ?? num(o.cache_creation_input_tokens);\n if (input === undefined && output === undefined && cache === undefined) return undefined;\n return { inputTokens: input, outputTokens: output, cacheReadTokens: cache };\n}\n\n/** What an assistant message's content carries, split by kind. */\ninterface MessageContent {\n /** The concatenated text blocks, when the message has any. */\n readonly text?: string;\n /** The names of the tool calls the message made, in order. */\n readonly tools?: readonly string[];\n}\n\n/**\n * Split an assistant message's content into its text and its tool calls.\n *\n * A message can be either, and a tool-only message carries no text at all —\n * which is why an empty text result must not be read as \"nothing happened\": a\n * turn whose whole content is a tool call is the common case in an agentic run,\n * and the caller needs the tool's name to show what the host is doing.\n */\nfunction messageContent(message: Record<string, unknown> | null): MessageContent {\n if (!message) return {};\n const content = message.content;\n if (typeof content === 'string') return { text: content };\n if (!Array.isArray(content)) return {};\n const parts: string[] = [];\n const tools: string[] = [];\n for (const block of content) {\n const b = asObj(block);\n if (!b) continue;\n if (typeof b.text === 'string') parts.push(b.text);\n else if (b.type === 'tool_use' && typeof b.name === 'string') tools.push(b.name);\n }\n return {\n ...(parts.length > 0 ? { text: parts.join('') } : {}),\n ...(tools.length > 0 ? { tools } : {}),\n };\n}\n\n/** Parse a raw stream-json line to a JSON value, or `null` for blank/invalid. */\nexport function parseStreamLine(line: string): unknown | null {\n const trimmed = line.trim();\n if (trimmed.length === 0) return null;\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n}\n\n/** Normalize a raw stream-json event to a {@link NoirEvent}, or `null`. */\nexport function normalizeStreamEvent(raw: unknown): NoirEvent | null {\n const r = asObj(raw);\n if (!r) return null;\n const type = r.type;\n if (type === 'system') {\n if (r.subtype === 'init') {\n return { kind: 'init', sessionId: str(r.session_id), model: str(r.model) };\n }\n return { kind: 'other', subtype: str(r.subtype) ?? 'system' };\n }\n if (type === 'assistant') {\n const message = asObj(r.message);\n const content = messageContent(message);\n const messageId = message ? str(message.id) : undefined;\n const usage = message ? usageFrom(message.usage) : undefined;\n const error = {\n // An API error message is a diagnostic, not the answer — flag it so the\n // caller never streams it as assistant text (optional-when-true keeps\n // exact-shape consumers of the existing union intact).\n ...(r.is_api_error_message === true || typeof r.error === 'string'\n ? {\n isError: true as const,\n ...(typeof r.error === 'string' ? { errorCategory: r.error } : {}),\n }\n : {}),\n };\n // A message with text reports text; one whose whole content is a tool call\n // reports the call, with the message's usage riding along so the turn is\n // still counted (a tool-only message is often the only carrier of its own\n // message id). A message that does both reports both: the text is the\n // answer and the call is what the host went on to do, and dropping the call\n // here would leave a host with no partial feed showing a turn that looks\n // like it only talked.\n if (content.text !== undefined) {\n return {\n kind: 'assistant',\n messageId,\n text: content.text,\n usage,\n ...(content.tools === undefined ? {} : { tools: content.tools }),\n ...error,\n };\n }\n if (content.tools !== undefined) {\n // One announcement per message, naming its first tool call. The host\n // writes one content block per line, so this is the whole message; a host\n // that packs several calls into one message still reports each of them\n // separately on the incremental feed.\n return { kind: 'tool', name: content.tools[0] as string, messageId, usage };\n }\n return { kind: 'assistant', messageId, usage, ...error };\n }\n if (type === 'result') {\n return {\n kind: 'result',\n isError: r.is_error === true,\n durationMs: num(r.duration_ms),\n numTurns: num(r.num_turns),\n totalCostUsd: num(r.total_cost_usd),\n usage: usageFrom(r.usage),\n };\n }\n if (type === 'stream_event') {\n return normalizeStreamEventInner(asObj(r.event));\n }\n return { kind: 'other', ...(typeof type === 'string' ? { subtype: type } : {}) };\n}\n\n/**\n * Normalize one frame of a `stream_event` (the incremental feed a host emits\n * when it is asked for partial messages). Only the two frames that carry live\n * progress are modelled — a tool call as it starts, and a stretch of assistant\n * text as it is written; the boundary frames around them are noise.\n */\nfunction normalizeStreamEventInner(inner: Record<string, unknown> | null): NoirEvent {\n if (!inner) return { kind: 'other', subtype: 'stream_event' };\n const innerType = str(inner.type);\n if (innerType === 'content_block_start') {\n const block = asObj(inner.content_block);\n const name = block ? str(block.name) : undefined;\n if (block && str(block.type) === 'tool_use' && name !== undefined) {\n return { kind: 'tool', name };\n }\n }\n if (innerType === 'content_block_delta') {\n const delta = asObj(inner.delta);\n const text = delta ? str(delta.text) : undefined;\n if (delta && delta.type === 'text_delta' && text !== undefined) {\n return { kind: 'delta', text };\n }\n }\n return { kind: 'other', subtype: innerType ?? 'stream_event' };\n}\n\n// ---------------------------------------------------------------------------\n// Usage reduction\n// ---------------------------------------------------------------------------\n\n/** A monotonic token/cost accumulator snapshot. */\nexport interface UsageSnapshot {\n readonly inputTokens: number;\n readonly outputTokens: number;\n readonly totalCostUsd: number;\n readonly numTurns: number;\n}\n\n/**\n * Accumulates token/cost from a `stream-json` feed with the dedup rule: take\n * the `max` usage per `message.id`, never sum lines. Claude emits one line per\n * content block of an assistant message, each carrying that message's\n * CUMULATIVE usage — so the correct per-message total is the max, and the\n * session total is the sum of per-message maxes.\n */\nexport class UsageReducer {\n private readonly maxByMessage = new Map<string, { input: number; output: number }>();\n private input = 0;\n private output = 0;\n private costUsd = 0;\n private turns = 0;\n\n /** Feed one normalized event into the reducer. */\n add(event: NoirEvent): void {\n if (event.kind === 'result') {\n if (event.totalCostUsd !== undefined) this.costUsd = event.totalCostUsd;\n if (event.numTurns !== undefined) this.turns = event.numTurns;\n return;\n }\n // A tool announcement carries the usage of the message it came out of, and\n // for a tool-only message it is the only carrier — so it folds through the\n // same per-message rule rather than being dropped. The rule itself is\n // unchanged: one entry per message id, taking the max.\n const usage = event.kind === 'assistant' || event.kind === 'tool' ? event.usage : undefined;\n const key = event.kind === 'assistant' || event.kind === 'tool' ? event.messageId : undefined;\n if (!key || !usage) return;\n const input = usage.inputTokens ?? 0;\n const output = usage.outputTokens ?? 0;\n const prev = this.maxByMessage.get(key);\n if (!prev) {\n this.maxByMessage.set(key, { input, output });\n this.input += input;\n this.output += output;\n return;\n }\n // Cumulative snapshot — only the growth over the last-seen max counts.\n this.input += Math.max(0, input - prev.input);\n this.output += Math.max(0, output - prev.output);\n this.maxByMessage.set(key, {\n input: Math.max(prev.input, input),\n output: Math.max(prev.output, output),\n });\n }\n\n /** The current accumulator snapshot. */\n snapshot(): UsageSnapshot {\n return {\n inputTokens: this.input,\n outputTokens: this.output,\n totalCostUsd: this.costUsd,\n numTurns: this.turns,\n };\n }\n}\n\n// ---------------------------------------------------------------------------\n// Spawn integration\n// ---------------------------------------------------------------------------\n\n/**\n * A host child a caller can stop, as the cancel path sees it: what `spawn`\n * returns, narrowed to the three things a kill ladder needs. A caller that has\n * this handle can stop THAT process, rather than hoping a signal reaches it.\n */\nexport interface HostChild {\n readonly pid?: number;\n /** Send it a signal. False when it is already gone. */\n kill(signal?: NodeJS.Signals): boolean;\n /** Fires when it is reaped, which is when the handle stops being useful. */\n once(event: 'exit', listener: () => void): unknown;\n}\n\n/**\n * Signal a child — the one and only safe way to do it.\n *\n * A spawn that failed (ENOENT, a bad interpreter) produces a handle with no\n * pid, and signalling that is NOT a harmless no-op: Node hands pid 0 to\n * `kill(2)`, which means \"every process in my own process group\". So an\n * unguarded `child.kill()` while a bad command is failing would take out Noir,\n * whatever shares its job (a pipeline peer, a wrapper script) and the terminal\n * job around them — the opposite of stopping one host. Callers that skip the\n * pid check are the bug; this is the check.\n */\nexport function signalChild(child: HostChild, signal: NodeJS.Signals): void {\n if (child.pid === undefined) return;\n child.kill(signal);\n}\n\n/**\n * How often a bridged child is offered the polite signal again, and for how long.\n *\n * A bridged child is the user's INTERACTIVE shell until its alias hands the\n * process over to the host, and an interactive shell discards a SIGTERM that\n * arrives while it is still starting up (sourcing the rc files): the signal is\n * gone before the alias runs, so the host never sees it and only the caller's\n * escalation ends the run. One signal is therefore not enough for a bridged run\n * — it is offered again on this beat until the child is reaped, or until this\n * window closes (the same order as the interrupt grace, so a host that ignores\n * every signal is still ended by the escalation).\n *\n * A directly spawned host needs none of this: nothing interposes between the\n * spawn and the process that has to die.\n */\nexport const BRIDGE_POLITE_RESEND_MS = 250;\nexport const BRIDGE_POLITE_WINDOW_MS = INTERRUPT_GRACE_MS;\n\nexport interface RunHostOptions {\n readonly host: HostId;\n readonly prompt: string;\n readonly customBinary?: string;\n /** Extra args appended after the host's headless flags (run-profile `args`). */\n readonly extraArgs?: readonly string[];\n /** Environment for the host spawn + shell fallback (defaults to process.env). */\n readonly env?: Record<string, string | undefined>;\n /** Raw stream-json line (for transcript persistence). */\n onLine?: (line: string) => void;\n /** Normalized event (for streaming render). */\n onEvent?: (event: NoirEvent) => void;\n /**\n * The child, as soon as it exists — and again for every later spawn of the\n * same run, because a command resolved through the shell bridge is a second\n * child. Handing it over is what lets a caller kill the process itself: a\n * signal that reached Noir is not otherwise addressed to the host it spawned.\n */\n onChild?: (child: HostChild) => void;\n /**\n * Cancel the run: aborting this sends the host child a `SIGTERM`. A signal\n * that is already aborted kills the child as soon as it spawns, so a caller\n * that raced its own cancellation does not leak a process. Escalation (a\n * SIGKILL for a host that ignores the polite signal) is the caller's, not\n * this function's — the run still resolves through the ordinary close path\n * with the kill folded into its result.\n */\n signal?: AbortSignal;\n}\n\nexport interface RunHostResult {\n readonly exitCode: number;\n readonly usage: UsageSnapshot;\n readonly eventCount: number;\n /** Host stderr (surfaced on a non-zero exit so errors are not swallowed). */\n readonly stderr: string;\n /** Whether the host signalled an error (non-zero exit OR a stream-json\n * `is_error:true` result / API-error assistant event). Exit code alone is\n * not reliable — claude can exit 0 with `is_error:true` (#79500). */\n readonly isError: boolean;\n /** The first API-error assistant text (e.g. \"Not logged in · Please run\n * /login\"), quoted into the CLI's failure message. */\n readonly errorText?: string;\n /** The first API-error category (e.g. \"authentication_failed\"), for\n * actionable auth guidance in the CLI. */\n readonly errorCategory?: string;\n}\n\n/**\n * Spawn the host headless and consume its `stream-json` over a stdio pipe.\n * Rejects when the host has no spawnable CLI or the binary fails to spawn;\n * otherwise resolves with the exit code + the reduced usage snapshot.\n */\nexport function runHost(opts: RunHostOptions): Promise<RunHostResult> {\n const spec = resolveHostRun(opts.host, opts.customBinary);\n if (!spec) {\n return Promise.reject(\n new Error(`host '${opts.host}' is not a spawnable CLI (no default command)`),\n );\n }\n return spawnAndConsume(\n spec.binary,\n [...spec.flags, ...(opts.extraArgs ?? []), opts.prompt],\n opts,\n false,\n );\n}\n\n/**\n * Spawn `binary` with `args` and consume its stream-json. On an ENOENT spawn\n * error (and not already a shell-bridge run) it attempts the shell fallback:\n * the name may be an alias/function or a PATH entry only visible inside the\n * user's interactive shell — see `shell-bridge.ts` for the safety model.\n *\n * `bridged` marks the spawn whose process is an interactive shell before it is\n * the host, which is the one case where a single polite signal can be lost —\n * see {@link BRIDGE_POLITE_RESEND_MS}.\n */\nfunction spawnAndConsume(\n binary: string,\n args: readonly string[],\n opts: RunHostOptions,\n shellRun: boolean,\n bridged = false,\n): Promise<RunHostResult> {\n return new Promise((resolve, reject) => {\n const child = spawn(binary, [...args], {\n stdio: ['ignore', 'pipe', 'pipe'],\n ...(opts.env ? { env: opts.env as NodeJS.ProcessEnv } : {}),\n });\n opts.onChild?.(child);\n const reducer = new UsageReducer();\n let eventCount = 0;\n let stderrBuf = '';\n let errored = false;\n let errorText: string | undefined;\n let errorCategory: string | undefined;\n // A failed spawn fires BOTH 'error' and 'close' (close with a synthetic\n // code like -2). The 'close' handler must defer to the 'error' path so the\n // shell fallback (or the rejection) owns the resolution, never a -2 result.\n let spawnError: unknown = null;\n\n // Cancellation. The listener is dropped the moment the child is gone, so a\n // controller reused across runs does not accumulate dead children.\n const { signal } = opts;\n let resend: NodeJS.Timeout | undefined;\n let offersLeft = Math.ceil(BRIDGE_POLITE_WINDOW_MS / BRIDGE_POLITE_RESEND_MS);\n const stopResend = (): void => {\n if (resend === undefined) return;\n clearTimeout(resend);\n resend = undefined;\n };\n /** Offer the polite signal again while there is still a child to take it. */\n const offerAgain = (): void => {\n if (offersLeft <= 0) return;\n offersLeft -= 1;\n resend = setTimeout(() => {\n resend = undefined;\n signalChild(child, 'SIGTERM');\n offerAgain();\n }, BRIDGE_POLITE_RESEND_MS);\n };\n const onAbort = (): void => {\n signalChild(child, 'SIGTERM');\n if (bridged) offerAgain();\n };\n if (signal) {\n if (signal.aborted) onAbort();\n else signal.addEventListener('abort', onAbort, { once: true });\n }\n const detachAbort = (): void => {\n signal?.removeEventListener('abort', onAbort);\n stopResend();\n };\n\n child.on('error', (err) => {\n spawnError = err;\n detachAbort();\n if (!shellRun && (err as NodeJS.ErrnoException).code === 'ENOENT') {\n void shellFallback(binary, args, opts, resolve, reject, err);\n return;\n }\n reject(err);\n });\n\n const rl = createInterface({ input: child.stdout });\n rl.on('line', (line) => {\n opts.onLine?.(line);\n const raw = parseStreamLine(line);\n if (raw === null) return;\n const event = normalizeStreamEvent(raw);\n if (event === null) return;\n eventCount += 1;\n if (event.kind === 'assistant' && event.isError === true) {\n errored = true;\n if (errorText === undefined && event.text && event.text.length > 0) errorText = event.text;\n if (errorCategory === undefined && event.errorCategory) errorCategory = event.errorCategory;\n } else if (event.kind === 'result' && event.isError === true) {\n errored = true;\n }\n reducer.add(event);\n opts.onEvent?.(event);\n });\n\n child.stderr.setEncoding('utf8');\n child.stderr.on('data', (chunk: string) => {\n stderrBuf += chunk;\n });\n\n child.on('close', (code, signal) => {\n detachAbort();\n if (spawnError !== null) return; // the error/fallback path owns this spawn\n // A host terminated by a signal (crash/SIGKILL/OOM) yields code === null\n // with no result event — without this it would be reported as exit 0\n // success. Fold the kill into a non-zero exit + isError (128+n is the\n // shell convention for signal termination).\n const killed = code === null && signal != null;\n resolve({\n exitCode: killed ? 128 + (signalNumber(signal) ?? 0) : (code ?? 0),\n usage: reducer.snapshot(),\n eventCount,\n stderr: stderrBuf,\n isError: errored || killed,\n errorText: killed && errorText === undefined ? `terminated by signal ${signal}` : errorText,\n errorCategory,\n });\n });\n });\n}\n\n/**\n * ENOENT shell-bridge attempt: probe the user's shell for `binary`; respawn the\n * resolved absolute path directly, or bridge an alias/function via the shell\n * (prompt + flags travel only as argv). On any failure re-reject the original\n * ENOENT so run.ts builds the friendly \"no executable found\" message.\n */\nasync function shellFallback(\n binary: string,\n args: readonly string[],\n opts: RunHostOptions,\n resolve: (r: RunHostResult) => void,\n reject: (e: unknown) => void,\n originalErr: unknown,\n): Promise<void> {\n try {\n const res = await resolveCommandViaShell(binary, { env: opts.env ?? process.env });\n if (res.kind === 'path') {\n resolve(await spawnAndConsume(res.path, args, opts, true));\n return;\n }\n if (res.kind === 'alias' || res.kind === 'function') {\n const bridge = buildBridgeArgs(binary, args, res.shell);\n // `bridged`: this child is the user's interactive shell until the alias\n // execs the host, so its stop has to survive that shell's startup.\n resolve(await spawnAndConsume(bridge.binary, bridge.args, opts, true, true));\n return;\n }\n } catch {\n // fall through to the original ENOENT below\n }\n reject(originalErr);\n}\n","// Post-run actions for `noir run`: a short menu offered once a successful run\n// has finished, so the answer that just streamed past can be put somewhere —\n// memory, a task finding, a handoff artifact, a file — or the session can be\n// continued, without leaving the terminal to re-open an editor or the host.\n//\n// Every action delegates to a command seam that already exists (`memory\n// capture`, `task research record`, `handoff`, a fresh host invocation, a file\n// write) instead of talking to the daemon itself: the single-writer, privacy\n// and exit-code contracts keep living in one place per concern. \"Continue this\n// session\" is a NEW invocation of the same single-shot run path — the host is\n// spawned, streams, and exits exactly as before; Noir never keeps a session\n// alive between turns.\n//\n// Three laws hold here. The menu is offered only on a terminal that allows\n// prompts, so `--json`, `--no-input`, CI and piped runs never see it and keep\n// their exact previous output. Nothing here may change the finished run's exit\n// code: an action that fails (daemon down, no active task, an unwritable path)\n// is reported and dropped, never escalated. And the menu is ONE definition\n// shared by two surfaces — the terminal prompt and the TUI's overlay — so the\n// actions, their order and their wording cannot drift between them: the surface\n// decides how a row is drawn and where a value is typed, the performer decides\n// what a choice does.\n\nimport { writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { CommanderError } from 'commander';\nimport { handoff } from './commands/handoff.js';\nimport { memoryCapture } from './commands/memory.js';\nimport { taskResearchRecord } from './commands/task.js';\nimport { type CliOptions, isInteractive, NoirCliError, success, warn } from './output.js';\n\n/** What the menu can do with a finished run. */\nexport type PostRunAction = 'memory' | 'research' | 'handoff' | 'resume' | 'save' | 'dismiss';\n\n/** One rendered menu row. */\nexport interface PostRunMenuOption {\n readonly value: PostRunAction;\n readonly label: string;\n readonly hint: string;\n /**\n * The value this row needs collected before it can run — a prompt for a\n * continuation, a path for a file write. A surface that can ask for it (the\n * terminal prompt does; the TUI draws its own input line) reads the label and\n * placeholder from here rather than guessing them, so the question it asks is\n * the same one.\n */\n readonly needsValue?: { readonly label: string; readonly placeholder: string };\n}\n\n/** What performing an action did. Reported, never thrown. */\nexport interface PostRunOutcome {\n /** Whether the action did what it said it would. */\n readonly ok: boolean;\n /** The line to show. Empty when the action's own command output is the report. */\n readonly message: string;\n /**\n * The underlying cause, quoted only when it has not already been reported by\n * the command that threw.\n */\n readonly detail?: string;\n /** How the line reads: a confirmation, or a caution. Defaults from `ok`. */\n readonly tone?: 'confirm' | 'caution';\n}\n\nexport interface PostRunActionInput {\n /** The host's answer as plain text — what was streamed to stdout. */\n readonly answer: string;\n /** Path of the raw stream-json transcript, quoted in the prompts. */\n readonly transcript: string;\n /** The host's session id, when it reported one. */\n readonly sessionId?: string;\n /** The host binary that just ran, named in the prompts. */\n readonly host: string;\n readonly opts: CliOptions;\n /**\n * Continue an existing session with a prompt collected fresh. Absent when\n * continuing is not on offer (no session id was reported, or this run is\n * itself the continuation) — the menu drops the option rather than rendering\n * a dead row.\n */\n readonly resume?: (prompt: string, sessionId: string) => Promise<void>;\n /**\n * Whether a continuation is on offer, overriding the `resume` rule above. A\n * surface that continues a session ITSELF — the TUI starts another turn on\n * the same screen — says so here, rather than having to hand over a function\n * it would never call. Absent means \"ask the `resume` seam\".\n */\n readonly canContinue?: boolean;\n}\n\n/** The provenance a post-run capture carries into memory. */\nconst CAPTURE_EVENT = 'capture';\n\n/** Provenance for a research finding: the run itself is the evidence. */\nconst RESEARCH_SOURCE = 'noir run';\n\n/** The daemon caps a research finding at 220 characters and refuses more. */\nconst RESEARCH_TEXT_LIMIT = 220;\n\n/** How each action is named while it runs, and in a failure it reports. */\nconst ACTION_LABEL: Record<PostRunAction, string> = {\n memory: 'Saving to memory',\n research: 'Recording the finding',\n handoff: 'Writing the handoff',\n resume: 'Continuing the session',\n save: 'Writing the file',\n dismiss: 'Dismissing',\n};\n\n/** The @clack module, typed from its dynamic import (it loads lazily). */\ntype Clack = typeof import('@clack/prompts');\n\n/**\n * The actions on offer for this run, in the order they are shown. Only the ones\n * that actually have something to work with: an empty answer has nothing to\n * save, and a run that reported no session id has nothing to continue.\n */\nexport function postRunActions(input: PostRunActionInput): PostRunMenuOption[] {\n const answer = input.answer.trim();\n const canContinue =\n input.canContinue ?? (input.sessionId !== undefined && input.resume !== undefined);\n const options: PostRunMenuOption[] = [];\n if (answer.length > 0) {\n options.push({\n value: 'memory',\n label: 'Save answer to memory',\n hint: \"distil it into this project's memory (needs the daemon)\",\n });\n options.push({\n value: 'research',\n label: 'Add as task research',\n hint: 'record it as a finding on the active task',\n });\n }\n options.push({\n value: 'handoff',\n label: 'Write a handoff artifact',\n hint: 'snapshot the task, the next step and a seed for your host',\n });\n if (canContinue) {\n options.push({\n value: 'resume',\n label: 'Continue this session',\n hint: `ask ${input.host} a follow-up in the same session`,\n needsValue: {\n label: 'follow-up prompt',\n placeholder: 'e.g. now write the tests for that',\n },\n });\n }\n if (answer.length > 0) {\n options.push({\n value: 'save',\n label: 'Save answer to a file',\n hint: 'write the answer text to a path you choose',\n needsValue: { label: 'file path', placeholder: 'e.g. notes/answer.md' },\n });\n }\n options.push({\n value: 'dismiss',\n label: 'Dismiss',\n hint: 'nothing further — the run is already complete',\n });\n return options;\n}\n\n/**\n * Do one chosen action and report what happened. Never throws: the caller is\n * showing the outcome of a run that has already succeeded, and a follow-up that\n * failed must not become that run's result.\n *\n * `value` is whatever the surface collected for a row that declares\n * {@link PostRunMenuOption.needsValue} — a prompt, or a file path.\n */\nexport async function performPostRunAction(\n choice: PostRunAction,\n input: PostRunActionInput,\n value?: string,\n): Promise<PostRunOutcome> {\n const label = ACTION_LABEL[choice];\n try {\n switch (choice) {\n case 'memory':\n await saveToMemory(input);\n return { ok: true, message: '' };\n case 'research':\n await recordAsResearch(input);\n return { ok: true, message: '' };\n case 'handoff':\n await writeHandoff(input);\n return { ok: true, message: '' };\n case 'resume':\n return await continueSession(input, value);\n case 'save':\n return saveAnswerToFile(input, value);\n case 'dismiss':\n return { ok: true, message: '' };\n }\n } catch (err) {\n // A path that already reported its own diagnostic (`fail()` and the daemon\n // client both write before throwing) is not quoted back a second time.\n const quoted =\n err instanceof CommanderError || err instanceof NoirCliError\n ? undefined\n : `${label}: ${err instanceof Error ? err.message : String(err)}`;\n return {\n ok: false,\n message: `${label} did not complete — the run's result stands.`,\n ...(quoted === undefined ? {} : { detail: quoted }),\n };\n }\n}\n\n/**\n * Offer the actions for a finished run. Returns immediately — before loading\n * the prompt library — when the invocation is not interactive, which is what\n * keeps a scripted or piped run byte-identical to one made before this menu\n * existed. A cancelled, dismissed, or undrawable menu is a no-op, not an error:\n * the run has already succeeded and been reported, so the prompt itself can\n * never be the thing that fails it.\n */\nexport async function offerPostRunActions(input: PostRunActionInput): Promise<void> {\n if (!isInteractive(input.opts)) return;\n\n const options = postRunActions(input);\n // A menu whose only row is \"Dismiss\" is not worth a keystroke.\n if (options.length <= 1) return;\n\n const menu = await openMenu(options);\n if (menu === undefined) return;\n const chosen = options.find((o) => o.value === menu.choice);\n if (chosen === undefined) return;\n\n // The value is collected on the same prompt library, and a cancelled or\n // unusable one abandons the action the same way the menu itself does.\n const value =\n chosen.needsValue === undefined ? undefined : await askForValue(menu.clack, chosen, input);\n if (chosen.needsValue !== undefined && value === undefined) return;\n\n report(await performPostRunAction(menu.choice, input, value), input.opts);\n}\n\n/**\n * Render what an action did. A confirmation prints as one; a caution — or a\n * failure — prints as a diagnostic, and a failure quotes its cause only when\n * the cause has not already been printed by whoever threw.\n */\nfunction report(outcome: PostRunOutcome, opts: CliOptions): void {\n const caution = outcome.tone === 'caution' || (!outcome.ok && outcome.tone !== 'confirm');\n if (outcome.detail !== undefined) warn(outcome.detail, opts);\n if (outcome.message.length === 0) return;\n if (caution) warn(outcome.message, opts);\n else success(outcome.message, opts);\n}\n\n/**\n * Draw the menu and return what was chosen, or `undefined` when the user wants\n * nothing. Loading the prompt library, rendering the question, and reading the\n * answer are all one step on purpose: every way this can fail — the library not\n * loading, the terminal going away between the interactivity gate and the\n * render, stdin erroring — costs the menu and nothing else. It is an offer on a\n * run that has already succeeded and been reported, so it must never be the\n * thing that turns that run into a failure.\n */\nasync function openMenu(\n options: readonly PostRunMenuOption[],\n): Promise<{ clack: Clack; choice: PostRunAction } | undefined> {\n try {\n const clack = await import('@clack/prompts');\n const choice = await clack.select({\n message: 'The run finished. What next?',\n initialValue: 'dismiss',\n // Spread because the prompt library takes a mutable list; the rows\n // themselves are the shared descriptors and are never rewritten.\n options: [...options],\n });\n if (clack.isCancel(choice)) return undefined;\n return { clack, choice: choice as PostRunAction };\n } catch {\n return undefined;\n }\n}\n\n/**\n * Ask for the value a row declared it needs. `undefined` means the user backed\n * out (or the prompt could not be drawn), which abandons the action — an empty\n * answer is NOT a back-out, and is handed on so each action can decide what an\n * empty value means (a generated file name, or nothing to continue with).\n */\nasync function askForValue(\n clack: Clack,\n option: PostRunMenuOption,\n input: PostRunActionInput,\n): Promise<string | undefined> {\n const needs = option.needsValue;\n if (needs === undefined) return undefined;\n const message =\n option.value === 'save'\n ? `Write the answer to a file (the raw transcript is at ${input.transcript})`\n : `What should ${input.host} do next?`;\n try {\n const written = await clack.text({ message, placeholder: needs.placeholder });\n if (clack.isCancel(written)) return undefined;\n return String(written ?? '');\n } catch {\n return undefined;\n }\n}\n\n/** Save the answer through the existing memory-capture command. */\nasync function saveToMemory(input: PostRunActionInput): Promise<void> {\n await memoryCapture({\n ...input.opts,\n content: input.answer,\n eventType: CAPTURE_EVENT,\n });\n}\n\n/** Record the answer as a research finding on the active task. */\nasync function recordAsResearch(input: PostRunActionInput): Promise<void> {\n await taskResearchRecord({\n ...input.opts,\n type: 'discovery',\n text: researchText(input.answer),\n source: RESEARCH_SOURCE,\n });\n}\n\n/**\n * The finding text: whitespace collapsed onto one line and cut to the length\n * the daemon accepts (it refuses more). An over-long answer still lands as a\n * usable pointer rather than failing the whole action.\n */\nfunction researchText(answer: string): string {\n const condensed = answer.replace(/\\s+/g, ' ').trim();\n if (condensed.length <= RESEARCH_TEXT_LIMIT) return condensed;\n return `${condensed.slice(0, RESEARCH_TEXT_LIMIT - 1)}…`;\n}\n\n/** Write the handoff artifact through the existing handoff command. */\nasync function writeHandoff(input: PostRunActionInput): Promise<void> {\n await handoff({ ...input.opts, write: true });\n}\n\n/** Ask the host a follow-up in the session that just finished. */\nasync function continueSession(\n input: PostRunActionInput,\n value: string | undefined,\n): Promise<PostRunOutcome> {\n const { sessionId, resume } = input;\n if (sessionId === undefined || resume === undefined) return { ok: true, message: '' };\n const prompt = (value ?? '').trim();\n if (prompt.length === 0) {\n return {\n ok: true,\n tone: 'caution',\n message: 'No prompt given — the session was not continued.',\n };\n }\n await resume(prompt, sessionId);\n return { ok: true, message: '' };\n}\n\n/** Write the accumulated answer text to a path the user chooses. */\nfunction saveAnswerToFile(input: PostRunActionInput, value: string | undefined): PostRunOutcome {\n const fallback = `noir-run-${stamp()}.md`;\n const chosen = (value ?? '').trim();\n const target = resolve(chosen.length > 0 ? chosen : fallback);\n const body = input.answer.endsWith('\\n') ? input.answer : `${input.answer}\\n`;\n try {\n // The answer can quote credentials or private code, so a newly created\n // file is owner-only — the same reason the transcript is 0600.\n writeFileSync(target, body, { mode: 0o600 });\n } catch (err) {\n return {\n ok: false,\n tone: 'caution',\n message: `could not write ${target}: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n return { ok: true, message: `Answer written to ${target}` };\n}\n\n/** A filesystem-safe stamp for the default file name. */\nfunction stamp(): string {\n return new Date().toISOString().replace(/[:.]/g, '-');\n}\n","// Stopping a host run: the interrupt contract shared by `noir run` and the\n// TUI's live run screen.\n//\n// A run drives a host CLI as a child process. Stopping it is a two-step ladder\n// — the polite signal first, the forceful one after a grace — because a host\n// that ignores the polite signal would otherwise hold the terminal (or the run\n// screen) open for good. The child is never detached: it lives in Noir's own\n// process group, so this ladder is what guarantees it is gone before Noir\n// leaves. An interrupt that reported success and exited early would leave a\n// live host behind, holding the user's credentials and their terminal.\n\nimport { constants } from 'node:os';\nimport { type HostChild, signalChild } from './orchestrator.js';\nimport { INTERRUPT_GRACE_MS } from './run-timing.js';\n\nexport { INTERRUPT_GRACE_MS };\n\n/** The signals a run answers, in the order they are installed. */\nexport const INTERRUPT_SIGNALS = ['SIGINT', 'SIGTERM'] as const;\n\n/** One of the signals a run answers. */\nexport type InterruptSignal = (typeof INTERRUPT_SIGNALS)[number];\n\n/** The exit code a run leaves with when `signal` stopped it: 128 + its number. */\nexport function exitCodeForSignal(signal: NodeJS.Signals): number {\n return 128 + ((constants.signals as Record<string, number>)[signal] ?? 0);\n}\n\n/** What an interrupted run says — the terminal and the run screen alike. */\nexport function interruptedNotice(transcript: string): string {\n return `interrupted · transcript: ${transcript}`;\n}\n\nexport interface RunInterruptOptions {\n /** Grace between the polite signal and the forceful one. */\n readonly graceMs?: number;\n /** Leave at once, without cleaning up. How a second interrupt is honoured. */\n readonly exitNow?: (code: number) => void;\n}\n\n/**\n * The interrupt state of one run: the child that is live, and what has been\n * asked of it.\n *\n * The caller hands over every child a spawn produces (a run whose binary is\n * reached through the shell bridge produces a second one), and calls\n * {@link terminate} to stop — from a signal handler in the terminal, or from a\n * keystroke on the run screen. Both arrive here, so the kill path is the same\n * one in both cases.\n */\nexport class RunInterrupt {\n private readonly graceMs: number;\n private readonly exitNow: (code: number) => void;\n private readonly controller = new AbortController();\n /** The live child, or undefined between spawns. */\n private child: HostChild | undefined;\n /** The child the ladder already ran for — a repeat call is not a new ladder. */\n private stopping: HostChild | undefined;\n private grace: NodeJS.Timeout | undefined;\n private signalName: InterruptSignal | undefined;\n private stopRequested = false;\n private forced = false;\n private watching = false;\n /** The installed listeners, keyed by the signal each answers. */\n private readonly handlers = new Map<InterruptSignal, () => void>();\n\n constructor(opts: RunInterruptOptions = {}) {\n this.graceMs = opts.graceMs ?? INTERRUPT_GRACE_MS;\n this.exitNow =\n opts.exitNow ??\n ((code: number): void => {\n // A second interrupt means \"leave now\". Leaving must not leave the child\n // behind: SIGKILL is untrappable, so a fire-and-forget kill here ends a\n // host that was ignoring the polite signal, even though Noir does not\n // wait for the reap.\n if (this.child !== undefined) signalChild(this.child, 'SIGKILL');\n process.exit(code);\n });\n }\n\n /**\n * The cancel signal for the spawn. Aborting it is what a caller holding only\n * the signal can do; the ladder itself does not depend on it, which matters\n * because the child a run ends up driving is not always the first one spawned.\n */\n get signal(): AbortSignal {\n return this.controller.signal;\n }\n\n /** The signal that stopped the run, or undefined when nothing did. */\n get interruptedBy(): InterruptSignal | undefined {\n return this.signalName;\n }\n\n /** True once a child had to be forced — it did not honour the polite signal. */\n get escalated(): boolean {\n return this.forced;\n }\n\n /**\n * Hand over the child a spawn produced. Called for every spawn of one run: a\n * command resolved through the shell bridge is its own child, and a stop that\n * arrived before that child existed has to land on it too.\n */\n track(child: HostChild): void {\n this.child = child;\n child.once('exit', () => this.forget(child));\n if (this.stopRequested) this.terminate();\n }\n\n /**\n * Stop the host. The polite signal goes to the child itself, and a child still\n * alive after the grace is forced. Repeat calls are the same call: the ladder\n * runs once per child, so a keystroke held down is not a stream of signals.\n */\n terminate(): void {\n this.stopRequested = true;\n if (!this.controller.signal.aborted) this.controller.abort();\n const child = this.child;\n // Nothing spawned yet, or the spawn that was attempted failed and left no\n // process behind: the request is remembered, and `track` runs the ladder on\n // the child the next spawn produces.\n if (child === undefined || child.pid === undefined || child === this.stopping) return;\n this.stopping = child;\n this.clearGrace();\n signalChild(child, 'SIGTERM');\n this.grace = setTimeout(() => {\n this.grace = undefined;\n // Gone, or replaced by a newer spawn: there is nothing left to force.\n if (this.child !== child) return;\n this.forced = true;\n signalChild(child, 'SIGKILL');\n }, this.graceMs);\n }\n\n /**\n * Skip the grace and force the host now. The \"you asked twice\" path: the\n * terminal leaves at once on a second signal, and the run screen forces and\n * returns to the dashboard rather than holding a keystroke while the grace\n * runs out. Does nothing when no stop is in flight.\n */\n forceNow(): boolean {\n const child = this.child;\n if (child === undefined || child.pid === undefined || child !== this.stopping) return false;\n this.forced = true;\n this.clearGrace();\n signalChild(child, 'SIGKILL');\n return true;\n }\n\n /**\n * Install the terminal interrupt handlers. Only a run installs these, and only\n * for as long as it is driving the host: the process's own handling has to\n * come back afterwards, or a later command inherits an interrupt contract with\n * nothing left to interrupt.\n */\n watch(): void {\n if (this.watching) return;\n this.watching = true;\n // One listener per signal, each knowing which signal it is: the name is\n // carried by the closure rather than read off the callback's argument, so\n // this behaves the same whether the signal came from the OS or from code\n // that raised it directly.\n for (const signal of INTERRUPT_SIGNALS) {\n const listener = (): void => this.onSignal(signal);\n this.handlers.set(signal, listener);\n process.on(signal, listener);\n }\n }\n\n /** Put the process's own handling back, whatever the run left behind. */\n unwatch(): void {\n if (!this.watching) return;\n this.watching = false;\n for (const [signal, listener] of this.handlers) process.off(signal, listener);\n this.handlers.clear();\n }\n\n private onSignal(signal: InterruptSignal): void {\n if (this.signalName !== undefined) {\n // A second interrupt is the user insisting. Honour it at once rather than\n // waiting out a grace the child may be ignoring.\n this.exitNow(exitCodeForSignal(signal));\n return;\n }\n this.signalName = signal;\n this.terminate();\n }\n\n /** The child is gone: stop tracking it and disarm its ladder. */\n private forget(child: HostChild): void {\n if (this.child !== child) return; // a newer spawn owns the ladder now\n this.child = undefined;\n this.stopping = undefined;\n this.clearGrace();\n }\n\n private clearGrace(): void {\n if (this.grace === undefined) return;\n clearTimeout(this.grace);\n this.grace = undefined;\n }\n}\n","// Run profiles (`run.profiles` in .noir/config.yml): named host-binary\n// bundles selected by `--profile` / NOIR_PROFILE / run.defaultProfile, modeled\n// on the ssh_config / VS-Code-terminal-profiles precedent. Selection precedence\n// is the universal ladder: explicit flag > env var > persisted config default >\n// built-in default. An explicitly named profile that does not exist is a hard\n// error that lists the available names (botocore semantics + inline listing).\n//\n// Security doctrine: `env` values may reference `${VAR}` from Noir's process\n// environment and `null` deletes a variable from the child env — .noir/config.yml\n// is committable project state, so literal secrets must never be stored there.\n\nimport type { NoirConfig } from '@noir-ai/core';\nimport { isDeniedEnvKey, loadProjectInfo } from '@noir-ai/core';\n\nexport interface ProfileResolution {\n readonly binary?: string;\n readonly env?: Record<string, string | undefined>;\n readonly args?: readonly string[];\n readonly profileName?: string;\n}\n\nexport type ProfileResolutionResult =\n | { readonly ok: true; readonly profile: ProfileResolution }\n | { readonly ok: false; readonly message: string };\n\n/** Best-effort project config load — `noir run` keeps working outside a project. */\nexport function loadRunConfig(root: string): NoirConfig | null {\n try {\n return loadProjectInfo(root).config;\n } catch {\n return null;\n }\n}\n\n/** Expand `${NAME}` references from `env`; unresolved references stay literal. */\nexport function expandEnvVars(value: string, env: Record<string, string | undefined>): string {\n return value.replace(/\\$\\{([A-Za-z_][A-Za-z0-9_]*)\\}/g, (match, name: string) =>\n env[name] !== undefined ? (env[name] as string) : match,\n );\n}\n\n/**\n * Resolve the effective profile. `requested` is the `--profile` flag value.\n * Precedence: requested > NOIR_PROFILE env > run.defaultProfile > none.\n */\nexport function resolveRunProfile(\n requested: string | undefined,\n config: NoirConfig,\n env: Record<string, string | undefined>,\n): ProfileResolutionResult {\n const profiles = config.run?.profiles ?? {};\n const names = Object.keys(profiles);\n const name = requested ?? env.NOIR_PROFILE ?? config.run?.defaultProfile;\n if (name === undefined || name.length === 0) {\n return { ok: true, profile: {} }; // built-in host default, unchanged\n }\n const profile = profiles[name];\n if (!profile) {\n const available =\n names.length > 0\n ? names.join(', ')\n : '(none defined — add a run.profiles block to .noir/config.yml)';\n return {\n ok: false,\n message: `unknown run profile \"${name}\" — no profile named \"${name}\" is defined under run.profiles in .noir/config.yml. Available: ${available}`,\n };\n }\n const envExpanded: Record<string, string | undefined> = {};\n if (profile.env) {\n for (const [k, v] of Object.entries(profile.env)) {\n envExpanded[k] = v === null ? undefined : expandEnvVars(v, env);\n }\n }\n // The same process-injection names `.noir/.env` refuses are refused here too:\n // a profile `env` block lives in the committable `.noir/config.yml`, so a\n // deny-listed key it sets would be inherited by the spawned host process.\n // Refuse by KEY (never scan values — expansion can only rewrite a value, it\n // never routes one into a key).\n for (const key of Object.keys(envExpanded)) {\n if (isDeniedEnvKey(key)) {\n return {\n ok: false,\n message:\n `run profile \"${name}\" refuses process-injection key \"${key}\" — a profile env block ` +\n `in .noir/config.yml must not set ${key} (it can inject into the spawned host process). ` +\n `Remove \"${key}\" from the profile.`,\n };\n }\n }\n return {\n ok: true,\n profile: { binary: profile.binary, env: envExpanded, args: profile.args, profileName: name },\n };\n}\n\n/** Rows for `noir run --list-profiles`. Keys match the table() column labels. */\nexport function listProfiles(\n config: NoirConfig,\n): Array<{ NAME: string; DEFAULT: string; BINARY: string }> {\n const profiles = config.run?.profiles ?? {};\n const def = config.run?.defaultProfile;\n return Object.keys(profiles).map((name) => ({\n NAME: name,\n DEFAULT: name === def ? '*' : '',\n BINARY: profiles[name]?.binary ?? '',\n }));\n}\n","// A live status line for `noir run`, drawn on STDERR.\n//\n// The stretch between spawning the host and its first token of output is the\n// one part of a run with no feedback at all, and it has no upper bound: a slow\n// gateway, a queued model, or a host quietly retrying all read as a hang. This\n// line names what is happening — which host, which model, how long, how many\n// tokens so far — so that stretch is never silent.\n//\n// Three design constraints shape it:\n//\n// 1. STDERR, because stdout carries the host's answer (or the `--json`\n// envelope) and must stay clean for pipes, redirection, and machine\n// consumers. Under `--json` and `--quiet` the line emits nothing at all,\n// so those byte-for-byte contracts are untouched.\n// 2. EVENT-DRIVEN, never a timer. The host's answer streams to stdout, which\n// normally shares this terminal; a redraw racing that stream would land\n// inside the answer. Every render here is caused by a host event.\n// 3. ONE ROW AT A TIME. While the line owns the row the cursor is on, it\n// rewrites in place. The moment anything else is about to be written to\n// that row it finishes the row with a newline first, so the answer never\n// starts glued to the tail of the status text. It then keeps quiet until\n// stdout leaves the cursor at the start of a row again — which is what\n// makes a carriage return safe to use, since anything else would drag the\n// cursor back over what was just written.\n//\n// Where stderr is not a terminal (CI logs, redirection) there is nothing to\n// redraw and no cursor to protect, so the line degrades to two plain markers:\n// one when the run starts, one when it ends.\n\nimport { type NoirEvent, UsageReducer } from './orchestrator.js';\n\n/**\n * How many lines of the host's own stderr a failure message carries. Bounded\n * because a host may narrate far more than a terminal can usefully show; the\n * unfiltered stream is still in the transcript.\n */\nexport const HOST_STDERR_TAIL_LINES = 20;\n\nexport interface RunStatusOptions {\n /** Label for the host being driven (`claude`, `claude-work`, …). */\n readonly host: string;\n /** Machine-output mode: the status line is suppressed entirely. */\n readonly json?: boolean;\n /** The user asked for no diagnostics: the status line is suppressed entirely. */\n readonly quiet?: boolean;\n /** Whether the status line's own stream is a terminal (redraw) or a pipe (markers). */\n readonly stderrIsTty?: boolean;\n /**\n * Whether stdout renders on the same terminal as stderr. When it does, the\n * answer can land on the row the status line is holding, so the line must\n * yield first. When stdout is redirected or piped it is not on the terminal\n * at all, and the status line can keep its row for the whole run. Defaults to\n * true — the conservative answer when the caller does not say.\n */\n readonly stdoutSharesCursor?: boolean;\n /** Where the line is written (defaults to stderr). */\n readonly write?: (chunk: string) => void;\n /** Monotonic millisecond clock (defaults to `performance.now`). */\n readonly now?: () => number;\n}\n\n/**\n * Humanize a duration the way a person reads it: `8s`, `2m 18s`, `1h 05m`.\n * Raw seconds stop being readable well before a long tool-heavy run finishes.\n */\nexport function humanizeElapsed(ms: number): string {\n const total = Math.max(0, Math.floor(ms / 1000));\n const seconds = total % 60;\n const minutes = Math.floor(total / 60) % 60;\n const hours = Math.floor(total / 3600);\n if (hours > 0) return `${hours}h ${String(minutes).padStart(2, '0')}m`;\n if (minutes > 0) return `${minutes}m ${String(seconds).padStart(2, '0')}s`;\n return `${seconds}s`;\n}\n\nexport class RunStatusLine {\n private readonly host: string;\n private readonly enabled: boolean;\n private readonly animated: boolean;\n private readonly sharesCursor: boolean;\n private readonly write: (chunk: string) => void;\n private readonly now: () => number;\n /**\n * Running token totals for the live line. This is a SECOND accumulator: the\n * snapshot the run reports at the end is reduced independently from the full\n * event stream, and both apply the same `max usage per message id` rule, so\n * the number on screen and the number in the summary agree.\n */\n private readonly usage = new UsageReducer();\n private startedAt: number;\n private model: string | undefined;\n private streaming = false;\n private begun = false;\n private ended = false;\n /** True while the status text is sitting on the row the cursor is currently at. */\n private onRow = false;\n /** True when the last stdout write ended a line — the only safe moment to move the cursor. */\n private stdoutAtRowStart = true;\n /** The text currently on the row (empty when the row is not ours). */\n private rendered = '';\n\n constructor(opts: RunStatusOptions) {\n this.host = opts.host;\n this.enabled = opts.json !== true && opts.quiet !== true;\n this.animated = this.enabled && opts.stderrIsTty === true;\n this.sharesCursor = opts.stdoutSharesCursor !== false;\n this.write = opts.write ?? ((chunk: string): void => void process.stderr.write(chunk));\n this.now = opts.now ?? ((): number => performance.now());\n this.startedAt = this.now();\n }\n\n /**\n * Claim the row: show the seeded \"waiting\" state immediately, before the host\n * has said anything, so the spawn itself is never blank. Calling this twice\n * is a no-op.\n */\n begin(): void {\n if (!this.enabled || this.begun) return;\n this.begun = true;\n this.startedAt = this.now();\n if (this.animated) {\n this.draw();\n return;\n }\n this.write(`${this.render()}\\n`);\n }\n\n /** Feed one normalized host event. Anything unrenderable is ignored. */\n event(e: NoirEvent): void {\n if (!this.enabled || this.ended || !this.begun) return;\n if (e.kind === 'result') {\n this.end();\n return;\n }\n if (e.kind === 'init') {\n if (e.model !== undefined) this.model = e.model;\n } else if (e.kind === 'assistant' || e.kind === 'tool') {\n // A tool call is output: the host is working, so the line stops saying it\n // is still waiting — and a tool-only message carries that message's\n // tokens, which the shared accumulator folds in by the same rule.\n this.streaming = true;\n this.usage.add(e);\n } else if (e.kind === 'delta') {\n // The first slice of the answer: there is text on screen from here on.\n this.streaming = true;\n } else {\n return; // 'other' carries nothing this line can show\n }\n this.draw();\n }\n\n /**\n * Announce the chunk the caller is about to write to STDOUT — before writing\n * it. Warning the line first lets it finish its row: the answer would\n * otherwise start glued to the tail of the status text. It also records\n * whether that write ended a line, which is the only moment the cursor can be\n * moved without landing inside the answer.\n */\n beforeStdout(chunk: string): void {\n if (chunk.length === 0) return;\n if (this.animated && this.sharesCursor && this.onRow) {\n this.write('\\n');\n this.onRow = false;\n }\n this.stdoutAtRowStart = chunk.endsWith('\\n');\n }\n\n /**\n * Finish the row the host's answer left open. A failure message printed onto\n * a half-written answer reads as part of the answer, so a caller about to\n * report a failure asks for the row to be terminated first. A no-op unless\n * this line is drawing on a terminal stdout also writes to and the answer\n * stopped mid-line — the only state where the cursor is inside the answer.\n */\n finishRow(): void {\n if (!this.animated || !this.sharesCursor || this.stdoutAtRowStart) return;\n this.write('\\n');\n this.stdoutAtRowStart = true;\n }\n\n /**\n * Close the line out. On a terminal the line is erased so the run summary\n * takes its place — unless the answer has since moved the cursor off it, in\n * which case the text stays as the record of how long the wait was. On a pipe\n * it becomes the final marker. Safe to call twice.\n */\n end(): void {\n if (!this.enabled || this.ended) return;\n this.ended = true;\n if (!this.animated) {\n this.write(`${this.render(true)}\\n`);\n return;\n }\n if (!this.onRow || !this.cursorFree()) return;\n this.write('\\r\\x1b[K');\n this.rendered = '';\n this.onRow = false;\n }\n\n /** Rewrite the current row in place, skipping no-op rewrites and unsafe moments. */\n private draw(): void {\n if (!this.animated || !this.cursorFree()) return;\n const text = this.render();\n if (text === this.rendered) return;\n this.write(`\\r\\x1b[K${text}`);\n this.rendered = text;\n this.onRow = true;\n }\n\n /**\n * Whether the cursor is at the start of a row nobody else is using. On stdout\n * that is not this terminal there is no shared cursor to disturb; when it is,\n * only a write that ended a line leaves one free.\n */\n private cursorFree(): boolean {\n return !this.sharesCursor || this.stdoutAtRowStart;\n }\n\n /**\n * `final` marks the line the run ends on: a run that never got past the spawn\n * is over, so saying it is still waiting — directly above whatever the run\n * reported — would be a lie.\n */\n private render(final = false): string {\n const parts: string[] = [];\n if (this.streaming) {\n const u = this.usage.snapshot();\n parts.push(\n this.model ?? this.host,\n this.elapsed(),\n `↓${u.inputTokens.toLocaleString()}↑${u.outputTokens.toLocaleString()} tokens`,\n );\n return `● ${parts.join(' · ')}`;\n }\n // Before the first token there is nothing to count yet: name the host, then\n // the model once the host has announced it.\n if (this.model !== undefined) return `▶ ${[this.host, this.model, this.elapsed()].join(' · ')}`;\n if (final) return `▶ ${this.host} · no output after ${this.elapsed()}`;\n return `▶ ${this.host} · waiting for first event…`;\n }\n\n private elapsed(): string {\n return humanizeElapsed(this.now() - this.startedAt);\n }\n}\n\n/**\n * The last `maxLines` lines of the host's own stderr. A failing host narrates\n * its progress and its errors there and Noir otherwise reads none of it, which\n * leaves a failure reportable only by whatever single sentence the host put in\n * its stream — the run is unauditable without the rest. Bounded so a\n * pathological host cannot flood the terminal, and surfaced VERBATIM: it is the\n * host's own output, never Noir's environment, so there is nothing here to\n * redact and no value of Noir's to leak.\n */\nexport function hostStderrTail(stderr: string, maxLines = HOST_STDERR_TAIL_LINES): string {\n // A non-positive bound asks for no lines at all. `slice(-0)` is `slice(0)`,\n // so without this an unbounded tail would be printed by the code that exists\n // to bound it.\n if (maxLines <= 0) return '';\n const lines = stderr.split('\\n').map((line) => line.trimEnd());\n // A trailing newline produces one empty element that is not a line of output.\n while (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();\n if (lines.length === 0) return '';\n if (lines.length <= maxLines) return lines.join('\\n');\n const omitted = lines.length - maxLines;\n return [`… (${omitted} earlier lines omitted)`, ...lines.slice(-maxLines)].join('\\n');\n}\n","// `noir run <prompt>`: drive the host agentic CLI headless and render its\n// stream-json. This is the programmatic host-driving line and the\n// headless orchestrator surface: Noir spawns the host as a subprocess,\n// streams its output, and reports the token/cost from the `result` event — it\n// never runs its own model + tool loop (an agent loop is impossible by\n// construction: the model request type has no tools/stream parameter).\n//\n// Custom command: `--command <binary>` lets users with multiple host\n// profiles (e.g. two Claude Code installs, `claude` vs `claude-work`) point the\n// orchestrator at their own binary without restarting the terminal. The host\n// default is used when `--command` is absent.\n//\n// Contract: `--json` emits one `{ok,data}` envelope to stdout (scriptable);\n// otherwise the host's assistant text streams to stdout and the token/cost\n// summary + transcript path go to stderr. A transcript of the raw stream-json is\n// always persisted to `.noir/transcripts/`.\n\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type HostId, SUPPORTED_HOSTS } from '@noir-ai/adapters';\nimport { isDeniedEnvKey, loadNoirEnv, NOIR_DIR, parseConfig, paths } from '@noir-ai/core';\nimport {\n type NoirEvent,\n type RunHostResult,\n runHost,\n type UsageSnapshot,\n} from '../orchestrator.js';\nimport { type CliOptions, EXIT, fail, failAndExit, json, log, success } from '../output.js';\nimport { offerPostRunActions } from '../run-actions.js';\nimport { exitCodeForSignal, interruptedNotice, RunInterrupt } from '../run-interrupt.js';\nimport { loadRunConfig, resolveRunProfile } from '../run-profiles.js';\nimport { HOST_STDERR_TAIL_LINES, hostStderrTail, RunStatusLine } from '../run-status.js';\n\n/**\n * Anthropic credential / gateway variables that change how a headless (`-p`)\n * host run authenticates. A custom gateway sets `ANTHROPIC_AUTH_TOKEN` +\n * `ANTHROPIC_BASE_URL` and fails exactly like a stale API key, so the auth\n * advice must cover every recognised shape — not just `ANTHROPIC_API_KEY`.\n */\nconst CREDENTIAL_ENV_VARS = [\n 'ANTHROPIC_API_KEY',\n 'ANTHROPIC_AUTH_TOKEN',\n 'ANTHROPIC_BASE_URL',\n] as const;\n\n/**\n * The auth addendum that names each credential variable in effect AND the\n * source that won for it (`.noir/.env` vs the environment), so the \"unset it\"\n * advice stays actionable: after consolidation `applyNoirEnv` re-injects a\n * file-scoped key on every invocation, so shell-level unsetting does nothing —\n * the message must point at the file.\n *\n * NAMES AND SOURCES ONLY: the message is loggable and shareable, so\n * no branch here may ever print a variable's value. Returns `''` when no\n * recognised credential variable is set.\n */\nfunction credentialNote(\n env: Record<string, string | undefined>,\n sources: Record<string, 'file' | 'env'>,\n): string {\n const named = CREDENTIAL_ENV_VARS.filter((name) => {\n const value = env[name];\n return value !== undefined && value.length > 0;\n }).map(\n (name) => `${name} (from ${sources[name] === 'file' ? `${NOIR_DIR}/.env` : 'the environment'})`,\n );\n if (named.length === 0) return '';\n const one = named.length === 1;\n const subject = one ? `${named[0]} is set` : `${named.join(', ')} are set`;\n const pronoun = one ? 'it' : 'them';\n const verb = one ? 'overrides' : 'override';\n return (\n ` Note: ${subject} — ${pronoun} ${verb} the logged-in account in \\`-p\\` mode; ` +\n `unset ${pronoun} in the named source (or fix the value) if you meant to use your subscription.`\n );\n}\n\n/** Options accepted by `noir run` (globals + host/command/profile knobs). */\nexport interface RunOptions extends CliOptions {\n /** Host to drive (default `claude`). */\n readonly host?: string;\n /** Custom host binary overriding the per-host default. */\n readonly command?: string;\n /** Named run profile from .noir/config.yml `run.profiles`. */\n readonly profile?: string;\n}\n\n/** A single-row token/cost summary (human-readable). */\nfunction formatUsage(u: UsageSnapshot): string {\n const cost = u.totalCostUsd > 0 ? ` $${u.totalCostUsd.toFixed(2)}` : '';\n const turns = u.numTurns > 0 ? ` · ${u.numTurns} turns` : '';\n return `${u.inputTokens.toLocaleString()} in / ${u.outputTokens.toLocaleString()} out${cost}${turns}`;\n}\n\n/**\n * The answer text an event carries, or `undefined` when it carries none.\n * API-error assistant text (e.g. \"Not logged in · Please run /login\") is a\n * diagnostic, not the answer. One predicate for both the live stream and the\n * accumulator, so what the user reads and what a post-run action can save are\n * always the same words.\n */\nfunction answerText(event: NoirEvent): string | undefined {\n if (event.kind === 'assistant' && event.text && event.text.length > 0 && event.isError !== true) {\n return event.text;\n }\n return undefined;\n}\n\n/**\n * Stream an assistant text delta to stdout (non-json mode only). The host's\n * answer is data, so it goes to stdout; diagnostics go to stderr. The status\n * line is told first, so it can finish its own row before the answer is written\n * — the answer must never start glued to the tail of the progress text.\n */\nfunction streamEvent(event: NoirEvent, opts: RunOptions, status: RunStatusLine): void {\n if (opts.json === true) return; // json mode buffers; no streaming writes\n const text = answerText(event);\n if (text !== undefined) {\n status.beforeStdout(text);\n process.stdout.write(text);\n }\n}\n\n/**\n * Run the host headless and report. Rejects are translated to a clean CLI\n * failure (exit 1) rather than an unhandled rejection.\n */\nexport async function run(prompt: string, opts: RunOptions): Promise<void> {\n await runOnce(prompt, opts, undefined);\n}\n\n/**\n * One host invocation, from validation to summary.\n *\n * `resumeSessionId` continues a session an earlier invocation started: the\n * child is spawned with `--resume <id>` and a prompt collected fresh, and\n * everything else — the stream, the transcript, the summary — is this same\n * code path. It stays a single-shot invocation: the host answers and exits,\n * and Noir holds no session open between turns.\n */\nasync function runOnce(\n prompt: string,\n opts: RunOptions,\n resumeSessionId: string | undefined,\n): Promise<void> {\n const host = (opts.host ?? 'claude') as HostId;\n if (!(SUPPORTED_HOSTS as readonly string[]).includes(host)) {\n fail(EXIT.USAGE, `unknown host '${host}' (supported: ${SUPPORTED_HOSTS.join(', ')})`, opts);\n }\n if (prompt.length === 0) {\n const selectors: string[] = [];\n if (opts.command) selectors.push(`--command ${opts.command}`);\n if (opts.profile) selectors.push(`--profile ${opts.profile}`);\n const usage =\n selectors.length > 0 ? `noir run ${selectors.join(' ')} <prompt>` : 'noir run <prompt>';\n fail(EXIT.USAGE, `a prompt is required: \\`${usage}\\``, opts);\n }\n\n const root = process.cwd();\n // Credential provenance for the failure advice: `sources` records, per key,\n // whether `.noir/.env` or the ambient environment won. Names\n // only — never a value. Read from `process.env`, which the bin's preAction\n // has already overlaid with `.noir/.env` (the file wins).\n const envSources = loadNoirEnv(root).sources;\n\n // Run-profile resolution: --profile > NOIR_PROFILE > run.defaultProfile >\n // built-in default. Config load is best-effort — `noir run` keeps working\n // outside an initialized project (no profiles, built-in host behavior).\n const config = loadRunConfig(root) ?? parseConfig({});\n const resolved = resolveRunProfile(opts.profile, config, process.env);\n if (!resolved.ok) fail(EXIT.USAGE, resolved.message, opts);\n const profile = resolved.profile;\n // An explicit `--command` (per-invocation override) wins over a profile's\n // binary; the profile's binary is the fallback when --command is absent.\n const customBinary = opts.command ?? profile.binary;\n // A continuation appends `--resume <id>` after the profile's own args, so a\n // profile's flags stay in effect on the resumed turn too.\n const extraArgs =\n resumeSessionId === undefined\n ? profile.args\n : [...(profile.args ?? []), '--resume', resumeSessionId];\n const env = profile.env ? mergeEnv(process.env, profile.env) : undefined;\n\n // Spec 13.3: running outside an initialized project stays supported, but the\n // gap deserves a word — with no `.noir/.env` there are no project\n // credentials, so an auth failure would read as \"noir is broken\" instead of\n // \"this project has none\". One informational stderr line, BEFORE the spawn:\n // never a failure, never a prompt, and silenced by `log()` under\n // --json/--quiet so a machine consumer's output stays pristine.\n if (!existsSync(join(root, NOIR_DIR, '.env'))) {\n log(\n `This project has no ${NOIR_DIR}/.env — that is where Noir reads project credentials and ` +\n `run config from. Run \\`noir init\\` to scaffold one (\\`noir run\\` works without it).`,\n opts,\n );\n }\n\n const transcriptLines: string[] = [];\n // The answer as plain text, accumulated as it streams. The transcript is raw\n // stream-json, so it cannot feed a post-run action; this can. Accumulating\n // does not touch what is written to stdout — the same text still goes out\n // live, byte for byte.\n const answer: string[] = [];\n // The host's own id for this session, when it reports one — the handle a\n // continuation needs.\n let sessionId: string | undefined;\n\n // The binary the user is actually driving — a per-invocation `--command` or\n // profile override wins over the host default. Named in the status line and\n // in every failure message, so it is resolved once here.\n const binary = customBinary && customBinary.length > 0 ? customBinary : host;\n\n // Live progress on stderr for the wait before the host's first token. Silent\n // under --json/--quiet; two plain markers instead of an animated line when\n // stderr is not a terminal.\n const status = new RunStatusLine({\n host: binary,\n json: opts.json,\n quiet: opts.quiet,\n stderrIsTty: process.stderr.isTTY === true,\n stdoutSharesCursor: process.stdout.isTTY === true,\n });\n status.begin();\n\n // The interrupt contract for this run, installed for exactly as long as a\n // host child is live. A Ctrl+C in the terminal — or a SIGTERM from whatever\n // started Noir — stops the HOST and then reports it, rather than killing Noir\n // and leaving the host running behind it.\n // One verdict per run, whichever path reaches it first. The second-signal path\n // below writes its envelope and leaves, but it leaves only once that write has\n // landed — and the child it forced can resolve this run inside that window, so\n // the run's own interrupted path can arrive at the verdict second. A run that\n // wrote two verdicts would hand a scripted consumer two answers to one run.\n let verdictWritten = false;\n const claimVerdict = (): boolean => {\n if (verdictWritten) return false;\n verdictWritten = true;\n return true;\n };\n\n const interrupt = new RunInterrupt({\n // A second interrupt leaves at once, and leaves the same verdict behind as\n // the first: the run owes its consumer an envelope under --json, and this is\n // the one path where `fail()` cannot write it (there is no caller left to\n // catch its throw). The child is forced first, so leaving does not leave a\n // host behind.\n exitNow: (code) => {\n interrupt.forceNow();\n if (claimVerdict()) {\n failAndExit(code, interruptedNotice(safeTranscript(host, transcriptLines)), opts);\n }\n },\n });\n interrupt.watch();\n\n let result: RunHostResult;\n try {\n result = await runHost({\n host,\n prompt,\n customBinary,\n extraArgs,\n env,\n onLine: (line) => transcriptLines.push(line),\n onEvent: (event) => {\n if (event.kind === 'init' && sessionId === undefined) sessionId = event.sessionId;\n status.event(event);\n streamEvent(event, opts, status);\n const text = answerText(event);\n if (text !== undefined) answer.push(text);\n },\n signal: interrupt.signal,\n onChild: (child) => interrupt.track(child),\n });\n } catch (err) {\n // Clear the status line before the failure text so the error is not printed\n // onto the tail of a half-drawn progress row, and finish the row the host's\n // answer may have left open so the error is not read as part of it.\n status.end();\n status.finishRow();\n // A stop that landed while the spawn was still failing is still a stop. The\n // run was asked to end, and why the host never got going is beside the point\n // — reporting it as a failure would blame the host for doing as it was told.\n if (interrupt.interruptedBy !== undefined) {\n if (claimVerdict()) {\n failInterrupted(interrupt.interruptedBy, host, transcriptLines, opts);\n }\n // The verdict is already written by the path that left the process; this\n // one has nothing to add but a second answer.\n return;\n }\n const detail = err instanceof Error ? err.message : String(err);\n const enoent = (err as NodeJS.ErrnoException)?.code === 'ENOENT';\n const guidance = enoent\n ? ` No executable '${binary}' was found. Shell aliases and functions (e.g. from .zshrc) are invisible to noir — use an executable on PATH, an absolute path, or a launcher script such as ~/.local/bin/${binary}.`\n : '';\n const subject =\n customBinary && customBinary.length > 0\n ? `custom command '${customBinary}'`\n : `host '${host}'`;\n fail(EXIT.ERROR, `failed to run ${subject}: ${detail}.${guidance}`, opts);\n } finally {\n // Whatever happened, the process's own signal handling comes back: a later\n // command must not inherit an interrupt contract with nothing to interrupt.\n interrupt.unwatch();\n }\n\n // An interrupt is not a host failure. The run was stopped on purpose, so the\n // verdict is the stop itself plus the record of what the host had produced —\n // never a \"failed\" line about a host that was doing as it was told. The child\n // is already reaped (the kill ladder above waits it out), so this is the last\n // thing the run does.\n if (interrupt.interruptedBy !== undefined) {\n // Leave the answer's line, and the status row, closed before the verdict is\n // written: it is read as the run's last word, not as more of the stream.\n status.end();\n status.finishRow();\n if (claimVerdict()) {\n failInterrupted(interrupt.interruptedBy, host, transcriptLines, opts);\n }\n // The leaving path already wrote the verdict this run owes its consumer.\n return;\n }\n\n const transcript = safeTranscript(host, transcriptLines);\n const failed = result.exitCode !== 0 || result.isError;\n\n if (failed) {\n // A failed host run is an error, not a success: exit 1, {ok:false} under\n // --json, and no misleading \"usage\" line. The raw stream-json transcript is\n // still persisted (it is the audit record) and referenced in the message.\n status.end();\n // The answer, if any, stopped mid-line, and a failure printed onto it reads\n // as the tail of the answer rather than as the run's verdict.\n status.finishRow();\n // The host's own stderr is where its progress and its deeper error detail\n // live, and it is otherwise withheld entirely — a bounded tail is what makes\n // the failure diagnosable. It is a human diagnostic: under --json the\n // message stays the one concise sentence a machine consumer reads, and the\n // transcript path is what points at the rest.\n const stderrTail = hostStderrTail(result.stderr);\n const reason =\n result.errorText && result.errorText.trim().length > 0\n ? result.errorText.trim()\n : `exit code ${result.exitCode}`;\n // Auth guidance is keyed off the stream's error CATEGORY (authoritative),\n // falling back to a text heuristic only when the category is absent. A host\n // that could not authenticate tends to say so on its stderr rather than in\n // the stream, so the tail feeds the heuristic too. The login hint names the\n // RESOLVED binary, not a literal 'claude'.\n const isAuth =\n result.errorCategory === 'authentication_failed' ||\n result.errorCategory === 'oauth_org_not_allowed' ||\n /not logged|login|authenticate|invalid api key/i.test(`${reason}\\n${stderrTail}`);\n let message = `host '${binary}' failed (exit ${result.exitCode}): ${reason}`;\n if (isAuth) {\n message += ` Open a terminal and run \\`${binary} /login\\` (interactive-only — it cannot run inside \\`noir run\\`), then retry.`;\n message += credentialNote(env ?? process.env, envSources);\n }\n message += ` If you use another profile, pass \\`--command <binary>\\` or define a run profile under run.profiles. transcript: ${transcript}`;\n if (opts.json !== true && stderrTail.length > 0) {\n message += `\\n${binary} stderr (last ${HOST_STDERR_TAIL_LINES} lines):\\n${stderrTail}`;\n }\n fail(EXIT.ERROR, message, opts);\n }\n\n if (opts.json === true) {\n json({\n ok: true,\n data: {\n host,\n prompt,\n exitCode: result.exitCode,\n usage: result.usage,\n numTurns: result.usage.numTurns,\n events: result.eventCount,\n transcript,\n // The answer as plain words, so a consumer does not have to reassemble\n // it from the stream-json transcript.\n answerText: answer.join(''),\n ...(sessionId === undefined ? {} : { sessionId }),\n },\n });\n return;\n }\n\n // The answer, if any, left the cursor mid-line; the summary starts on a line\n // of its own, and the status line (when it still held the row) is erased here.\n status.end();\n process.stdout.write('\\n');\n success(`usage: ${formatUsage(result.usage)} (API-equivalent estimate, not billed)`, opts);\n log(`transcript: ${transcript}`, opts);\n\n // Everything past this point is an offer, not part of the run. The menu\n // returns immediately unless this is a terminal that allows prompts, so a\n // piped or scripted run keeps the output it has always had.\n await offerPostRunActions({\n answer: answer.join(''),\n transcript,\n sessionId,\n host: binary,\n opts,\n // Only the first run of a session can be continued: a resumed run is\n // itself the continuation, so the menu never nests more than one deep.\n ...(resumeSessionId === undefined && sessionId !== undefined\n ? { resume: (nextPrompt: string, id: string) => runOnce(nextPrompt, opts, id) }\n : {}),\n });\n}\n\n/**\n * Merge a profile's env overlay over the base env; `undefined` values delete\n * the key. This is the single place a profile env becomes a child environment,\n * so it also refuses a process-injection key — a second net behind profile\n * resolution, in case some future path builds a child env without resolving the\n * profile first. The overlay keys are scanned (never the base, whose\n * `NODE_OPTIONS`/`LD_LIBRARY_PATH` etc. are the user's own ambient environment).\n */\nexport function mergeEnv(\n base: Record<string, string | undefined>,\n overlay: Record<string, string | undefined>,\n): Record<string, string | undefined> {\n for (const key of Object.keys(overlay)) {\n if (isDeniedEnvKey(key)) {\n fail(\n EXIT.ERROR,\n `refusing to run the host: the profile env defines process-injection key \"${key}\" ` +\n `(it can inject into the spawned host process)`,\n );\n }\n }\n const merged = { ...base, ...overlay };\n return Object.fromEntries(Object.entries(merged).filter(([, v]) => v !== undefined));\n}\n\n/**\n * The verdict of a run that was stopped on purpose: the conventional exit code\n * for the signal that stopped it, where the part of the run that happened is,\n * and nothing about failure. One function so every path that ends a run this way\n * says the same thing in the same shape.\n */\nfunction failInterrupted(\n signal: NodeJS.Signals,\n host: string,\n lines: readonly string[],\n opts: RunOptions,\n): never {\n return fail(exitCodeForSignal(signal), interruptedNotice(safeTranscript(host, lines)), opts);\n}\n\n/**\n * {@link writeTranscript} with its failure folded in rather than raised. The\n * interrupt path writes a transcript while the run is on its way out, so a\n * throw there would replace the verdict (and, in a signal handler, the exit)\n * with a stack trace about a file. `(not persisted)` is already the writer's\n * own answer for \"it did not land\"; this just makes it the only answer.\n */\nfunction safeTranscript(host: string, lines: readonly string[]): string {\n try {\n return writeTranscript(host, lines);\n } catch {\n return '(not persisted)';\n }\n}\n\n/**\n * Persist the raw stream-json lines to `.noir/transcripts/<host>-<ts>.jsonl`.\n * Exported because the TUI's run screen persists a run the same way, and the\n * directory mode / file mode / best-effort rules below are the whole contract\n * for a transcript on disk — a second implementation would be a second answer\n * to \"who can read this file\".\n */\nexport function writeTranscript(\n host: string,\n lines: readonly string[],\n root: string = process.cwd(),\n): string {\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\n const dir = paths.transcriptsDir(root);\n const file = join(dir, `${host}-${ts}.jsonl`);\n try {\n // Transcripts contain raw host prompts/output (may include secrets) — create\n // the dir 0700 and the file 0600 so they are not group/world-readable.\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n writeFileSync(file, `${lines.join('\\n')}${lines.length > 0 ? '\\n' : ''}`, { mode: 0o600 });\n } catch {\n // Transcript persistence is best-effort — a read-only .noir/ must not fail\n // the run.\n return '(not persisted)';\n }\n return file;\n}\n","// Conflict-resolution UX seam. The engine (@noir-ai/create) is UI-free;\n// this module builds the scaffold conflict options (policy + a lazy @clack\n// resolver) per the CLI interactivity contract:\n// - TTY + interactive → @clack select menu (Replace/Rename/Duplicate/Keep/Cancel,\n// plus a 6th \"merge (with conflict markers)\" when `mergedWithMarkers` is set)\n// - non-TTY / CI / NO_COLOR → preserve (never silently clobber in a pipe)\n// - --force → overwrite (explicit re-scaffold; no prompt)\n//\n// Universal contract: this seam now drives EVERY file-producing path. The\n// engine's regenerate path consults `onConflict` (apply-to-all keyed by artifact\n// CLASS so an N-pointer `noir init --upgrade` → 1 prompt); managedBlock conflicts\n// stay PER-FILE. A unified line-diff preview (LCS-based, from\n// @noir-ai/create's `lineDiff`) renders to STDERR before the prompt (through the\n// shared theme — `+` green / `-` red / context dim), so `--json`/piped stdout stays\n// pristine. The 6th \"merge\" option writes the engine-provided zdiff3-marked\n// bytes when a 3-way merge hit an overlap.\nimport {\n type ConflictContext,\n type ConflictResolution,\n type ConflictResolverReturn,\n lineDiff,\n} from '@noir-ai/create';\nimport { isInteractive } from './output.js';\nimport { c } from './theme.js';\n\nexport interface ConflictOptsInput {\n /** `--force`: explicit re-scaffold — overwrite differing files, no prompt. */\n force?: boolean;\n /** Explicit interactivity signal. When set, wins over the env/TTY\n * heuristic. `false` ⇒ preserve (never prompt); `true` ⇒ allow the @clack\n * resolver. The CLI derives this from the `NOIR_NON_INTERACTIVE` bridge\n * bin.ts owns + the `isInteractive()` TTY/CI/NO_COLOR gate. */\n interactive?: boolean;\n}\n\nexport type ScaffoldConflictOpts = {\n conflictPolicy: 'overwrite' | 'preserve';\n onConflict?: (ctx: ConflictContext) => Promise<ConflictResolverReturn> | ConflictResolverReturn;\n};\n\n/**\n * Build the scaffold conflict options for the current invocation. The engine\n * calls `onConflict` only when a `regenerate` file exists AND differs from the\n * template, so passing this on every init/create/sync is harmless on a first\n * run (no existing files → no conflict).\n *\n * The `interactive` flag (explicit > env bridge > TTY/CI/NO_COLOR\n * gate) drives the prompt decision so the engine never reads `process.env` for\n * interactivity. The `NOIR_NON_INTERACTIVE` bridge (--json/--no-input) and the\n * `isInteractive()` gate both reduce to `preserve` (never prompt).\n *\n * Apply-to-all: when the resolver returns `{resolution, applyToAll: true}`\n * the engine stores the choice in its per-run memory keyed by artifact CLASS\n * (regenerate shares one decision; managedBlock/managedBlocks stay per-file).\n */\n/** True when the bin's preAction hook flagged this invocation non-interactive\n * (`--json` / `--no-input`) so the conflict resolver never prompts under those. */\nfunction flaggedNonInteractive(): boolean {\n const v = process.env.NOIR_NON_INTERACTIVE;\n return v !== undefined && v !== '';\n}\n\nexport function buildConflictOpts(input: ConflictOptsInput = {}): ScaffoldConflictOpts {\n if (input.force === true) {\n return { conflictPolicy: 'overwrite' };\n }\n // Explicit interactive flag wins over the env/TTY heuristic.\n if (input.interactive === false) {\n return { conflictPolicy: 'preserve' };\n }\n if (input.interactive === true) {\n return { conflictPolicy: 'preserve', onConflict: clackConflictResolver };\n }\n // Fallback: NOIR_NON_INTERACTIVE bridge (--json/--no-input) OR the\n // isInteractive() TTY/CI/NO_COLOR gate — either ⇒ preserve, never prompt.\n if (flaggedNonInteractive() || !isInteractive()) {\n return { conflictPolicy: 'preserve' };\n }\n return { conflictPolicy: 'preserve', onConflict: clackConflictResolver };\n}\n\n/**\n * Render a colored unified line-diff of `{existing, proposed}` to STDERR\n * before the @clack prompt. Stderr (not stdout) so `--json` / piped stdout\n * stays pristine. The shared theme colors `+` lines green (`c.ok`), `-` lines red\n * (`c.error`), and dims context lines (`c.dim`); all color is gated through\n * `useColor()` so NO_COLOR / non-TTY / CLICOLOR_FORCE behave consistently.\n * Honored by `theme.test.ts`'s NO_COLOR gate.\n */\nfunction renderDiffPreview(ctx: ConflictContext): void {\n // `lineDiff` is imported statically at the top of this module. It used to be\n // a lazy `require('@noir-ai/create')`, but tsup bundles every @noir-ai/*\n // internal into dist/bin.js — a dynamic `require` survives bundling as an\n // ESM-incompatible `__require` shim that throws \"Dynamic require of\n // @noir-ai/create is not supported\" at runtime (broke `init --upgrade`\n // whenever a file conflict triggered this preview). The static import is\n // bundled correctly and runs in pure ESM with no side effects here.\n const lines = lineDiff(ctx.existing, ctx.proposed);\n if (lines.length === 0) return;\n const out: string[] = [`noir: conflict in ${ctx.relPath}`];\n for (const dl of lines) {\n if (dl.type === 'add') out.push(c.ok(`+ ${dl.line}`));\n else if (dl.type === 'del') out.push(c.error(`- ${dl.line}`));\n else out.push(c.dim(` ${dl.line}`));\n }\n process.stderr.write(`${out.join('\\n')}\\n`);\n}\n\n/**\n * @clack-based conflict resolver. Renders a unified diff to stderr\n * (via {@link renderDiffPreview}) BEFORE the select prompt, then offers the\n * usual Replace/Rename/Duplicate/Keep/Cancel set PLUS a 6th \"merge (with\n * conflict markers)\" option when `ctx.mergedWithMarkers` is set (a 3-way merge\n * hit an overlap and the engine has the marked bytes ready).\n *\n * Apply-to-all: when the user picks an option, @clack asks whether to reuse the\n * choice for the rest of the run; if yes, the resolver returns the rich\n * `{resolution, applyToAll: true}` shape and the engine stores it in per-class\n * memory. `managedBlock`/`managedBlocks` modes bypass apply-to-all (per-file).\n */\nasync function clackConflictResolver(ctx: ConflictContext): Promise<ConflictResolverReturn> {\n renderDiffPreview(ctx);\n const clack = await import('@clack/prompts');\n const options: Array<{ value: ConflictResolution; label: string; hint: string }> = [\n {\n value: 'replace',\n label: 'Replace',\n hint: 'overwrite with the template (discard your edits)',\n },\n { value: 'rename', label: 'Rename', hint: 'keep yours as <file>.local; write the template' },\n {\n value: 'duplicate',\n label: 'Create duplicate',\n hint: 'write template to <file>.noir; keep yours',\n },\n { value: 'preserve', label: 'Keep mine', hint: 'skip; leave your file unchanged' },\n { value: 'cancel', label: 'Cancel', hint: 'abort the scaffold' },\n ];\n // 6th \"merge\" option only when the engine has marked bytes ready.\n if (ctx.mergedWithMarkers !== undefined) {\n options.splice(4, 0, {\n value: 'merge',\n label: 'Merge (with conflict markers)',\n hint: 'write the 3-way merge with <<<<<<< / ||||||| / ======= / >>>>>>> markers',\n });\n }\n const choice = await clack.select({\n message: `${ctx.relPath} was edited and differs from the Noir template.`,\n initialValue: 'preserve',\n options,\n });\n if (clack.isCancel(choice)) return 'cancel';\n const resolution = choice as ConflictResolution;\n // Apply-to-all. The `regenerate` scaffold path AND the `skill` emit path both\n // key their per-run memory by a single class, so one \"Yes\" reuses the choice\n // across the whole run. The `managedBlock`/`managedBlocks` paths are per-file\n // (the engine keys memory by path::block, not by class), so they must NOT\n // offer \"apply to all\" — reusing a per-file decision there would silently\n // clobber unrelated blocks.\n const mode = (ctx.mode ?? 'regenerate') as string;\n const supportsApplyToAll = mode !== 'managedBlock' && mode !== 'managedBlocks';\n if (supportsApplyToAll) {\n const remember = await clack.select({\n message: `Apply \"${labelFor(resolution)}\" to all remaining conflicts this run?`,\n initialValue: 'no',\n options: [\n { value: 'yes', label: 'Yes', hint: 'reuse this choice for the rest of this run' },\n { value: 'no', label: 'No', hint: 'prompt again for each conflict' },\n ],\n });\n if (!clack.isCancel(remember) && remember === 'yes') {\n return { resolution, applyToAll: true };\n }\n }\n return resolution;\n}\n\nfunction labelFor(resolution: ConflictResolution): string {\n switch (resolution) {\n case 'replace':\n return 'Replace';\n case 'rename':\n return 'Rename';\n case 'duplicate':\n return 'Create duplicate';\n case 'preserve':\n return 'Keep mine';\n case 'merge':\n return 'Merge (with conflict markers)';\n case 'cancel':\n return 'Cancel';\n }\n}\n","// Write-path semantic dedup. After `scaffold()` writes host-context\n// files (CLAUDE.md / AGENTS.md / GEMINI.md / .noir/rules/RULES.md), embed the\n// just-written file and compare it against the OTHER existing host-context\n// files. Surface a near-duplicate as a NON-BLOCKING recommendation:\n//\n// - cosine ≥ 0.95 → ACTION prompt (interactive): Replace the existing /\n// Mirror into it / Skip (delete the new file) / Create\n// anyway. Under --json/--no-input the action tier records\n// the near-dup in ScaffoldResult.conflicts[] (mode=\n// 'artifact', similarity set) and proceeds with the\n// default (Create anyway); NEVER prompts.\n// - 0.85–0.95 → INFO-only hint to stderr, write proceeds.\n// - < 0.85 → silent.\n//\n// GRACEFUL DEGRADATION (the make-orreak constraint): the embedder (ONNX\n// Runtime + the ~22 MB all-MiniLM-L6-v2 model) is lazy-loaded with a 5s ping\n// timeout; if it is unavailable, slow, OR throws, the hook prints a one-line\n// stderr warn-skip and returns an empty result. `noir init`/`sync`/`create`\n// NEVER block on a model download or fail because the embedder is missing —\n// dedup is an ENHANCEMENT, not a gate. A fresh project with no existing\n// host-context candidates short-circuits BEFORE any embedding (fast path).\n//\n// CONTENT-HASH GATE: candidate embeddings are cached under\n// `.noir/dedup-cache.json` keyed by SHA-256(content); only changed candidates\n// re-embed. The proposed file's embedding is cached the same way, so a repeat\n// `noir sync` that re-emits byte-identical content does NOT re-embed. Cache\n// entries are scoped to the resolved model id (a model change invalidates them).\n//\n// When a near-dup is found, the conflict record (mode 'artifact'\n// — the closest generic in ConflictContext['mode']) is appended to the\n// ScaffoldResult.conflicts[] the caller returns, so `--json` consumers see the\n// near-dup with its cosine `similarity` without a prompt.\n//\n// Stream discipline: every hint / warn-skip → STDERR; the CLI's `json()` is the\n// sole stdout writer. Colors via the shared theme (`c.warn`/`c.dim`) so NO_COLOR /\n// non-TTY strip cleanly.\n\nimport { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { type ConflictResolution, type ProjectInfo, sha256Hex, sha256Hex12 } from '@noir-ai/core';\nimport type { ConflictRecord, ScaffoldResult } from '@noir-ai/create';\nimport { collectDedupCandidates } from './commands/doctor.js';\nimport { c } from './theme.js';\n\n/** Local embedder shape (@noir-ai/context's `EmbedFn`). */\nexport type EmbedLike = (text: string) => Promise<Float32Array>;\n\n/** Host-context files dedup scans (mirrors `collectDedupCandidates`). */\nconst HOST_CONTEXT_RELS = new Set(['CLAUDE.md', 'AGENTS.md', 'GEMINI.md', '.noir/rules/RULES.md']);\n\n/** Cosine ≥ this → ACTION prompt (interactive) or conflict record (--json). */\nconst ACTION_THRESHOLD = 0.95;\n/** Cosine ≥ this (and < ACTION) → INFO-only hint. Below → silent. */\nconst INFO_THRESHOLD = 0.85;\n/** The first embed() call triggers ONNX load + possibly a model download; bound\n * it so init/sync/create never block indefinitely on a cold cache. */\nconst EMBEDDER_PING_TIMEOUT_MS = 5000;\n/** Bump on disk-format changes; an older file is discarded (no migration). */\nconst DEDUP_CACHE_VERSION = 1;\n\nexport interface DedupWriteOpts {\n /** Explicit interactivity flag (the engine + skills emit read the same). */\n interactive: boolean;\n /** Project info — read from `.noir/config.yml` by the caller. When undefined\n * OR when the resolved embedder is `kind:'none'`, the hook warn-skips. */\n project?: ProjectInfo;\n /** Testability seam (mirrors `checkSemanticDupDoctor`'s `opts.embed`): when\n * set, the hook uses this embedder directly and skips the lazy ONNX load +\n * ping-timeout. Production omits it. */\n embed?: EmbedLike;\n}\n\nexport interface DedupWriteResult {\n /** Near-duplicates found, one entry per (proposed, matched) pair. */\n found: Array<{ proposedRel: string; matchedRel: string; similarity: number }>;\n /** Conflict records to append to {@link ScaffoldResult.conflicts}. */\n conflicts: ConflictRecord[];\n}\n\n/**\n * Run the write-path semantic dedup over the host-context files in\n * `res.written`. Does NOT mutate `res`; the caller decides whether to splice\n * `result.conflicts` into `res.conflicts`. Non-blocking: any embedder failure\n * degrades to a stderr warn-skip and an empty result.\n */\nexport async function checkWritePathDedup(\n root: string,\n res: ScaffoldResult,\n opts: DedupWriteOpts,\n): Promise<DedupWriteResult> {\n const result: DedupWriteResult = { found: [], conflicts: [] };\n // Fast path: only host-context files we JUST wrote are eligible. Fresh\n // projects (first init/create) typically write ONE host pointer + RULES.md;\n // if no other host-context file exists, candidates is empty → no embedding.\n const writtenHostFiles = res.written.filter((p) => HOST_CONTEXT_RELS.has(p));\n if (writtenHostFiles.length === 0) return result;\n\n const cache = loadCache(root);\n const embed = await resolveEmbedder(opts, cache);\n if (embed === undefined) return result; // warn-skip already emitted\n\n // Wrap the embedder with the content-hash cache so unchanged candidates (and\n // a repeat-emit of byte-identical proposed bytes) skip the underlying call.\n const cachedEmbed = makeCachedEmbed(cache, embed);\n\n for (const proposedRel of writtenHostFiles) {\n const proposedAbs = join(root, proposedRel);\n let proposedText: string;\n try {\n proposedText = readFileSync(proposedAbs, 'utf8');\n } catch {\n continue;\n }\n if (proposedText.trim().length === 0) continue;\n\n // Candidates = existing host-context files MINUS this proposed path. A\n // first-run init writing CLAUDE.md while AGENTS.md already exists compares\n // the new CLAUDE.md against AGENTS.md + RULES.md (not against itself).\n const candidates = collectDedupCandidates(root).filter((c) => c.path !== proposedRel);\n if (candidates.length === 0) continue; // fresh project, nothing to compare\n\n const ctx = await import('@noir-ai/context');\n const proposed = { path: proposedRel, text: proposedText };\n // Graceful degradation INSIDE the loop too: an embedder that loads (passes\n // the ping) but throws mid-batch (transient remote failure, OOM, …) must\n // never break the write. Catch, warn-skip this proposal, keep going.\n let best: Awaited<ReturnType<typeof ctx.findNearestDuplicate>>;\n try {\n best = await ctx.findNearestDuplicate(\n proposed,\n candidates,\n async (t) => cachedEmbed(t),\n INFO_THRESHOLD,\n );\n } catch (e) {\n process.stderr.write(\n `${c.dim(`noir: semantic-dedup skipped for ${proposedRel} (${errMsg(e)}).`)}\\n`,\n );\n continue;\n }\n if (best === null) continue;\n\n const matchedRel = best.a === proposedRel ? best.b : best.a;\n const sim = best.similarity;\n result.found.push({ proposedRel, matchedRel, similarity: sim });\n\n // The conflict record's resolution reflects what actually happened — not a\n // hardcoded 'preserve'. Non-interactive + cancel default to 'preserve'\n // (Create anyway); the prompt's replace/mirror choices map onto the\n // ConflictResolution union.\n let resolution: ConflictResolution = 'preserve';\n if (sim >= ACTION_THRESHOLD) {\n if (opts.interactive) {\n resolution = await promptDedupAction(root, proposedRel, matchedRel, proposedText, sim);\n } else {\n // --json / --no-input: record + proceed with the default (Create\n // anyway). Never prompt under non-interactive.\n process.stderr.write(\n `${c.warn('noir: near-duplicate')} ${proposedRel} ≈ ${matchedRel} (cosine ${sim.toFixed(2)}); creating anyway.\\n`,\n );\n }\n } else {\n // 0.85 – 0.95: INFO-only hint. Write already succeeded; just surface.\n process.stderr.write(\n `${c.dim(`noir: hint ${proposedRel} looks similar to existing ${matchedRel} (cosine ${sim.toFixed(2)}).`)}\\n`,\n );\n }\n\n // Append a structured record so --json consumers see the\n // near-dup with its cosine similarity without a prompt. The path is the\n // file we just wrote (consistent with the \"path = the file being\n // written\" convention); existingSha is the MATCHED file's hash, proposedSha the new\n // bytes'. Resolution 'preserve' = we kept both as-is under Create-anyway.\n const matchedText = safeRead(join(root, matchedRel));\n result.conflicts.push({\n path: proposedRel,\n mode: 'artifact',\n similarity: sim,\n existingSha: sha256Hex12(matchedText ?? ''),\n proposedSha: sha256Hex12(proposedText),\n resolution,\n });\n }\n\n saveCache(root, cache);\n return result;\n}\n\n/**\n * Resolve the embedder: injected (test seam) > local-embedder from the user's\n * `context.embedder` config. Returns undefined (after a stderr warn-skip) when\n * kind='none', the factory throws, OR the first ping exceeds the timeout.\n *\n * Model-id tracking runs in BOTH paths so a config change invalidates the\n * on-disk cache even when the caller injected an embedder (the test seam\n * still flows through the same cache discipline as production).\n */\nasync function resolveEmbedder(\n opts: DedupWriteOpts,\n cache: DedupCache,\n): Promise<EmbedLike | undefined> {\n const ctx = await import('@noir-ai/context');\n // Resolve the model id from config when available; an injected embedder\n // without project info skips invalidation (cache stays model-scoped to '' —\n // a fresh cache, which is fine for tests that always inject).\n let modelId: string | undefined;\n if (opts.project !== undefined) {\n const cfg = ctx.resolveEmbedderConfig(opts.project.config.context);\n if (cfg.kind === 'none') {\n process.stderr.write(`${c.dim('noir: semantic-dedup skipped (embedder kind=none).')}\\n`);\n return undefined;\n }\n modelId =\n cfg.kind === 'local' ? (cfg.model ?? ctx.DEFAULT_LOCAL_MODEL) : (cfg.model ?? cfg.kind);\n // Invalidate cache on a model change BEFORE deciding the embedder source.\n if (modelId !== undefined && cache.model !== modelId) {\n cache.model = modelId;\n cache.entries = {};\n }\n }\n // Test seam: an injected embedder bypasses the lazy ONNX load + ping.\n if (opts.embed !== undefined) return opts.embed;\n if (modelId === undefined) return undefined; // no project + no inject → skip\n\n let embed: EmbedLike;\n try {\n const project = opts.project;\n if (project === undefined) return undefined;\n const cfg = ctx.resolveEmbedderConfig(project.config.context);\n embed = ctx.createEmbedFn(cfg).embed as EmbedLike;\n } catch (e) {\n process.stderr.write(\n `${c.dim(`noir: semantic-dedup skipped (embedder unavailable: ${errMsg(e)}).`)}\\n`,\n );\n return undefined;\n }\n // Pre-warm with a ping under a timeout. The first call triggers the ONNX\n // load + (on a cold ~/.noir/models/) a ~22 MB model download; bound it so\n // init/sync never block on the network. A throw OR a timeout → warn-skip.\n const ping = await withTimeout(\n embed('ping').then(() => true),\n EMBEDDER_PING_TIMEOUT_MS,\n );\n if (ping !== true) {\n process.stderr.write(\n `${c.dim('noir: semantic-dedup skipped (embedder load too slow or unavailable).')}\\n`,\n );\n return undefined;\n }\n return embed;\n}\n\n/** ACTION prompt for the ≥0.95 tier (interactive only). */\nasync function promptDedupAction(\n root: string,\n proposedRel: string,\n matchedRel: string,\n proposedText: string,\n sim: number,\n): Promise<ConflictResolution> {\n const clack = await import('@clack/prompts');\n type Choice = 'create' | 'skip' | 'replace' | 'mirror';\n const choice = await clack.select({\n message: `${proposedRel} is near-duplicate of existing ${matchedRel} (cosine ${sim.toFixed(2)}). Create anyway?`,\n initialValue: 'create' as Choice,\n options: [\n {\n value: 'create' as Choice,\n label: 'Create anyway',\n hint: 'keep the new file as-is (default)',\n },\n { value: 'skip' as Choice, label: 'Skip', hint: `delete ${proposedRel}; keep ${matchedRel}` },\n {\n value: 'replace' as Choice,\n label: 'Replace existing',\n hint: `overwrite ${matchedRel} with this content`,\n },\n {\n value: 'mirror' as Choice,\n label: 'Mirror into existing',\n hint: `append this content to ${matchedRel}`,\n },\n ],\n });\n if (clack.isCancel(choice)) return 'preserve'; // cancel = keep the default (Create anyway)\n if (choice === 'skip') {\n try {\n rmSync(join(root, proposedRel), { force: true });\n } catch {\n /* best-effort; the write already happened */\n }\n return 'preserve'; // the matched file is kept\n } else if (choice === 'replace') {\n try {\n writeFileSync(join(root, matchedRel), proposedText, 'utf8');\n } catch {\n /* best-effort */\n }\n return 'replace';\n } else if (choice === 'mirror') {\n try {\n const existing = readFileSync(join(root, matchedRel), 'utf8');\n writeFileSync(\n join(root, matchedRel),\n `${existing}\\n\\n<!-- mirrored from ${proposedRel} -->\\n${proposedText}\\n`,\n 'utf8',\n );\n } catch {\n /* best-effort */\n }\n return 'duplicate'; // the proposed content was folded into the existing file\n }\n // 'create' → no-op (the just-written file stands).\n return 'preserve';\n}\n\n// ---------------------------------------------------------------------------\n// Content-hash cache (.noir/dedup-cache.json).\n// ---------------------------------------------------------------------------\n\ninterface DedupCache {\n version: number;\n model: string;\n entries: Record<string, { v: number[] }>;\n}\n\nfunction emptyCache(): DedupCache {\n return { version: DEDUP_CACHE_VERSION, model: '', entries: {} };\n}\n\nfunction cachePath(root: string): string {\n return join(root, '.noir', 'dedup-cache.json');\n}\n\nfunction loadCache(root: string): DedupCache {\n try {\n const raw = readFileSync(cachePath(root), 'utf8');\n const parsed = JSON.parse(raw) as Partial<DedupCache>;\n if (parsed.version !== DEDUP_CACHE_VERSION) return emptyCache();\n if (typeof parsed.entries !== 'object' || parsed.entries === null) return emptyCache();\n return {\n version: DEDUP_CACHE_VERSION,\n model: typeof parsed.model === 'string' ? parsed.model : '',\n entries: parsed.entries,\n };\n } catch {\n return emptyCache();\n }\n}\n\nfunction saveCache(root: string, cache: DedupCache): void {\n try {\n mkdirSync(join(root, '.noir'), { recursive: true });\n writeFileSync(cachePath(root), JSON.stringify(cache), 'utf8');\n } catch {\n // Best-effort; cache persistence is an enhancement, never a gate.\n }\n}\n\n/** Wrap `embed` with a SHA-256 content-hash cache. Cache hits return the\n * stored vector WITHOUT calling `embed`; misses call `embed` and store. */\nfunction makeCachedEmbed(cache: DedupCache, embed: EmbedLike): EmbedLike {\n return async (text: string): Promise<Float32Array> => {\n const key = sha256Hex(text);\n const cached = cache.entries[key];\n if (cached !== undefined) return Float32Array.from(cached.v);\n const v = await embed(text);\n cache.entries[key] = { v: Array.from(v) };\n return v;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Small helpers.\n// ---------------------------------------------------------------------------\n\nfunction safeRead(abs: string): string | undefined {\n try {\n return readFileSync(abs, 'utf8');\n } catch {\n return undefined;\n }\n}\n\n/** Resolve `p` OR undefined on timeout/throw (never rejects). The underlying\n * promise keeps running in the background; we just stop awaiting it. */\nfunction withTimeout<T>(p: Promise<T>, ms: number): Promise<T | undefined> {\n return new Promise((resolve) => {\n const t = setTimeout(() => resolve(undefined), ms);\n p.then(\n (v) => {\n clearTimeout(t);\n resolve(v);\n },\n () => {\n clearTimeout(t);\n resolve(undefined);\n },\n );\n });\n}\n\nfunction errMsg(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n","// `noir init` — first-run + `--upgrade`.\n//\n// Refactor: the ad-hoc writers (syncIgnores, writeManagedRegion,\n// writeFileSync for .mcp.json/project.id/config.yml/NOIR.md/RULES.md) are\n// replaced by a single call into `@noir-ai/create`'s `scaffold({mode:'init'})`.\n// The manifest + three-mode writer are the source of truth for what init\n// emits; this module is now a thin caller that owns ONLY:\n// - the transport/url precondition (the localhost security gate, whose\n// error strings are locked by url-validation.test.ts), and\n// - skills emission (out-of-manifest by design — composed after scaffold()).\n//\n// Multi-host: the 8 direct `claudeAdapter` imports across init/sync/create\n// collapsed to `resolveAdapter(host)` where `host` comes from `--host <id>`\n// (default `'claude'`). The adapter drives (a) the manifest via\n// `scaffold({host})` and (b) skills emission — claude/cursor have a skill dir;\n// gemini/agents-md/opencode have no skill concept and the call is skipped.\n//\n// Deliberate behavior changes vs the predecessor (latent-bug fixes; see the\n// CHANGELOG):\n// - `.noir/project.id` → skipIfExists (predecessor overwrote on every init,\n// orphaning the store DB named after the id). Re-init now preserves it.\n// - `.noir/config.yml` → skipIfExists (predecessor overwrote).\n// - `.noir/NOIR.md` → managedBlock with BRIEF_BLOCK markers (predecessor\n// wrote the whole file with no markers). First-run output GAINS markers;\n// user notes outside the markers survive re-runs.\n// - `.noir/scaffold-version` is now stamped on init/create (engine-owned).\n\nimport { existsSync } from 'node:fs';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, type ProjectInfo, paths } from '@noir-ai/core';\nimport { type ScaffoldResult, scaffold } from '@noir-ai/create';\nimport {\n type CompileTarget,\n type EmitSummary,\n emitSkillsToDir,\n type SkillConflict,\n} from '@noir-ai/skills';\nimport { buildConflictOpts, type ScaffoldConflictOpts } from './conflict.js';\nimport { checkWritePathDedup } from './dedup-write.js';\nimport { log, resolveInteractive } from './output.js';\n\nexport interface InitOptions {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n /** `noir init --upgrade`: run scaffold migrations from the on-disk\n * scaffold-version to current, then re-emit every manifest mode except\n * `mergeJson` — including `skipIfExists`, which creates only when the file is\n * absent. That backfills seeds added to the manifest after this project was\n * initialized without ever touching a user-owned file. */\n upgrade?: boolean;\n /** Target host. Resolution order: this explicit `--host <id>` value > the\n * `host:` field of an existing `.noir/config.yml` > `'claude'` (the default,\n * and the regression anchor for a project that has no config yet). Reading\n * the configured host matters for `--upgrade`/`--force`, which re-emit into\n * a project that already chose a host: emitting under the default instead\n * would write a spurious claude surface and refresh none of the artifacts\n * the project actually uses. An absent or unreadable config degrades to the\n * default rather than failing the run.\n * Drives both scaffold emission (the manifest's host-specific half) and\n * skills emission (skipped for hosts with no `skillsDir`). */\n host?: HostId;\n /** Re-scaffold even if already initialized (bypasses the\n * already-initialized no-op guard in scaffold()). */\n force?: boolean;\n /** `--dry-run`/`--preview` — report the planned writes to stderr\n * (via {@link reportPlannedWrites}) without touching disk. The scaffold\n * engine already supports this; the CLI just surfaces it. */\n dryRun?: boolean;\n /** Alias for `--dry-run`. Kept on the options bag so direct callers can\n * pass either spelling; the bin collapses both flags before dispatch. */\n preview?: boolean;\n}\n\n/**\n * The {@link ScaffoldResult} plus the skill pack's own report. The scaffold's\n * `conflicts[]` covers the files the manifest owns; skills are emitted outside\n * the manifest, so their conflicts and their stale leftovers would otherwise\n * be invisible to a `--json` consumer.\n */\nexport interface InitResult extends ScaffoldResult {\n /** One record per skill file that existed AND differed from the compiled\n * bytes, with the resolution that was applied — including the files a\n * non-interactive run left alone. Always an array once skills emission has\n * run; absent on the `--dry-run` and already-initialized paths, which stop\n * before emission. */\n skillConflicts?: SkillConflict[];\n /** Names of the skills left with the user's own bytes because a differing\n * file was preserved. Empty when every skill is current — the `--json`\n * counterpart of the stderr line that warns about stale skills. */\n preservedSkills?: string[];\n}\n\n/**\n * Initialize Noir in `root`. Returns the {@link InitResult} (the\n * {@link ScaffoldResult} with structured `conflicts[]`, any dedup records, and\n * the skill pack's conflicts/stale names) so `--json` callers can surface\n * conflict detail. `undefined` when the already-initialized guard\n * short-circuited (a no-op).\n */\nexport async function init(root: string, opts: InitOptions): Promise<InitResult | undefined> {\n assertTransportUrl(opts);\n\n const host: HostId = resolveInitHost(root, opts);\n // The engine reads ScaffoldOptions.interactive (hermetic — never\n // process.env). The CLI derives it once from the bridge + TTY/CI/NO_COLOR gate.\n const interactive = resolveInteractive();\n const conflictOpts = buildConflictOpts({ force: opts.force, interactive });\n // --dry-run/--preview collapse to a single dryRun boolean. The engine\n // skips every write and returns the PLANNED lists; skills emission + dedup are\n // skipped too (they would touch disk / load the embedder).\n const dryRun = opts.dryRun === true || opts.preview === true;\n\n const res: InitResult = await scaffold({\n root,\n mode: 'init',\n host,\n transport: opts.transport,\n interactive,\n ...(opts.url !== undefined ? { url: opts.url } : {}),\n ...(opts.upgrade === true ? { upgrade: true } : {}),\n ...(opts.force === true ? { force: true } : {}),\n ...(dryRun ? { dryRun: true } : {}),\n ...conflictOpts,\n });\n // dry-run reports the planned writes (result's written/skipped/identical)\n // and stops BEFORE skills emission + the \"initialized\" message — nothing was\n // written, so the host skill dir must stay untouched and we must not claim\n // init. Under --json the bin emits the planned list as the `{ok, data}`\n // envelope on stdout (the data channel) instead.\n if (dryRun) {\n reportPlannedWrites(res);\n return res;\n }\n // If the already-initialized guard no-op'd scaffold, stop — don't re-emit\n // skills or print \"initialized\" (scaffold already printed the no-op message).\n if (res.noop) return res;\n\n // Thread the SAME conflictOpts into skills emission so the\n // skills-emit conflict flow is LIVE in interactive mode (the producer\n // accepts conflict opts; this closes the wiring gap). The safe-default\n // `assertNotUserOwned` runs unconditionally inside the producer.\n const skillSummary = await emitHostSkills(root, host, conflictOpts, interactive);\n // Skills live outside the manifest, so their report is folded onto the\n // scaffold result by hand. Both keys are always present once emission has\n // run (empty arrays when nothing conflicted) so a machine consumer can read\n // them without an existence check.\n res.skillConflicts = skillSummary?.conflicts ?? [];\n res.preservedSkills = skillSummary?.preserved ?? [];\n\n // Write-path semantic dedup. Non-blocking; degrades to a\n // stderr warn-skip when the embedder is unavailable. Records near-dups on\n // `res.conflicts` so `--json` consumers see them without a prompt.\n // Best-effort project read: first-run init has no .noir/config.yml yet →\n // undefined → resolveEmbedder warn-skips. `init --force` on an existing\n // project reads the config and runs the dedup against existing host files.\n let projectInfo: ProjectInfo | undefined;\n try {\n projectInfo = loadProjectInfo(root);\n } catch {\n projectInfo = undefined;\n }\n const dedup = await checkWritePathDedup(root, res, { interactive, project: projectInfo });\n if (dedup.conflicts.length > 0) res.conflicts.push(...dedup.conflicts);\n\n // Doc-only seeds an upgrade replaced because the user never edited them. Worth\n // a line of its own: the user's next `git diff` shows these files changing,\n // and the reason should already be on screen.\n if (res.refreshed.length > 0) {\n process.stderr.write(\n `Refreshed ${res.refreshed.length} unedited doc seed(s): ${res.refreshed.join(', ')}\\n`,\n );\n }\n // Migration-transformed files (e.g. `.noir/config.yml` gaining the env\n // pointer) are surfaced on their own — distinct from `refreshed` (doc seeds),\n // because a migrated file is a config change the next `git diff` will show.\n if (res.migrationChanged.length > 0) {\n process.stderr.write(\n `Migrated ${res.migrationChanged.length} file(s): ${res.migrationChanged.join(', ')}\\n`,\n );\n }\n process.stderr.write(\n `Noir initialized in ${root} (host: ${host}, transport: ${opts.transport}).\\n`,\n );\n process.stderr.write(\n 'Next: run `noir` to open the home menu (or `noir status` for a snapshot).\\n',\n );\n return res;\n}\n\n/**\n * Resolve which host this run emits for: an explicit `--host <id>` > the\n * `host:` field of an existing `.noir/config.yml` > `'claude'`.\n *\n * A fresh project has no config, so the default is what a bare `noir init` has\n * always produced. A project that already chose a host must keep it when\n * re-emitting (`--upgrade`/`--force`); emitting under the default instead would\n * write artifacts for the wrong host and leave the ones the project actually\n * uses stale.\n *\n * The config read is best-effort — an absent, unreadable, or invalid config\n * must not fail the run, so each degrades to the default. Only the unreadable\n * case is reported, because that is the one where a host the user configured\n * cannot be determined. A config that parses but omits `host:` is not an error:\n * the schema supplies the default.\n */\nfunction resolveInitHost(root: string, opts: InitOptions): HostId {\n if (opts.host !== undefined) return opts.host;\n if (!existsSync(paths.config(root))) return 'claude';\n try {\n return loadProjectInfo(root).config.host;\n } catch {\n // Also reached when the config is readable but the project id is missing or\n // invalid — either way the configured host is unknowable from here.\n process.stderr.write(\n `Could not read the configured host from ${paths.config(root)}; using 'claude'.\\n`,\n );\n return 'claude';\n }\n}\n\n/**\n * Report a dry-run (--dry-run/--preview) scaffold result. After\n * `scaffold({dryRun:true})` the result's `written`/`skipped`/`identical`/\n * `refreshed` carry the PLANNED paths (nothing touched disk): `written` = files\n * that WOULD be written, `skipped` = skipIfExists files already present (left\n * alone), `identical` = files whose bytes would match the template (no\n * rewrite), `refreshed` = doc-only seeds an upgrade would replace because they\n * are still an unedited copy of an older version's text.\n * `conflicts` is always empty under dryRun (no writes → no conflicts).\n * `fileModes` carries the planned permission for entries that would CREATE at a\n * non-default mode — annotated onto the planned line (`… (mode 600)`).\n *\n * Emitted via the `log()` stderr helper — a HUMAN diagnostic, so under `--json`\n * the bin emits the planned list as the structured `{ok, data}` envelope on\n * stdout (the data channel) instead, matching the CLI stream discipline used by\n * the other init/create/sync diagnostics. Shared by init/create/sync (the same\n * dryRun surface on all three scaffold modes).\n */\nexport function reportPlannedWrites(res: ScaffoldResult): void {\n log('Dry run — no files were written.');\n if (res.written.length > 0) {\n log('Planned writes:');\n for (const p of res.written) {\n // A planned creation at a non-default mode is reported\n // here so `noir init --dry-run` shows the 0600 `.noir/.env` seed without\n // writing it. `fileModes` is absent/empty for every other entry.\n const mode = res.fileModes?.[p];\n log(mode === undefined ? ` ${p}` : ` ${p} (mode ${mode.toString(8).padStart(3, '0')})`);\n }\n }\n if (res.refreshed.length > 0) {\n // Doc-only seeds still carrying the text an older Noir shipped: an upgrade\n // replaces them because the user never edited them. Reported apart from the\n // planned writes so the preview does not read as \"Noir will overwrite your\n // files\".\n log('Would refresh (unedited older copy):');\n for (const p of res.refreshed) log(` ${p}`);\n }\n if (res.skipped.length > 0) {\n log('Would leave as-is (already present):');\n for (const p of res.skipped) log(` ${p}`);\n }\n if (res.identical.length > 0) {\n log('Would rewrite (byte-identical, no-op):');\n for (const p of res.identical) log(` ${p}`);\n }\n}\n\n/**\n * Compose skill emission onto the resolved adapter's `skillsDir` (claude →\n * `.claude/skills/`; cursor → `.cursor/rules/` compiled as `.mdc`; the other\n * three hosts have no skill concept and are skipped with a stderr note).\n *\n * The `CompileTarget` matches the host id (a later foundation widening made the\n * enum the same union) so cursor skills compile to the `.mdc` rule shape via\n * `compileSkill(_, 'cursor')`; the others keep the verbatim SKILL.md format.\n *\n * Returns the emit summary for the caller to carry into `--json`, or\n * `undefined` when the host has no skill emitter to report on.\n */\nasync function emitHostSkills(\n root: string,\n host: HostId,\n conflictOpts: ScaffoldConflictOpts,\n interactive: boolean,\n): Promise<EmitSummary | undefined> {\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // Standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (Each command used to phrase this differently.)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n return undefined;\n }\n const target: CompileTarget = host;\n // Forward conflictPolicy + onConflict + interactive so an\n // interactive `noir init` with a conflicting skill emit consults the\n // resolver (clack menu + diff preview + apply-to-all); --json/--no-input\n // stays prompt-free via the `interactive: false` guard. The resolver shape\n // is structurally compatible with the skill emit seam (see skills/types.ts);\n // the cast isolates the literal-narrowing mismatch on the return type.\n type SkillEmitOpts = NonNullable<Parameters<typeof emitSkillsToDir>[1]>;\n const skillOpts: SkillEmitOpts = {\n includeIntegrations: true,\n target,\n conflictPolicy: conflictOpts.conflictPolicy,\n interactive,\n };\n if (conflictOpts.onConflict !== undefined) {\n skillOpts.onConflict = conflictOpts.onConflict as SkillEmitOpts['onConflict'];\n }\n const summary = await emitSkillsToDir(skillsDir, skillOpts);\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Emitted ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n // The count above is skills that are fully current. Anything left behind is\n // named on its own line: a run that kept stale files and reported only a\n // success count is how a CI upgrade silently stops refreshing skills.\n const stale = preservedStaleLine(summary.preserved ?? []);\n if (stale !== undefined) process.stderr.write(`${stale}\\n`);\n return summary;\n}\n\n/**\n * The line that names the skills an emit left behind as stale, or undefined when\n * there are none. ONE wording across `init`, `sync`, `create` and `skills`, so\n * `preserved as stale` in a CI log greps to every command that emits the pack —\n * a run that kept stale files and reported only a success count is how an\n * upgrade silently stops refreshing skills.\n */\nexport function preservedStaleLine(preserved: readonly string[]): string | undefined {\n if (preserved.length === 0) return undefined;\n return `${preserved.length} skill(s) preserved as stale (interactive TTY required to refresh): ${preserved.join(', ')}`;\n}\n\n/**\n * Transport + URL precondition. The error strings here are the SECURITY GATE\n * locked by `url-validation.test.ts` — do not rephrase. Thrown errors are\n * plain `Error`s (bin.ts's `main().catch` maps them to stderr + exitCode 1).\n *\n * Exported so `noir create` (commands/create.ts) reuses the SAME gate instead\n * of duplicating the localhost allowlist.\n */\nexport function assertTransportUrl(opts: {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n}): void {\n if (opts.transport === 'streamable-http' && opts.url === undefined) {\n throw new Error('--transport streamable-http requires --url');\n }\n if (opts.url !== undefined) {\n assertLocalhostUrl(opts.url);\n }\n}\n\n// Validate a streamable-http --url is http(s) and localhost-only. Gate 2's\n// daemon binds 127.0.0.1, so persisting a non-localhost URL is a footgun.\n// Not exported: the single external surface is assertTransportUrl(); thrown\n// errors propagate to bin.ts's main().catch (stderr + exitCode 1).\nfunction assertLocalhostUrl(raw: string): void {\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n throw new Error(`Invalid URL: ${raw}`);\n }\n if (!['http:', 'https:'].includes(url.protocol)) {\n throw new Error('Only http/https URLs are supported');\n }\n if (!['localhost', '127.0.0.1', '::1'].includes(url.hostname)) {\n throw new Error(`Only localhost URLs are supported (got ${url.hostname})`);\n }\n}\n","// `noir skills {list,sync}`.\n//\n// Both sub-commands are IN-PROCESS (no daemon, no store): the builtin skill\n// pack is a filesystem artifact shipped with `@noir-ai/skills`, so `list` reads\n// it directly via `discoverBuiltin()` and `sync` re-emits it into the host\n// adapter's skills dir via `emitSkillsToDir` (the same primitive `noir init` /\n// `noir sync` use). Skill state is host-local files, never the daemon store, so\n// these never hit exit 4 (DAEMON_DOWN).\n//\n// Scriptability (hard rule): `--json` emits the versioned `{ok,data}`\n// envelope to stdout (the only stdout write); the human table + banner go to\n// stderr via the centralized `table()` / `log()` helpers, which auto-strip\n// under NO_COLOR / non-TTY / --json. No interactive input is ever required, so\n// these commands are safe on a pipe / CI unconditionally.\n\nimport { resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, type ProjectInfo } from '@noir-ai/core';\nimport {\n type BuiltinSkill,\n buildRegistry,\n type CompileTarget,\n discoverAll,\n emitSkillsToDir,\n type IntegrationSkill,\n lintSkill,\n} from '@noir-ai/skills';\nimport { preservedStaleLine } from '../init.js';\nimport { type CliOptions, EXIT, fail, info, log, table, warn } from '../output.js';\n\n/** Options accepted by `skills` sub-commands (the global flags only). */\nexport interface SkillsOptions extends CliOptions {}\n\n// ---------------------------------------------------------------------------\n// Category derivation.\n//\n// Skills ship no `category` frontmatter field (the skill contract is `{name,\n// description, references?}`), so the column is a PRESENTATION-layer grouping\n// derived from the skill name. The map covers the 26 builtins; an unknown name\n// falls back to its `noir-`-stripped segment so a newly authored skill still\n// gets a sensible cell instead of an empty one. This is display-only — the\n// `--json` payload carries `category` too (same derivation) for consistency.\n// ---------------------------------------------------------------------------\nconst CATEGORY: Record<string, string> = {\n 'noir-brainstorming': 'discovery',\n 'noir-sync': 'discovery',\n 'noir-checkpoint': 'discovery',\n 'noir-exploring': 'discovery',\n 'noir-spec': 'spec',\n 'noir-planning': 'plan',\n 'noir-prd': 'plan',\n 'noir-executing-plans': 'execute',\n 'noir-test-driven-development': 'execute',\n 'noir-systematic-debugging': 'execute',\n 'noir-subagent': 'execute',\n 'noir-parallel': 'execute',\n 'noir-verifying': 'verify',\n 'noir-security': 'verify',\n 'noir-wrap': 'document',\n 'noir-readme': 'document',\n 'noir-shipping': 'git',\n 'noir-worktree': 'git',\n 'noir-recall': 'memory',\n 'noir-remember': 'memory',\n 'noir-context': 'context',\n 'noir-frontend': 'domain',\n 'noir-backend': 'domain',\n 'noir-doctor': 'meta',\n 'noir-writing-skills': 'meta',\n 'noir-rules': 'meta',\n};\n\nfunction categoryOf(name: string): string {\n const mapped = CATEGORY[name];\n if (mapped !== undefined) return mapped;\n // Unknown skill: its own topic (the `noir-`-stripped name) is a reasonable\n // default cell so the table never shows an empty category.\n return name.replace(/^noir-/, '') || 'general';\n}\n\n/** Normalized skill row (the `data.skills[]` element + the table source). */\nexport interface SkillRow {\n name: string;\n category: string;\n description: string;\n /** Distinguishes the shipped builtins from the integration skills\n * (e.g. `noir-clickup`) so `noir skills list` shows the full pack and which\n * entries are integrations. Defaults to `'builtin'` for back-compat. */\n kind: 'builtin' | 'integration';\n /** `full` (playbook) vs `stub` (marker). Derived from the body; the\n * registry enriches this from the same source so list + registry agree. */\n status: 'full' | 'stub';\n}\n\nfunction toRow(s: BuiltinSkill, kind: 'builtin' | 'integration' = 'builtin'): SkillRow {\n const description =\n typeof s.frontmatter.description === 'string' ? s.frontmatter.description : '';\n const status: 'full' | 'stub' = s.skillMd.includes('> **Stub:**') ? 'stub' : 'full';\n // Category: frontmatter `metadata.category` is the single source of truth\n // (\"data, not code\"); the curated CATEGORY map is only the fallback for a\n // legacy skill that lacks metadata. Keeps `skills list`, the registry, and\n // the docs table agreeing on one category per skill.\n const category = s.frontmatter.metadata?.category?.trim() || categoryOf(s.name);\n return { name: s.name, category, description, kind, status };\n}\n\n/**\n * Truncate `text` to `max` chars on a word boundary for dense table cells. This\n * is DISPLAY-ONLY for the human table; the full description is always carried\n * verbatim in the `--json` payload. (Distinct from the context engine's\n * \"never truncate the retrieval DATA\" rule — that governs search snippets,\n * not table formatting.)\n */\nfunction truncate(text: string, max: number): string {\n if (text.length <= max) return text;\n const slice = text.slice(0, Math.max(0, max - 1));\n // Cut at the last whitespace inside the slice so we don't split a word.\n const boundary = slice.lastIndexOf(' ');\n const head = boundary > 0 ? slice.slice(0, boundary) : slice;\n return `${head.trimEnd()}…`;\n}\n\n// ---------------------------------------------------------------------------\n// `noir skills list`\n// ---------------------------------------------------------------------------\n/**\n * `noir skills list`: discover the full shipped pack (builtins + integrations)\n * and render it. Consistent with `emitSkillsToDir` (which emits BOTH) — the\n * `noir-clickup` integration shows up here alongside the 26 builtins.\n *\n * `--json` emits `{ok:true, data:{count, skills: SkillRow[]}}` to stdout. A\n * discovery failure (the pack dir is unreadable) maps to exit 1 (ERROR) —\n * not exit 4, since this command never touches the daemon.\n */\nexport async function skillsList(opts: SkillsOptions): Promise<void> {\n let builtins: BuiltinSkill[];\n let integrations: IntegrationSkill[];\n try {\n const all = discoverAll();\n builtins = all.builtins;\n integrations = all.integrations;\n } catch (err) {\n fail(\n EXIT.ERROR,\n `skills list: could not discover skills (${err instanceof Error ? err.message : String(err)})`,\n opts,\n );\n }\n const rows = [\n ...builtins.map((b) => toRow(b, 'builtin')),\n ...integrations.map((i) => toRow(i, 'integration')),\n ];\n // Status is derived from the body (stub marker); category uses the CLI's\n // CATEGORY map (name-derived) until skills carry `metadata.category` — after\n // the content rewrite every skill gets, and `skills registry` reads it.\n // `skills list` keeps the curated CATEGORY map as its single source so the\n // human table is stable; the registry is the queryable form.\n const data = { count: rows.length, skills: rows };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n\n const intCount = integrations.length;\n const banner =\n intCount > 0\n ? `noir skills — ${rows.length} skill${rows.length === 1 ? '' : 's'} (${builtins.length} builtin, ${intCount} integration${intCount === 1 ? '' : 's'})`\n : `noir skills — ${rows.length} builtin skill${rows.length === 1 ? '' : 's'}`;\n log(banner, opts);\n table(\n rows.map((r) => ({\n Skill: r.name,\n Kind: r.kind,\n Category: r.category,\n Status: r.status,\n Description: truncate(r.description, 80),\n })),\n ['Skill', 'Kind', 'Category', 'Status', 'Description'],\n opts,\n );\n}\n\n// ---------------------------------------------------------------------------\n// `noir skills sync`\n// ---------------------------------------------------------------------------\n/**\n * `noir skills sync`: re-emit the builtin pack into the host adapter's skills\n * dir. Reuses the same `emitSkillsToDir` primitive as `noir init` / `noir sync`\n * (the host-local file write; no daemon involved).\n *\n * Requires the project to be initialized (`loadProjectInfo` throws otherwise →\n * exit 1 with the \"Run `noir init` first\" hint). When the host adapter exposes\n * no `skillsDir` (no skill emitter for this host), the command reports nothing\n * to sync and exits 0 — behavior-preserving with the existing `noir sync`.\n *\n * `--json` emits `{ok:true, data:{emitted, references, dir}}` to stdout.\n */\nexport async function skillsSync(opts: SkillsOptions): Promise<void> {\n const root = process.cwd();\n // loadProjectInfo asserts Noir is initialized; route it through fail() so an\n // uninitialized project emits the canonical {ok:false,error} envelope under\n // --json (a plain Error would leave stdout empty) — same as every other command.\n let project: ProjectInfo;\n try {\n project = loadProjectInfo(root);\n } catch {\n fail(EXIT.ERROR, 'Noir is not initialized in this directory. Run `noir init` first.', opts);\n }\n\n // Resolve the PROJECT's host adapter (not a hardcoded claude) — mirroring\n // sync.ts. A non-claude host (cursor/agents-md/opencode) has its own skillsDir\n // (e.g. .cursor/rules), and a host with NO skillsDir must report no-emitter\n // instead of silently writing a spurious .claude/skills/ tree.\n const adapter = resolveAdapter(project.config.host);\n if (!adapter.skillsDir) {\n const data = {\n emitted: [],\n references: 0,\n host: project.config.host,\n reason: 'no-skill-emitter',\n };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n info('This host has no skill emitter; nothing to sync.', opts);\n return;\n }\n\n const dir = adapter.skillsDir({ root });\n const target: CompileTarget = project.config.host;\n const summary = await emitSkillsToDir(dir, { target });\n const pruned = summary.pruned ?? [];\n const preserved = summary.preserved ?? [];\n const data = {\n emitted: summary.emitted,\n references: summary.references,\n dir,\n pruned,\n // What the count above does NOT cover: skills whose files were preserved\n // (stale on disk, by design, in a non-interactive run). Same field `noir\n // init --json` carries, so a consumer reads one shape from every emitter.\n preserved,\n };\n\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n\n log(\n `Synced ${summary.emitted.length} Noir skill${summary.emitted.length === 1 ? '' : 's'} to ${dir}.`,\n opts,\n );\n // Surface stale-dir pruning in both human + JSON modes for parity with\n // `noir sync` (the structured `pruned` field above covers --json).\n if (pruned.length > 0) {\n warn(\n `Pruned ${pruned.length} stale noir-* skill dir${pruned.length === 1 ? '' : 's'}: ${pruned.join(', ')}`,\n opts,\n );\n }\n // Same wording (and same reason) as `noir init`: the count above is skills\n // that are fully current, and a skill left stale has to be named — under\n // --json the `preserved` field above is what carries it.\n const stale = preservedStaleLine(preserved);\n if (stale !== undefined) warn(stale, opts);\n}\n\n// ---------------------------------------------------------------------------\n// `noir skills lint`\n// ---------------------------------------------------------------------------\n/**\n * `noir skills lint`: the structural quality gate over the full shipped pack.\n * Runs `lintSkill` (validateSkill errors + soft warnings) over `discoverAll()`.\n * In-process (no daemon) — the pack is a filesystem artifact.\n *\n * Exit contract: 0 when every skill validates clean (errors empty); 1 when any\n * skill has errors (the gate fails). Warnings alone do NOT fail the lint — they\n * are advisory and listed for the author. This mirrors how a real linter treats\n * hard errors vs style warnings.\n *\n * `--json` emits `{ok, data:{skills:[{name, errors, warnings}]}}` to stdout\n * (the only stdout write); the human report goes to stderr.\n */\nexport async function skillsLint(opts: SkillsOptions): Promise<void> {\n let skills: Array<{ name: string; errors: string[]; warnings: string[] }>;\n try {\n const all = discoverAll();\n skills = [...all.builtins, ...all.integrations].map(lintSkill);\n } catch (err) {\n fail(\n EXIT.ERROR,\n `skills lint: could not lint skills (${err instanceof Error ? err.message : String(err)})`,\n opts,\n );\n }\n const errored = skills.filter((s) => s.errors.length > 0);\n const warned = skills.filter((s) => s.warnings.length > 0 && s.errors.length === 0);\n\n const data = { count: skills.length, errored: errored.length, skills };\n\n // Exit-1 contract (the gate must FAIL, not silently pass): a lint error\n // produces a non-zero exit + the canonical failure envelope under --json\n // (`{ok:false,error:{code,message}}` — no ad-hoc `{ok:false,data}` shape).\n if (errored.length > 0) {\n const message = `skills lint: ${errored.length} skill${errored.length === 1 ? '' : 's'} failed validation`;\n if (opts.json === true) {\n process.stdout.write(\n `${JSON.stringify({ ok: false, error: { code: EXIT.ERROR, message }, data })}\\n`,\n );\n process.exitCode = EXIT.ERROR;\n return;\n }\n warn(\n `noir skills lint — ${errored.length} skill${errored.length === 1 ? '' : 's'} FAIL validation:`,\n opts,\n );\n for (const s of errored) {\n for (const e of s.errors) warn(` ${s.name}: ${e}`, opts);\n }\n if (warned.length > 0) {\n info(` ${warned.length} skill${warned.length === 1 ? '' : 's'} carry lint warnings:`, opts);\n for (const s of warned) {\n for (const w of s.warnings) info(` ${s.name}: ${w}`, opts);\n }\n }\n fail(EXIT.ERROR, message, opts);\n }\n\n // Clean: exit 0.\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(`noir skills lint — ${skills.length} skills, all validate clean.`, opts);\n if (warned.length > 0) {\n info(` ${warned.length} skill${warned.length === 1 ? '' : 's'} carry lint warnings:`, opts);\n for (const s of warned) {\n for (const w of s.warnings) info(` ${s.name}: ${w}`, opts);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// `noir skills registry`\n// ---------------------------------------------------------------------------\n/**\n * `noir skills registry`: emit the runtime-derived skill registry (id, kind,\n * category, version, status, refs, lines). In-process (no daemon) — reads the\n * shipped pack via `buildRegistry()`. The registry is NOT a committed file;\n * frontmatter is the single source of truth and this command derives it on\n * demand (nothing is cached to disk).\n *\n * `--json` emits `{ok:true, data:{count, skills: SkillRegistryEntry[]}}` to\n * stdout; the human view is a table to stderr.\n */\nexport async function skillsRegistry(opts: SkillsOptions): Promise<void> {\n let reg: ReturnType<typeof buildRegistry>;\n try {\n reg = buildRegistry();\n } catch (err) {\n fail(\n EXIT.ERROR,\n `skills registry: could not build registry (${err instanceof Error ? err.message : String(err)})`,\n opts,\n );\n }\n const data = { count: reg.length, skills: reg };\n if (opts.json === true) {\n process.stdout.write(`${JSON.stringify({ ok: true, data })}\\n`);\n return;\n }\n log(`noir skills registry — ${reg.length} skills.`, opts);\n table(\n reg.map((r) => ({\n Skill: r.name,\n Kind: r.kind,\n Category: r.category,\n Version: r.version,\n Status: r.status,\n Refs: String(r.referenceCount),\n Lines: String(r.lines),\n })),\n ['Skill', 'Kind', 'Category', 'Version', 'Status', 'Refs', 'Lines'],\n opts,\n );\n}\n","import { applyNoirEnv, loadProjectInfo } from '@noir-ai/core';\nimport { ensureDaemonRunning, startStdioServer } from '@noir-ai/daemon';\n\nexport async function serve(opts: { stdio: boolean }): Promise<void> {\n // Load .noir/.env before constructing the MCP server so integration tokens\n // (e.g. CLICKUP_API_TOKEN) are resolvable at call time even when the daemon\n // was launched from a context that did not inherit the shell rc.\n applyNoirEnv(process.cwd());\n const project = loadProjectInfo(process.cwd());\n if (opts.stdio) {\n await startStdioServer({ project, transport: 'stdio', daemon: false });\n return; // stdio server runs until stdin closes\n }\n // Prefer the shared daemon; on failure fall back to in-process stdio so Noir\n // keeps working even when the daemon cannot start (FS-degradation path).\n try {\n const { url } = await ensureDaemonRunning({\n project,\n idleTimeoutSec: project.config.daemon.idleTimeoutSec,\n ...(project.config.daemon.port !== undefined ? { port: project.config.daemon.port } : {}),\n });\n process.stderr.write(\n `Noir daemon available at ${url}. (For HTTP clients, use this URL in .mcp.json.) ` +\n `Add the daemon's bearer token via the host MCP headersHelper (run \\`noir daemon token\\`).\\n`,\n );\n } catch (err) {\n process.stderr.write(\n `Noir daemon unavailable (${err instanceof Error ? err.message : String(err)}); falling back to stdio.\\n`,\n );\n await startStdioServer({ project, transport: 'stdio', daemon: false });\n }\n}\n","// `noir create [dir]` — greenfield first-run for the AI layer only.\n//\n// New command. Bootstraps Noir's AI layer (.noir/ store, host\n// pointers, skills) into `dir` (created if absent), with NO external\n// scaffolder chaining (the AI layer only; no `pnpm create` wrapping).\n//\n// Reuses init's transport/url handling (including the localhost security\n// gate) by calling into the same engine: `scaffold({mode:'create', root})`.\n// The only difference from `noir init` is the mode tag (which selects the\n// manifest's first-run subset + generates a fresh project id unconditionally)\n// and that the target dir is `resolve(dir ?? cwd)` rather than `process.cwd()`.\n//\n// Multi-host: gains `--host <id>` (default `'claude'`); the host drives\n// both scaffold emission + skills emission via `resolveAdapter(host)` —\n// shared with init through `emitHostSkills`. See `init.ts` for the matrix.\n//\n// Stream discipline matches init: diagnostics → stderr, nothing on stdout\n// (so `noir create --json` is safe by construction — there is no data payload).\n//\n// Exit codes: `assertTransportUrl` is called DIRECTLY so transport/url\n// errors propagate as plain `Error` → bin.ts's handleError → exit 1, EXACTLY\n// matching `noir init` for the same error class. (The CLI exit-code contract\n// lists usage=2 for a missing `--url`; neither `init` nor `create` implements\n// that split today —\n// both report it as a plain error=1 — and the review asked for consistency\n// over a one-sided fix. Changing them together is a separate, contract-level\n// change.) The target dir is created by the engine's `create` mode (the\n// duplicate mkdir that used to live here is gone — one attributable failure\n// point, in scaffold.ts).\n\nimport { resolve } from 'node:path';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, type ProjectInfo } from '@noir-ai/core';\nimport { type ScaffoldResult, scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from '../conflict.js';\nimport { checkWritePathDedup } from '../dedup-write.js';\nimport { assertTransportUrl, preservedStaleLine, reportPlannedWrites } from '../init.js';\nimport { resolveInteractive } from '../output.js';\n\nexport interface CreateOptions {\n transport: 'stdio' | 'streamable-http';\n url?: string;\n /** Target host. Defaults to `'claude'`. */\n host?: HostId;\n /** Re-scaffold even if already initialized. */\n force?: boolean;\n /** `--dry-run`/`--preview` — report the planned writes to stderr\n * without touching disk (incl. NOT creating the target dir). */\n dryRun?: boolean;\n /** Alias for `--dry-run`. Kept on the options bag so direct callers can\n * pass either spelling; the bin collapses both flags before dispatch. */\n preview?: boolean;\n}\n\n/**\n * Bootstrap Noir's AI layer in `dir`. Returns the {@link ScaffoldResult} (with\n * structured `conflicts[]` + any dedup records appended); the bin\n * emits it under `--json`. `undefined` when the already-initialized guard\n * short-circuited (a no-op).\n *\n * @param dir Target directory (created if absent). Defaults to `process.cwd()`.\n * @param opts Transport + URL + host (same semantics as `noir init`).\n */\nexport async function create(\n dir: string | undefined,\n opts: CreateOptions,\n): Promise<ScaffoldResult | undefined> {\n // Call assertTransportUrl directly so the SAME error class yields the SAME\n // exit code as `noir init` (plain Error → exit 1 for both missing-url and\n // non-localhost). The previous NoirCliError(EXIT.USAGE) wrapper made `create`\n // exit 2 where `init` exits 1.\n assertTransportUrl(opts);\n\n const root = resolve(dir ?? process.cwd());\n // Host is left to the engine (explicit `--host` > the project's configured\n // host > `'claude'`) so `create --force` over an existing project keeps its\n // host; `res.host` reports the host actually emitted.\n // Derive interactive once (consistency with init/sync) so both scaffold\n // and skills emission see the same hermetic flag.\n const interactive = resolveInteractive();\n const conflictOpts = buildConflictOpts({ force: opts.force, interactive });\n // --dry-run/--preview collapse to a single dryRun boolean. The engine\n // skips the target-dir mkdir + every write; skills emission + dedup are\n // skipped too (they would touch disk / load the embedder).\n const dryRun = opts.dryRun === true || opts.preview === true;\n\n const res = await scaffold({\n root,\n mode: 'create',\n ...(opts.host !== undefined ? { host: opts.host } : {}),\n transport: opts.transport,\n interactive,\n ...(opts.url !== undefined ? { url: opts.url } : {}),\n ...(opts.force === true ? { force: true } : {}),\n ...(dryRun ? { dryRun: true } : {}),\n ...conflictOpts,\n });\n // dry-run reports the planned writes and stops BEFORE skills emission +\n // the \"created\" message — the target dir was NOT created, so nothing may\n // touch disk. (The engine's create-mode mkdir is gated on !dryRun.)\n if (dryRun) {\n reportPlannedWrites(res);\n return res;\n }\n // A no-op (already-initialized guard) must not re-emit skills.\n if (res.noop) return res;\n\n // The host the engine actually emitted (explicit --host, or the project's\n // configured host resolved inside scaffold).\n const host: HostId = res.host;\n\n // Skills are out-of-manifest by design — same composition as init.\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // Standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (Each command used to phrase this differently.)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n } else {\n const target: CompileTarget = host;\n // Forward conflictOpts so an interactive `noir create` with a\n // conflicting skill emit consults the resolver (mirrors init/sync).\n type SkillEmitOpts = NonNullable<Parameters<typeof emitSkillsToDir>[1]>;\n const skillOpts: SkillEmitOpts = {\n includeIntegrations: true,\n target,\n conflictPolicy: conflictOpts.conflictPolicy,\n interactive,\n };\n if (conflictOpts.onConflict !== undefined) {\n skillOpts.onConflict = conflictOpts.onConflict as SkillEmitOpts['onConflict'];\n }\n const summary = await emitSkillsToDir(skillsDir, skillOpts);\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Emitted ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n // The emitted count above is skills that are fully current; a skill whose\n // files were preserved is stale on disk and has to be named, in the same\n // words `noir init` uses.\n const stale = preservedStaleLine(summary.preserved ?? []);\n if (stale !== undefined) process.stderr.write(`${stale}\\n`);\n }\n\n // Write-path semantic dedup. Greenfield create rarely has\n // existing host-context candidates (the target dir is fresh), so the fast\n // path fires; `--force` on a pre-existing Noir project reads the config and\n // runs the full dedup. Best-effort: a missing/corrupt config warn-skips.\n let projectInfo: ProjectInfo | undefined;\n try {\n projectInfo = loadProjectInfo(root);\n } catch {\n projectInfo = undefined;\n }\n const dedup = await checkWritePathDedup(root, res, { interactive, project: projectInfo });\n if (dedup.conflicts.length > 0) res.conflicts.push(...dedup.conflicts);\n\n process.stderr.write(`Noir created in ${root} (host: ${host}, transport: ${opts.transport}).\\n`);\n process.stderr.write('Next: cd into the new directory, then run `noir` to open the home menu.\\n');\n return res;\n}\n","// `noir sync` — re-emit the runtime subset of the scaffold manifest.\n//\n// Refactor: delegates to `@noir-ai/create`'s `scaffold({mode:'sync'})`,\n// which emits ONLY the regenerate + managedBlock entries (the always-safe-to-\n// rewrite subset). This ADOPTS THE ENGINE'S\n// SEMANTICS and retires the predecessor's ad-hoc behavior:\n//\n// - The engine re-emits `.mcp.json` (regenerate) + NOIR.md brief\n// (managedBlock) + CLAUDE.md context/rules blocks + ignore-file blocks.\n// The predecessor's sync re-emitted only CLAUDE.md blocks + ignores and\n// did NOT refresh `.mcp.json`/NOIR.md — the engine is a strict superset\n// here, so a transport/url change is now picked up by `noir sync`.\n// - The engine does NOT seed `.noir/rules/RULES.md` on sync (RULES.md is\n// skipIfExists, owned by init/create). The predecessor seeded it when\n// missing; init now owns all seeds. (By design: \"sync re-emits\n// generated/managed content; init owns seeds.\")\n//\n// Multi-host: sync resolves the host from `.noir/config.yml` (persisted by\n// `noir init --host <id>` via the config seed's `host: {{host}}` literal) OR\n// from an explicit `--host <id>` override. The resolved adapter drives the\n// manifest + skills emission. Skills emission stays a core sync feature for\n// hosts with a `skillsDir` (claude → `.claude/skills/`; cursor → `.cursor/rules/`\n// as `.mdc`); gemini/agents-md/opencode have no skill concept and the step is\n// skipped. `scaffold({mode:'sync'})` throws when the project isn't initialized\n// (no .noir/project.id), matching the predecessor's `loadProjectInfo` gate.\n\nimport { existsSync } from 'node:fs';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, paths } from '@noir-ai/core';\nimport { type ScaffoldResult, scaffold } from '@noir-ai/create';\nimport { type CompileTarget, emitSkillsToDir } from '@noir-ai/skills';\nimport { buildConflictOpts } from './conflict.js';\nimport { checkWritePathDedup } from './dedup-write.js';\nimport { preservedStaleLine, reportPlannedWrites } from './init.js';\nimport { resolveInteractive } from './output.js';\n\nexport interface SyncOptions {\n /** `--host <id>` override. When set, takes precedence over the\n * `.noir/config.yml` `host:` field. Useful for re-emitting under a\n * different host without re-init (advanced — the canonical host stays\n * whatever init wrote). */\n host?: HostId;\n /** Overwrite differing regenerated files without prompting (bypasses\n * the conflict menu). */\n force?: boolean;\n /** Three-way merge managed regions (preserve hand-edits inside\n * `<!-- noir:* -->` markers across a template update). DEFAULT TRUE;\n * `--merge` is now a no-op (kept for backward compatibility). */\n merge?: boolean;\n /** Opt OUT of managed-region merge (restore strip-replace). Exposed on\n * the bin as `--no-merge-regions`. When explicitly `false`, hand-edits\n * inside `<!-- noir:* -->` markers are discarded on a template upgrade. */\n mergeManagedRegions?: boolean;\n /** `--dry-run`/`--preview` — report the planned writes to stderr\n * without touching disk. sync still requires a valid `.noir/project.id`\n * (the engine's \"not initialized\" gate fires regardless). */\n dryRun?: boolean;\n /** Alias for `--dry-run`. Kept on the options bag so direct callers can\n * pass either spelling; the bin collapses both flags before dispatch. */\n preview?: boolean;\n}\n\nexport async function sync(root: string, opts: SyncOptions = {}): Promise<ScaffoldResult> {\n const host = resolveSyncHost(root, opts);\n // The engine reads ScaffoldOptions.interactive (hermetic — never\n // process.env). The CLI derives it once from the bridge + TTY/CI/NO_COLOR gate.\n const interactive = resolveInteractive();\n const conflictOpts = buildConflictOpts({ force: opts.force, interactive });\n\n // Load project info once for both the dedup hook (embedder config)\n // and the existing config-host fallback. Best-effort: a missing/corrupt file\n // is left to scaffold's \"not initialized\" gate below.\n let projectInfo: ReturnType<typeof loadProjectInfo> | undefined;\n try {\n projectInfo = loadProjectInfo(root);\n } catch {\n projectInfo = undefined;\n }\n\n // --dry-run/--preview collapse to a single dryRun boolean.\n const dryRun = opts.dryRun === true || opts.preview === true;\n\n const res = await scaffold({\n root,\n mode: 'sync',\n host,\n interactive,\n ...(dryRun ? { dryRun: true } : {}),\n ...conflictOpts,\n // Merge defaults TRUE inside scaffold; only forward an explicit opt-out.\n ...(opts.mergeManagedRegions === false ? { mergeManagedRegions: false } : {}),\n });\n\n // dry-run reports the planned writes and stops BEFORE skills emission +\n // dedup (both would touch disk / load the embedder). The engine still throws\n // its \"not initialized\" gate when `.noir/project.id` is missing, so a dry-run\n // sync on an uninitialized project is a clean error (not a misleading no-op).\n if (dryRun) {\n reportPlannedWrites(res);\n return res;\n }\n\n // Surface the no-op so users see sync was a true no-op on disk (the\n // scaffold wrote nothing — every runtime file was content-hash identical).\n // Skills emission below still runs (it has its own dedup).\n if (res.written.length === 0 && res.identical.length > 0) {\n process.stderr.write('Nothing to sync (Noir-managed files are up to date).\\n');\n }\n\n const adapter = resolveAdapter(host);\n const skillsDir = adapter.skillsDir?.({ root });\n if (skillsDir === undefined) {\n // Standardized wording — same phrase across init/sync/create so logs\n // grep uniformly. (sync used to phrase this as \"nothing to sync\".)\n process.stderr.write(`host '${host}' has no skill emitter; skipping skills\\n`);\n } else {\n const target: CompileTarget = host;\n // Forward conflictOpts so the skills-emit conflict flow is\n // live in interactive mode (mirrors init.ts). --json/--no-input stays\n // prompt-free via the `interactive: false` guard.\n type SkillEmitOpts = NonNullable<Parameters<typeof emitSkillsToDir>[1]>;\n const skillOpts: SkillEmitOpts = {\n includeIntegrations: true,\n target,\n conflictPolicy: conflictOpts.conflictPolicy,\n interactive,\n };\n if (conflictOpts.onConflict !== undefined) {\n skillOpts.onConflict = conflictOpts.onConflict as SkillEmitOpts['onConflict'];\n }\n const summary = await emitSkillsToDir(skillsDir, skillOpts);\n const relDir = skillsDir.replace(`${root}/`, '');\n process.stderr.write(\n `Synced ${summary.emitted.length} Noir skills to ${relDir}/ (target: ${target}).\\n`,\n );\n // Surface stale-dir pruning so a user can see when a previous Noir\n // version's builtin was removed (the dir was deleted from .claude/skills/).\n // Pure hygiene; never affects correctness of the freshly-emitted pack.\n const pruned = summary.pruned ?? [];\n if (pruned.length > 0) {\n process.stderr.write(\n `Pruned ${pruned.length} stale noir-* skill dir${pruned.length === 1 ? '' : 's'}: ${pruned.join(', ')}\\n`,\n );\n }\n // The emitted count above is skills that are fully current; anything the\n // emit preserved is stale on disk and has to be named, exactly as `noir\n // init` names it.\n const stale = preservedStaleLine(summary.preserved ?? []);\n if (stale !== undefined) process.stderr.write(`${stale}\\n`);\n }\n\n // Write-path semantic dedup. Non-blocking; degrades to a\n // stderr warn-skip when the embedder is unavailable. Records near-dups on\n // `res.conflicts` so `--json` consumers see them without a prompt.\n const dedup = await checkWritePathDedup(root, res, { interactive, project: projectInfo });\n if (dedup.conflicts.length > 0) res.conflicts.push(...dedup.conflicts);\n\n return res;\n}\n\n/** Resolve the sync host: explicit `--host` override > `.noir/config.yml`\n * `host:` field > `'claude'` (default — preserves the v1.1 regression anchor\n * for projects initialized before the multi-host layer). The config read is\n * best-effort: if\n * `.noir/config.yml` is absent OR fails to parse, scaffold's own\n * \"not initialized\" gate (no `.noir/project.id`) fires below with the locked\n * error string. */\nfunction resolveSyncHost(root: string, opts: SyncOptions): HostId {\n if (opts.host !== undefined) return opts.host;\n if (!existsSync(paths.config(root))) return 'claude';\n try {\n return loadProjectInfo(root).config.host;\n } catch {\n return 'claude';\n }\n}\n","// `noir` bin entry. Migrated from the hand-rolled `parseArgs` dispatcher onto a\n// commander Command tree. Behavior of init / sync / mcp serve / daemon\n// start|stop / doctor is preserved flag-for-flag; the only change is the dispatch\n// path and the new global flags + exit-code contract.\n//\n// Testability: commander is configured with `.exitOverride()` so it NEVER calls\n// `process.exit`. The exported `createProgram()` factory returns a fresh, fully\n// wired program for unit tests; `run(argv)` is the bin's own entry and maps any\n// thrown error to `process.exitCode` (Node then exits naturally with that code).\n// A main-module guard prevents auto-running when bin.ts is imported by tests.\n\nimport { realpathSync } from 'node:fs';\nimport { pathToFileURL } from 'node:url';\nimport { type HostId, SUPPORTED_HOSTS } from '@noir-ai/adapters';\nimport { applyNoirEnv, NOIR_VERSION } from '@noir-ai/core';\nimport { Command, Option } from 'commander';\nimport { contextIndex, contextSearch, contextStatus } from './commands/context.js';\nimport {\n daemonRestart,\n daemonStart,\n daemonStatus,\n daemonStop,\n daemonToken,\n} from './commands/daemon.js';\nimport { doctor } from './commands/doctor.js';\nimport { captureAmbientEnv, env } from './commands/env.js';\nimport { type HandoffOptions, handoff } from './commands/handoff.js';\nimport { type HomeDeps, home } from './commands/home.js';\nimport {\n memoryCapture,\n memoryConsolidate,\n memoryForget,\n memoryRecall,\n memorySave,\n memorySessions,\n} from './commands/memory.js';\nimport { release } from './commands/release.js';\nimport { type RunOptions, run as runHostCommand } from './commands/run.js';\nimport { skillsLint, skillsList, skillsRegistry, skillsSync } from './commands/skills.js';\nimport { type StatusOptions, status } from './commands/status.js';\nimport {\n taskAbandon,\n taskAdvance,\n taskBlock,\n taskDecompose,\n taskNew,\n taskNext,\n taskResearch,\n taskResearchRecord,\n taskResume,\n taskStatus,\n taskVerify,\n} from './commands/task.js';\nimport {\n daemonJoin,\n workspaceLeave,\n workspaceList,\n workspaceStatus,\n workspaceStop,\n} from './commands/workspace.js';\nimport { init } from './init.js';\nimport {\n type CliOptions,\n EXIT,\n fail,\n handleError,\n inferExitCode,\n info,\n json,\n NoirCliError,\n requireInteractive,\n table,\n tip,\n} from './output.js';\nimport { listProfiles, loadRunConfig } from './run-profiles.js';\nimport { serve } from './serve.js';\nimport { buildPaletteCommands } from './tui/commands/registry.js';\nimport type { PaletteCommand } from './tui/palette/types.js';\n\n// Exit-code contract, error type, `fail`, and exit-code mapping live in\n// `./output.js` (the central output + exit-code infrastructure). Re-exported here so existing\n// imports from `./bin.js` (bin.test.ts, future commands) keep working without\n// a second source of truth.\nexport { EXIT, fail, inferExitCode, NoirCliError };\n\n/**\n * The TUI palette source. Walks a FRESH {@link createProgram} at `noir tui`\n * launch and projects every leaf subcommand into a {@link PaletteCommand}.\n * Defined here (not in the tui) so the palette derives from the REAL command\n * tree without the tui graph importing the bin (which would be circular).\n */\nexport function buildPaletteCommandsForTui(): PaletteCommand[] {\n return buildPaletteCommands(createProgram());\n}\n\n// ---------------------------------------------------------------------------\n// Program factory. Each call returns a fresh, independently-parseable Command\n// (commander parse state is mutable, so tests should drive a fresh program).\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Action-option bridge. Commander passes each action handler the positional\n// args + options + the action's Command as the LAST argument. The global flags\n// (`--json/--no-input/--quiet/--verbose/--cwd`, registered `` on\n// the program) land on `command.optsWithGlobals()`. The two helpers below read\n// those globals off the trailing Command without depending on how many params\n// commander injected in front of it (it varies with the command's arity), so a\n// single pattern works for every action.\n// ---------------------------------------------------------------------------\nfunction actionGlobals(args: readonly unknown[]): Record<string, unknown> {\n // `noUncheckedIndexedAccess` → element access yields `unknown`; the optional\n // chain + cast handles the undefined case without a runtime branch.\n const last = args[args.length - 1];\n return (last as Command | undefined)?.optsWithGlobals() ?? {};\n}\n\n/**\n * The trailing {@link Command} commander always passes as the LAST action\n * argument. Used by the context/memory/task actions to read their own options\n * (`cmd.opts()`) + globals (`cmd.optsWithGlobals()`) without depending on how\n * many positional args commander injected in front of it. Positional values are\n * read off the FRONT of `args` (commander puts positionals first, then the\n * options object, then the command). Throws on a malformed call rather than\n * silently coercing `undefined`.\n */\nfunction trailingCmd(args: readonly unknown[]): Command {\n const last = args[args.length - 1];\n if (!(last instanceof Command)) {\n throw new NoirCliError(\n EXIT.ERROR,\n 'internal: commander action callback received no trailing Command',\n );\n }\n return last;\n}\n\n/** Map commander's parsed global-flag bag onto the CLI's {@link CliOptions}. */\nfunction toCliOptions(g: Record<string, unknown>): CliOptions {\n // `--no-input` is stored under `input` (commander strips the `no-` prefix);\n // default `true`, so `input !== false` ⇒ input allowed.\n return {\n json: g.json === true,\n quiet: g.quiet === true,\n verbose: g.verbose === true,\n input: g.input !== false,\n // TUI policy — additive only. `--tui`/`--no-tui` land on `tui`\n // (true/false; absent when neither flag is given). Spread conditionally so\n // the default-args case (no flag) does NOT add a `tui` key — keeps the\n // exact-shape assertions in bin.test.ts green.\n ...(g.tui === true || g.tui === false ? { tui: g.tui === true } : {}),\n // `--no-tips` is stored under `tips` (default true; flag ⇒ false). Map to\n // the additive `noTips` spelling used by CliOptions + `tip()`.\n ...(g.tips === false ? { noTips: true } : {}),\n };\n}\n\n/** {@link toCliOptions} + the daemon-client knobs `status` needs. */\nfunction toStatusOptions(g: Record<string, unknown>): StatusOptions {\n return {\n json: g.json === true,\n quiet: g.quiet === true,\n verbose: g.verbose === true,\n input: g.input !== false,\n ...(g.tips === false ? { noTips: true } : {}),\n };\n}\n\n/**\n * Narrow commander's `string | undefined` host option to `HostId |\n * undefined`. Commander's `.choices(SUPPORTED_HOSTS)` already rejects unknown\n * values at parse time (usage=2), so by the time the action runs any string\n * present IS one of `SUPPORTED_HOSTS`. The cast is therefore total — but kept\n * explicit (not a type assertion) so a future change to the choices wiring\n * surfaces here rather than silently coercing. Returns `undefined` for an\n * unset flag so callers can conditional-spread (preserves the exact-arg-shape\n * assertions in bin.test.ts).\n */\nfunction parseHost(raw: string | undefined): HostId | undefined {\n if (raw === undefined) return undefined;\n // The cast is safe: commander's `.choices` enforces it pre-action.\n return raw as HostId;\n}\n\n// ---------------------------------------------------------------------------\n// TUI policy + deprecation / redirect infrastructure.\n//\n// Approach B (locked): TUI-primary UX, but NEVER hard-gate any subcommand. The\n// bare `noir` home menu is the documented human entry point; every subcommand\n// stays 100% scriptable. `--json` is the headless contract. The deprecation\n// registry below is the formal \"warn for N → redirect for N → never silently\n// remove\" channel; ZERO entries today (no command is deprecated).\n// ---------------------------------------------------------------------------\n\n/** One entry in the {@link DEPRECATIONS} registry. */\nexport interface DeprecationEntry {\n /** Old command form as a path prefix, e.g. `['legacy-status']` or\n * `['context','legacy']`. Matched against the dispatched command's path\n * (program name `noir` stripped). */\n readonly oldArgv: readonly string[];\n /** Replacement command form, e.g. `['status']`. Shown in the redirect hint. */\n readonly newArgv: readonly string[];\n /** Version the deprecation started in, e.g. `'1.4.0'`. */\n readonly since: string;\n}\n\n/**\n * Deprecation registry. **Empty today** — no `noir` command is deprecated.\n * When adding an entry, update the CHANGELOG + `docs/reference/cli.md` (and,\n * if the old form still routes, add a redirect). {@link emitDeprecationHintsFor}\n * scans this on every dispatch and emits one `tip()` per match; `--no-tips` /\n * `--json` suppress the hint (see {@link tip}).\n */\nexport const DEPRECATIONS: DeprecationEntry[] = [];\n\n/**\n * Build the dispatched command's path (e.g. `['noir','context','search']`) by\n * walking `.parent` up to the program root. Used by {@link emitDeprecationHintsFor}\n * to match an {@link DeprecationEntry.oldArgv} prefix (program name stripped).\n */\nfunction commandPath(cmd: Command): string[] {\n const path: string[] = [];\n let c: Command | null = cmd;\n while (c !== null) {\n path.unshift(c.name());\n c = c.parent;\n }\n return path;\n}\n\n/**\n * Emit one redirect hint per matching {@link DEPRECATIONS} entry for the\n * dispatched command. Respects `--no-tips` and `--json` via {@link tip} (a CI\n * run's stderr stays quiet and its stdout envelope stays pristine). Exported so\n * tests can drive it with a temporarily-populated registry without going\n * through commander.\n */\nexport function emitDeprecationHintsFor(cmd: Command, opts: CliOptions): void {\n if (DEPRECATIONS.length === 0) return; // common case (no entries today)\n // Strip the leading program name ('noir') for matching.\n const rel = commandPath(cmd).slice(1);\n for (const d of DEPRECATIONS) {\n if (d.oldArgv.every((a, i) => rel[i] === a)) {\n tip(\n `\\`noir ${d.oldArgv.join(' ')}\\` is deprecated since v${d.since}; use \\`noir ${d.newArgv.join(' ')}\\`.`,\n opts,\n );\n }\n }\n}\n\nexport function createProgram(): Command {\n const program = new Command();\n\n program\n .name('noir')\n .description('Noir — discipline, context, and memory layer for agentic CLIs.')\n .version(NOIR_VERSION, '-v, --version')\n // Global flags. makeGlobal() propagates each to every subcommand so\n // they parse in any position (e.g. `noir status --json` as well as `noir\n // --json status`) and appear on subcommand --help.\n .addOption(new Option('--json', 'emit machine-readable JSON to stdout'))\n .addOption(new Option('--no-input', 'never prompt; error if input is required'))\n .addOption(new Option('--quiet', 'suppress non-essential diagnostics'))\n .addOption(new Option('--verbose', 'show additional diagnostic detail'))\n .addOption(new Option('--cwd <path>', 'run as if started in <path>'))\n // TUI policy — advisory routing for bare `noir` only (never hard-gates a\n // subcommand). `--tui`/`--no-tui` are a commander negatable pair: parsed\n // onto a single `tui` attribute (true / false / absent=auto). Bare `noir`\n // in a TTY runs the home menu by default; `--no-tui` forces the\n // non-interactive `status` path even in a TTY; `--tui` is a hint that still\n // requires a TTY (so it never blocks in CI / a pipe). See home.ts + CLAUDE.md.\n .addOption(\n new Option('--tui', 'prefer the interactive home menu for bare `noir` (advisory; TTY-only)'),\n )\n .addOption(\n new Option(\n '--no-tui',\n 'route bare `noir` to the non-interactive `status` path even in a TTY',\n ),\n )\n // Deprecation / redirect hints — `--no-tips` silences the `tip()` helper\n // for CI / log-friendly runs. No command is deprecated today; the flag is\n // the headless contract for quieting future notices.\n .addOption(new Option('--no-tips', 'suppress redirect / deprecation hints on stderr'))\n .exitOverride((err) => {\n // Never process.exit; surface to the caller (run()/test) instead.\n throw err;\n })\n .configureOutput({\n // Stream discipline: --help/--version → stdout (conventional; users pipe\n // them), errors → stderr. --json data is emitted by command actions via\n // process.stdout directly, NOT by commander's writeOut, so routing help/\n // version to stdout does not conflict with --json payloads.\n writeOut: (str) => process.stdout.write(str),\n writeErr: (str) => process.stderr.write(str),\n });\n\n // --cwd: chdir before the action runs so the existing modules (which read\n // process.cwd()) honor it. actionCmd.optsWithGlobals() works on whichever\n // program instance is being parsed, so createProgram() isolates correctly.\n program.hook('preAction', (_thisCmd, actionCmd) => {\n const opts = actionCmd.optsWithGlobals();\n const cwd = opts.cwd;\n if (typeof cwd === 'string' && cwd.length > 0) {\n try {\n process.chdir(cwd);\n } catch (err) {\n throw new NoirCliError(\n EXIT.USAGE,\n `--cwd: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n // Load project-local .noir/.env AFTER the --cwd chdir so the .env root\n // matches the effective project (a `--cwd /other` run must never inherit the\n // launch dir's tokens into the host subprocess). Idempotent — and the\n // `.noir/.env` rule\n // inverts the usual dotenv rule: a key the file DEFINES wins, and the real\n // environment is the fallback for keys the file omits. The overlay is\n // confined to this process tree (see applyNoirEnv), so a sibling shell and\n // the user's own manual `claude` invocations are untouched.\n //\n // `noir env` is the ONE command that must also see the AMBIENT environment\n // as it was before that overlay: reporting \"the file's value won, and the\n // one you exported is being ignored\" needs the pre-overlay values, which are\n // indistinguishable from the file's own afterwards. Capture it for that\n // command only — every other command just applies the overlay.\n if (actionCmd.name() === 'env') captureAmbientEnv();\n applyNoirEnv(process.cwd());\n // Propagate --json / --no-input to the deep conflict resolver via env\n // so a regenerate conflict never prompts under those flags (the @clack\n // prompt writes to stdout — it would corrupt --json output and violate\n // --no-input). Done here centrally so init/create/sync need no arg changes\n // (no bin.test.ts arg-pin cascade). buildConflictOpts reads this flag.\n const nonInteractive = opts.json === true || opts.input === false;\n if (nonInteractive) process.env.NOIR_NON_INTERACTIVE = '1';\n else delete process.env.NOIR_NON_INTERACTIVE;\n // Deprecation / redirect hints. Scans {@link DEPRECATIONS} against the\n // dispatched command path; emits via `tip()` (suppressed by --no-tips /\n // --json). Empty registry today — no-op until an entry is added.\n emitDeprecationHintsFor(actionCmd, toCliOptions(opts));\n });\n\n // ----- migrated commands (behavior-preserving) -----\n\n program\n .command('init')\n .description('scaffold Noir in the current project (.noir/, .mcp.json, CLAUDE.md, skills)')\n .option('--transport <transport>', 'stdio | streamable-http (default: stdio)', 'stdio')\n .option('--url <url>', 'streamable-http daemon URL (localhost only)')\n .option(\n '--upgrade',\n 'run scaffold migrations before re-emitting (re-run on an existing project)',\n )\n .option('--force', 're-scaffold even if already initialized (bypasses the already-init no-op)')\n .option('--dry-run', 'report planned writes without writing anything')\n .option('--preview', 'alias for --dry-run')\n .addOption(\n // Target host. When the flag is absent the host is read from an existing\n // `.noir/config.yml`, falling back to `'claude'` (so a fresh project is\n // unaffected). The chosen host is forwarded to scaffold() + skills\n // emission via resolveAdapter. The choice list mirrors `SUPPORTED_HOSTS`\n // so a new host lands here automatically; commander rejects anything else\n // as usage=2.\n new Option(\n '--host <id>',\n 'target agentic CLI (default: the host in .noir/config.yml, else claude)',\n ).choices(SUPPORTED_HOSTS),\n )\n .action(\n async (\n opts: {\n transport?: string;\n url?: string;\n upgrade?: boolean;\n force?: boolean;\n host?: string;\n dryRun?: boolean;\n preview?: boolean;\n },\n cmd: Command,\n ) => {\n // Preserve the parseArgs coercion exactly: only 'streamable-http' is\n // special; any other value (incl. typos / future transports) → 'stdio'.\n const transport: 'stdio' | 'streamable-http' =\n opts.transport === 'streamable-http' ? 'streamable-http' : 'stdio';\n // `upgrade`/`host` are conditionally spread so an UNSET flag does NOT add\n // an `upgrade: false` / `host: …` key to the opts object — that would\n // break the toEqual-based arg assertions in bin.test.ts (which expects\n // exactly `{transport, url}` for the default-invocation cases). Matches\n // the conditional-spread pattern used for task/memory options elsewhere.\n const upgrade = opts.upgrade === true;\n const force = opts.force === true;\n const host = parseHost(opts.host);\n // --dry-run/--preview collapse to a single dryRun boolean (the\n // command modules read both spellings, but the bin normalizes).\n const dryRun = opts.dryRun === true || opts.preview === true;\n const result = await init(process.cwd(), {\n transport,\n url: opts.url,\n ...(upgrade ? { upgrade } : {}),\n ...(force ? { force } : {}),\n ...(host !== undefined ? { host } : {}),\n ...(dryRun ? { dryRun } : {}),\n });\n // ScaffoldResult gap close: surface the structured ScaffoldResult (with\n // `conflicts[]`) on stdout under `--json`, wrapped in the versioned\n // `{ok, data}` envelope so it matches the headless contract every read\n // command uses. The args passed to init() are UNCHANGED (bin.test.ts\n // arg pins still hold); only the return value is captured + emitted.\n // `init` returns `undefined` on the already-initialized no-op — skip.\n if (cmd.optsWithGlobals().json === true && result !== undefined) {\n json({ ok: true, data: result });\n }\n },\n );\n\n // `noir create [dir]` — greenfield AI-layer bootstrap. Lazy import\n // mirrors sync's dispatcher so the create module isn't loaded for unrelated\n // commands. Optional `[dir]` defaults to process.cwd() inside the action.\n program\n .command('create [dir]')\n .description('bootstrap the Noir AI layer in a new or empty directory')\n .option('--transport <transport>', 'stdio | streamable-http (default: stdio)', 'stdio')\n .option('--url <url>', 'streamable-http daemon URL (localhost only)')\n .option('--force', 're-scaffold even if already initialized (bypasses the already-init no-op)')\n .option('--dry-run', 'report planned writes without writing anything')\n .option('--preview', 'alias for --dry-run')\n .addOption(\n new Option(\n '--host <id>',\n 'target agentic CLI (default: claude) — drives host-side emission',\n ).choices(SUPPORTED_HOSTS),\n )\n .action(\n async (\n dir: string | undefined,\n opts: {\n transport?: string;\n url?: string;\n force?: boolean;\n host?: string;\n dryRun?: boolean;\n preview?: boolean;\n },\n cmd: Command,\n ) => {\n const transport: 'stdio' | 'streamable-http' =\n opts.transport === 'streamable-http' ? 'streamable-http' : 'stdio';\n const force = opts.force === true;\n const host = parseHost(opts.host);\n // --dry-run/--preview collapse to a single dryRun boolean.\n const dryRun = opts.dryRun === true || opts.preview === true;\n const { create } = await import('./commands/create.js');\n const result = await create(dir, {\n transport,\n url: opts.url,\n ...(force ? { force } : {}),\n ...(host !== undefined ? { host } : {}),\n ...(dryRun ? { dryRun } : {}),\n });\n // ScaffoldResult gap close: surface the structured ScaffoldResult (with\n // `conflicts[]`) on stdout under `--json`, wrapped in the `{ok, data}`\n // envelope (headless contract). Skipped on the no-op return.\n if (cmd.optsWithGlobals().json === true && result !== undefined) {\n json({ ok: true, data: result });\n }\n },\n );\n\n program\n .command('sync')\n .description(\n 're-emit Noir managed files (.mcp.json, CLAUDE.md blocks, NOIR.md brief, ignores) + skills',\n )\n .option(\n '--force',\n 'overwrite differing regenerated files without prompting (bypasses the conflict menu)',\n )\n .option(\n '--merge',\n 'three-way merge managed regions (default since 1.3.0; flag kept for compatibility)',\n )\n .option('--dry-run', 'report planned writes without writing anything')\n .option('--preview', 'alias for --dry-run')\n .addOption(\n // Opt OUT of managed-region merge (restore strip-replace). Commander\n // stores `--no-merge-regions` under `mergeRegions` (default true; flag →\n // false). Bare `noir sync` keeps the merge default (true).\n new Option(\n '--no-merge-regions',\n 'strip-replace managed regions (discard hand-edits inside <!-- noir:* --> markers)',\n ),\n )\n .addOption(\n // Optional `--host` override. When omitted, sync reads host from\n // `.noir/config.yml` (whatever `noir init --host <id>` persisted). The\n // override is rarely needed — documented as advanced.\n new Option(\n '--host <id>',\n 'override the configured host (advanced; default reads .noir/config.yml)',\n ).choices(SUPPORTED_HOSTS),\n )\n .action(\n async (\n opts: {\n host?: string;\n force?: boolean;\n merge?: boolean;\n mergeRegions?: boolean;\n dryRun?: boolean;\n preview?: boolean;\n },\n cmd: Command,\n ) => {\n // Lazy import preserves the original dispatcher's deferred module load.\n const { sync } = await import('./sync.js');\n const host = parseHost(opts.host);\n const force = opts.force === true;\n const merge = opts.merge === true;\n // `--no-merge-regions` → commander stores `mergeRegions: false`.\n const noMergeRegions = opts.mergeRegions === false;\n // --dry-run/--preview collapse to a single dryRun boolean.\n const dryRun = opts.dryRun === true || opts.preview === true;\n // Single-positional regression anchor: when no `--host`/`--force`/`--merge`/\n // `--no-merge-regions`/`--dry-run` is given, call `sync(cwd)` exactly\n // (bin.test.ts pins this). Only spread the opts bag when a flag was\n // explicit so the default-args snapshot stays green.\n const result =\n host === undefined && !force && !merge && !noMergeRegions && !dryRun\n ? await sync(process.cwd())\n : await sync(process.cwd(), {\n ...(host !== undefined ? { host } : {}),\n ...(force ? { force } : {}),\n ...(merge ? { merge } : {}),\n ...(noMergeRegions ? { mergeManagedRegions: false } : {}),\n ...(dryRun ? { dryRun } : {}),\n });\n // ScaffoldResult gap close: surface the structured ScaffoldResult (with\n // `conflicts[]`) on stdout under `--json`, wrapped in the `{ok, data}`\n // envelope (headless contract). sync() always returns a ScaffoldResult\n // (never undefined), so the guard is the json flag only.\n if (cmd.optsWithGlobals().json === true) {\n json({ ok: true, data: result });\n }\n },\n );\n\n // `mcp` group — preserve legacy bare-`mcp` usage error.\n const mcpCmd = program.command('mcp').description('MCP server control');\n mcpCmd\n .command('serve')\n .description('run the Noir MCP server (stdio, or via the shared daemon)')\n .option('--stdio', 'force the stdio transport')\n .action(async (opts: { stdio?: boolean }) => {\n await serve({ stdio: opts.stdio === true });\n });\n mcpCmd.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir mcp serve [--stdio]');\n });\n\n // `daemon` group — start/stop/status/restart.\n // `start` runs the daemon in the FOREGROUND by default; `--detach` forks a\n // detached child. `--_detached-child` is the hidden marker the detached\n // child carries: it tells the child it IS the daemon (run in-process),\n // so it is never shown in `--help` and never meant for users.\n const daemonGrp = program.command('daemon').description('control the Noir daemon');\n daemonGrp\n .command('start')\n .description('start the Noir daemon (foreground, or background with --detach)')\n .option('--detach', 'run the daemon in the background and exit')\n .option('--workspace <name>', 'start a shared cross-repo workspace daemon instead')\n .option('--force', 'overwrite a non-Noir .mcp.json entry (--workspace only)')\n .addOption(\n new Option('--_detached-child', 'reserved: detached daemon child (internal)').hideHelp(),\n )\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const detach = g.detach === true;\n const detachChild = g._detachedChild === true;\n const workspace = typeof g.workspace === 'string' ? g.workspace : undefined;\n await daemonStart({\n ...toCliOptions(g),\n ...(detach ? { detach } : {}),\n ...(detachChild ? { detachChild } : {}),\n ...(workspace !== undefined ? { workspace } : {}),\n ...(g.force === true ? { force: true } : {}),\n });\n });\n daemonGrp\n .command('join')\n .description('join a shared workspace from this repo')\n .argument('<name>', 'workspace name')\n .option('--force', 'overwrite a non-Noir .mcp.json entry')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const name = typeof args[0] === 'string' ? (args[0] as string) : '';\n await daemonJoin({ ...toCliOptions(g), name, ...(g.force === true ? { force: true } : {}) });\n });\n daemonGrp\n .command('stop')\n .description('stop the Noir daemon')\n .action(async (...args: unknown[]) => {\n await daemonStop(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n daemonGrp\n .command('status')\n .description('report daemon pid/uptime/mode (exit 4 if not running)')\n .action(async (...args: unknown[]) => {\n await daemonStatus(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n daemonGrp\n .command('token')\n .description('print the daemon bearer token to stdout (for a host headersHelper)')\n .action(async (...args: unknown[]) => {\n await daemonToken(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n daemonGrp\n .command('restart')\n .description('stop then start the daemon')\n .option('--detach', 'run the daemon in the background and exit')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n await daemonRestart({\n ...toCliOptions(g),\n ...(g.detach === true ? { detach: true } : {}),\n });\n });\n daemonGrp.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir daemon start|join|stop|status|restart|token');\n });\n daemonGrp.addHelpText(\n 'after',\n [\n '',\n 'Shared workspaces (two repos, one product — e.g. backend + frontend):',\n ' noir daemon start --workspace <name> --detach found a workspace from this repo',\n ' noir daemon join <name> join it from the other repo',\n '',\n 'Guide: docs/how-to/shared-workspaces.md',\n ].join('\\n'),\n );\n\n const workspaceGrp = program.command('workspace').description('shared cross-repo workspaces');\n workspaceGrp\n .command('list')\n .description('list workspaces on this machine')\n .action(async (...args: unknown[]) => {\n await workspaceList(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n workspaceGrp\n .command('status')\n .description('members + daemon liveness for a workspace')\n .argument('[name]', \"workspace name (defaults to this repo's joined workspace)\")\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const name =\n typeof args[0] === 'string' && (args[0] as string).length > 0\n ? (args[0] as string)\n : undefined;\n await workspaceStatus({\n ...toCliOptions(cmd.optsWithGlobals()),\n ...(name !== undefined ? { name } : {}),\n });\n });\n workspaceGrp\n .command('leave')\n .description('leave the workspace this repo joined (restore stdio)')\n .option('--force', 'overwrite a non-Noir .mcp.json entry')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n await workspaceLeave({\n ...toCliOptions(g),\n ...(g.force === true ? { force: true } : {}),\n });\n });\n workspaceGrp\n .command('stop')\n .description('stop a workspace daemon (membership retained)')\n .argument('[name]', \"workspace name (defaults to this repo's joined workspace)\")\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const name =\n typeof args[0] === 'string' && (args[0] as string).length > 0\n ? (args[0] as string)\n : undefined;\n await workspaceStop({\n ...toCliOptions(cmd.optsWithGlobals()),\n ...(name !== undefined ? { name } : {}),\n });\n });\n workspaceGrp.addHelpText(\n 'after',\n [\n '',\n 'This group only inspects and leaves workspaces. To CREATE or JOIN one:',\n ' noir daemon start --workspace <name> --detach found it (from the first repo)',\n ' noir daemon join <name> join it (from the second repo)',\n '',\n 'Guide: docs/how-to/shared-workspaces.md',\n ].join('\\n'),\n );\n workspaceGrp.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir workspace list|status|leave|stop');\n });\n\n program\n .command('doctor')\n .description('environment + project health')\n .option(\n '--dedup',\n 'scan host-context + .noir/ docs for semantic near-duplicates (loads the local embedder)',\n )\n .action(async (...args: unknown[]) => {\n const g = actionGlobals(args);\n const dedup = trailingCmd(args).opts().dedup === true;\n await doctor({ ...toCliOptions(g), ...(dedup ? { dedup: true } : {}) });\n });\n\n // ----- new subcommand groups -----\n // Signatures match the exit-code + stream discipline so --help is accurate;\n // every action dispatches to\n // its command module in ./commands/*.js.\n\n program\n .command('status')\n .description('project + daemon + workflow + store snapshot')\n .action(async (...args: unknown[]) => {\n // Global flags (`--json`/`--verbose`/…) reach status via the trailing\n // Command; the action itself owns no command-specific options.\n await status(toStatusOptions(actionGlobals(args)));\n });\n\n // `noir env` — which configuration is in effect and where each value comes\n // from. READ-ONLY, and names-and-shapes only: never a value.\n program\n .command('env')\n .description('which configuration is in effect and where each value comes from')\n .action(async (...args: unknown[]) => {\n await env(toCliOptions(actionGlobals(args)));\n });\n\n const contextGrp = program.command('context').description('context engine');\n contextGrp\n .command('search')\n .description('hybrid search over the indexed context')\n .argument('<query>', 'search query')\n .option('--limit <n>', 'max results', '10')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n // Positional `<query>` is always the leading action arg.\n const query = typeof args[0] === 'string' ? (args[0] as string) : '';\n const limit = typeof g.limit === 'string' ? (g.limit as string) : undefined;\n await contextSearch({\n ...toCliOptions(g),\n query,\n ...(limit === undefined ? {} : { limit }),\n });\n });\n contextGrp\n .command('index')\n .description('(re)index project files into the context store')\n // Repeatable: the `(val, acc) => [...acc, val]` coercion accumulates each\n // `--path` into an array (commander's idiomatic collect pattern), so\n // `--path a --path b` → `['a','b']` instead of last-wins.\n .option(\n '--path <p>',\n 'path to index (repeatable)',\n (val: string, acc: string[]) => [...acc, val],\n [] as string[],\n )\n .option('--force', 'force a full reindex (drop all chunks+vectors, re-index from scratch)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const rawPaths = Array.isArray(g.path)\n ? (g.path as unknown[]).filter((p): p is string => typeof p === 'string')\n : [];\n const force = g.force === true;\n await contextIndex({\n ...toCliOptions(g),\n ...(rawPaths.length === 0 ? {} : { paths: rawPaths }),\n ...(force ? { force } : {}),\n });\n });\n contextGrp\n .command('status')\n .description('index freshness + counts')\n .action(async (...args: unknown[]) => {\n await contextStatus(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n contextGrp.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir context search|index|status');\n });\n\n const memoryGrp = program.command('memory').description('memory engine');\n memoryGrp\n .command('recall')\n .description('recall memories for a query')\n .argument('<query>', 'recall query')\n .option('--limit <n>', 'max results', '10')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const query = typeof args[0] === 'string' ? (args[0] as string) : '';\n const limit = typeof g.limit === 'string' ? (g.limit as string) : undefined;\n await memoryRecall({\n ...toCliOptions(g),\n query,\n ...(limit === undefined ? {} : { limit }),\n });\n });\n memoryGrp\n .command('save')\n .description('save an observation to long-term memory')\n // Both optional: `--content` is prompted interactively when absent (memory.ts),\n // or the command fails exit 2 under non-interactive / --no-input / --json.\n .option('--content <text>', 'memory content (prompted interactively if omitted)')\n .option(\n '--type <type>',\n 'observation type (pattern | preference | architecture | bug | workflow | fact | decision)',\n )\n .option('--files <files>', 'comma-separated related file paths')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const content = typeof g.content === 'string' ? (g.content as string) : undefined;\n const type = typeof g.type === 'string' ? (g.type as string) : undefined;\n const files = typeof g.files === 'string' ? (g.files as string) : undefined;\n await memorySave({\n ...toCliOptions(g),\n ...(content === undefined ? {} : { content }),\n ...(type === undefined ? {} : { type }),\n ...(files === undefined ? {} : { files }),\n });\n });\n memoryGrp\n .command('sessions')\n .description('list recent memory sessions')\n .action(async (...args: unknown[]) => {\n await memorySessions(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n memoryGrp\n .command('forget')\n .description('delete one or more memories by id')\n .argument('<ids...>', 'observation id(s)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n // Variadic positional → commander passes a string[] as the leading arg.\n const rawIds = Array.isArray(args[0]) ? (args[0] as unknown[]) : [];\n const ids = rawIds.filter((x): x is string => typeof x === 'string');\n await memoryForget({ ...toCliOptions(g), ids });\n });\n memoryGrp\n .command('consolidate')\n .description('consolidate memories into a derived lesson (provider-explicit)')\n .option('--types <types>', 'comma-separated observation types to consolidate')\n .option('--limit <n>', 'cap on candidate observations')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const types = typeof g.types === 'string' ? (g.types as string) : undefined;\n const limit = typeof g.limit === 'string' ? (g.limit as string) : undefined;\n await memoryConsolidate({\n ...toCliOptions(g),\n ...(types === undefined ? {} : { types }),\n ...(limit === undefined ? {} : { limit }),\n });\n });\n memoryGrp\n .command('capture')\n .description('distill a transcript/notes file (or stdin) into a memory (manual)')\n .argument('[file]', 'transcript/notes file (or pipe stdin)')\n .option('--content <text>', 'inline distilled content')\n .option('--event-type <type>', 'capture hook label (defaults to Stop)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const file =\n typeof args[0] === 'string' && (args[0] as string).length > 0\n ? (args[0] as string)\n : undefined;\n const content = typeof g.content === 'string' ? (g.content as string) : undefined;\n const eventType = typeof g.eventType === 'string' ? (g.eventType as string) : undefined;\n await memoryCapture({\n ...toCliOptions(g),\n ...(file === undefined ? {} : { file }),\n ...(content === undefined ? {} : { content }),\n ...(eventType === undefined ? {} : { eventType }),\n });\n });\n memoryGrp.action(() => {\n throw new NoirCliError(\n EXIT.USAGE,\n 'Usage: noir memory recall|save|capture|sessions|forget|consolidate',\n );\n });\n\n // `skills` group — list/sync the builtin pack in-process.\n const skillsGrp = program.command('skills').description('builtin skills');\n skillsGrp\n .command('list')\n .description('list installed Noir skills')\n .action(async (...args: unknown[]) => {\n await skillsList(toCliOptions(actionGlobals(args)));\n });\n skillsGrp\n .command('sync')\n .description('re-emit skills to the host skills dir')\n .action(async (...args: unknown[]) => {\n await skillsSync(toCliOptions(actionGlobals(args)));\n });\n skillsGrp\n .command('lint')\n .description('structural quality gate over the shipped pack')\n .action(async (...args: unknown[]) => {\n await skillsLint(toCliOptions(actionGlobals(args)));\n });\n skillsGrp\n .command('registry')\n .description('emit the runtime-derived skill registry')\n .action(async (...args: unknown[]) => {\n await skillsRegistry(toCliOptions(actionGlobals(args)));\n });\n skillsGrp.action(() => {\n throw new NoirCliError(EXIT.USAGE, 'Usage: noir skills list|sync|lint|registry');\n });\n\n const taskGrp = program.command('task').description('workflow task control');\n taskGrp\n .command('new')\n .description('start a new workflow task')\n .requiredOption('--slug <slug>', 'task slug')\n .option('--mode <mode>', 'full | quick')\n .option(\n '--class <taskClass>',\n 'task class (feature/epic/enhancement/bugfix/spike/quick-task/refactor) — drives the PRD gate',\n )\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const slug = typeof g.slug === 'string' ? (g.slug as string) : '';\n const mode = typeof g.mode === 'string' ? (g.mode as string) : undefined;\n const taskClass = typeof g.class === 'string' ? (g.class as string) : undefined;\n await taskNew({\n ...toCliOptions(g),\n slug,\n ...(mode === undefined ? {} : { mode }),\n ...(taskClass === undefined ? {} : { taskClass }),\n });\n });\n taskGrp\n .command('status')\n .description('active (or named) task status')\n .argument('[id]', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n // Optional positional: only a string at args[0] is the task id.\n const first = args[0];\n const id = typeof first === 'string' ? first : undefined;\n await taskStatus({\n ...toCliOptions(g),\n ...(id === undefined ? {} : { id }),\n });\n });\n taskGrp\n .command('advance')\n .description('advance the active task to the next phase')\n .option('--to <phase>', 'target phase')\n .option('--force <reason>', 'force the gate with a reason')\n .option('--skip', 'record the landing gate as skipped (advance continues)')\n .option('--no-artifacts', 'skip the document-phase artifact writes at done')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const to = typeof g.to === 'string' ? (g.to as string) : undefined;\n const force = typeof g.force === 'string' ? (g.force as string) : undefined;\n await taskAdvance({\n ...toCliOptions(g),\n ...(to === undefined ? {} : { to }),\n ...(force === undefined ? {} : { force }),\n ...(g.skip === true ? { skip: true } : {}),\n ...(g.artifacts === false ? { noArtifacts: true } : {}),\n });\n });\n taskGrp\n .command('next')\n .description('suggest the next phase + applicable skill')\n .action(async (...args: unknown[]) => {\n await taskNext(toCliOptions(trailingCmd(args).optsWithGlobals()));\n });\n taskGrp\n .command('decompose')\n .description('decompose a capability into an implementation plan')\n .argument('<capability>', 'capability id')\n .option('--out <path>', 'output path for the plan JSON')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const capability = typeof args[0] === 'string' ? (args[0] as string) : '';\n await taskDecompose({\n ...toCliOptions(g),\n capability,\n ...(typeof g.out === 'string' && g.out.length > 0 ? { out: g.out } : {}),\n });\n });\n taskGrp\n .command('verify')\n .description('run configured verify checks and submit evidence to the verify gate')\n .option(\n '--check <name>',\n 'restrict to a named check (repeatable)',\n (val: string, acc: string[]) => [...acc, val],\n [],\n )\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const check = Array.isArray(g.check) ? (g.check as string[]) : undefined;\n await taskVerify({\n ...toCliOptions(g),\n ...(check === undefined || check.length === 0 ? {} : { check }),\n });\n });\n taskGrp\n .command('research')\n .description('list research findings for the active (or named) task')\n .argument('[id]', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const first = args[0];\n const id = typeof first === 'string' ? first : undefined;\n await taskResearch({\n ...toCliOptions(g),\n ...(id === undefined ? {} : { id }),\n });\n });\n taskGrp\n .command('research-record')\n .description('record a research finding for the active (or named) task')\n .requiredOption('--type <type>', 'assumption | discovery | decision | grounding-fact')\n .requiredOption('--text <text>', 'finding text (capped)')\n .option('--source <ref>', 'evidence/citation (required unless grounding-fact)')\n .option('--task <id>', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n await taskResearchRecord({\n ...toCliOptions(g),\n type: String(g.type ?? ''),\n text: String(g.text ?? ''),\n ...(typeof g.source === 'string' && g.source.length > 0 ? { source: g.source } : {}),\n ...(typeof g.task === 'string' && g.task.length > 0 ? { task: g.task } : {}),\n });\n });\n taskGrp\n .command('resume')\n .description('resume the active (or named) in-flight/blocked task')\n .argument('[id]', 'task id (defaults to active)')\n .option('--prompt <text>', 'a continue instruction to surface in the briefing')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const first = args[0];\n const id = typeof first === 'string' ? first : undefined;\n const prompt = typeof g.prompt === 'string' ? (g.prompt as string) : undefined;\n await taskResume({\n ...toCliOptions(g),\n ...(id === undefined ? {} : { id }),\n ...(prompt === undefined ? {} : { prompt }),\n });\n });\n taskGrp\n .command('block')\n .description('mark the active (or named) task blocked with a reason')\n .argument('<reason>', 'why the task is stuck (non-empty)')\n .option('--task <id>', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const reason = typeof args[0] === 'string' ? (args[0] as string) : '';\n const task = typeof g.task === 'string' ? (g.task as string) : undefined;\n await taskBlock({\n ...toCliOptions(g),\n reason,\n ...(task === undefined ? {} : { task }),\n });\n });\n taskGrp\n .command('abandon')\n .description('abandon the active (or named) task (terminal, confirmed)')\n .option('--task <id>', 'task id (defaults to active)')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n const task = typeof g.task === 'string' ? (g.task as string) : undefined;\n await taskAbandon({\n ...toCliOptions(g),\n ...(task === undefined ? {} : { task }),\n });\n });\n taskGrp.action(() => {\n throw new NoirCliError(\n EXIT.USAGE,\n 'Usage: noir task new|status|advance|next|verify|research|research-record|resume|block|abandon',\n );\n });\n\n // `noir install` / `noir migrate`: move to the native install path,\n // preserving all settings. `migrate` is an alias (mirrors claude migrate-installer).\n const installCmd = program\n .command('install')\n .description(\n 'install Noir via the native managed-Node path (or migrate from another install method)',\n )\n .option('--list', 'list detected install methods')\n .option('--uninstall-prev', 'after a successful migrate, uninstall the previous install method')\n .option(\n '--dismiss',\n 'dismiss the migration banner for the current version (persists in install.json)',\n )\n .argument('[spec]', \"channel ('latest'|'beta') or exact version (default: latest)\")\n .action(\n async (\n spec: string | undefined,\n opts: { list?: boolean; uninstallPrev?: boolean; dismiss?: boolean },\n cmd: Command,\n ) => {\n const { install } = await import('./commands/install.js');\n const cli = cmd.optsWithGlobals() as CliOptions;\n await install({\n ...cli,\n spec,\n list: opts.list === true,\n uninstallPrev: opts.uninstallPrev === true,\n dismiss: opts.dismiss === true,\n });\n },\n );\n // `migrate` alias -- same behavior.\n installCmd.alias('migrate');\n\n // `noir update`: self-update via the active install method.\n program\n .command('update')\n .description('update Noir to the latest version via the active install method')\n .option('--check', 'check for a new version without changing anything')\n .argument('[spec]', \"channel ('latest'|'beta') or exact version (default: latest)\")\n .action(async (spec: string | undefined, opts: { check?: boolean }, cmd: Command) => {\n const { update } = await import('./commands/update.js');\n const cli = cmd.optsWithGlobals() as CliOptions;\n await update({ ...cli, spec, check: opts.check === true });\n });\n\n // `noir handoff` + the `noir wrap` session-end alias. Both dispatch the\n // SAME handler; the artifact reuses `gatherStatusPayload` (status.ts) +\n // `PHASE_SKILL` (task.ts) for the snapshot, does a bounded context/memory\n // extraction, and renders a pasteable host-handoff markdown block to STDOUT.\n // `--write` persists to `.noir/handoff/HO-<NNNN>-<id>.md` (gitignored); `--json` emits\n // the structured payload. Doctrine: the host-launch directive is TEXT ONLY —\n // Noir never spawns the host.\n function buildHandoffOptions(g: Record<string, unknown>): HandoffOptions {\n return { ...toCliOptions(g), ...(g.write === true ? { write: true } : {}) };\n }\n program\n .command('handoff')\n .description('emit a ready-to-paste host handoff prompt')\n .option('--write', 'persist to .noir/handoff/HO-<NNNN>-<id>.md (gitignored)')\n .action(async (...args: unknown[]) => {\n await handoff(buildHandoffOptions(trailingCmd(args).optsWithGlobals()));\n });\n // `noir wrap` — session-end alias (same handler, friendlier name at the end of\n // a session). Same options; no separate code path.\n program\n .command('wrap')\n .description('session-end alias for `noir handoff`')\n .option('--write', 'persist to .noir/handoff/HO-<NNNN>-<id>.md (gitignored)')\n .action(async (...args: unknown[]) => {\n await handoff(buildHandoffOptions(trailingCmd(args).optsWithGlobals()));\n });\n\n // `noir release <version> [--channel beta|stable] [--dry-run]` — guided\n // orchestrator over the patch-release flow. Hands off\n // at the human-approval gates; NEVER auto-approves the GitHub publish job.\n program\n .command('release')\n .description('guided release orchestrator over the patch-release flow')\n .argument('[version]', 'target version, e.g. 1.10.0')\n .option('--channel <channel>', 'beta (default) | stable')\n .option('--dry-run', 'print the checklist without executing any steps')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n await release({\n ...toCliOptions(g),\n ...(typeof args[0] === 'string' && (args[0] as string).length > 0\n ? { version: args[0] as string }\n : {}),\n ...(typeof g.channel === 'string' && (g.channel as string).length > 0\n ? { channel: g.channel as string }\n : {}),\n ...(g.dryRun === true ? { dryRun: true } : {}),\n });\n });\n\n // `noir tui` — the interactive Ink dashboard. LAZY-loaded: the Ink app\n // (React) is imported ONLY inside this action via `await import('./tui/…')`,\n // so React + Ink never enter the main CLI startup path. `noir status`,\n // `noir doctor`, a bare `noir`, etc. stay fast and React-free. The dispatch\n // seam is the SAME shape `home(opts, deps)` uses (a fresh commander program\n // per dispatch, with its own error → exit-code mapping) — command routing is\n // NOT reimplemented in the TUI; `/`-prefixed inputs are routed through it.\n // Interactive-only: requires a TTY; under `--json` / `--no-input` / non-TTY\n // / CI / NO_COLOR it errors cleanly via `requireInteractive` (exit 2).\n program\n .command('tui')\n .description('interactive Ink dashboard (host · phase · daemon + /command dispatch)')\n .action(async (...args: unknown[]) => {\n const g = actionGlobals(args);\n const opts = toCliOptions(g);\n // requireInteractive honors all the non-interactive conditions (no TTY,\n // --no-input, --json, CI, NO_COLOR) and fails exit 2 with a clear message.\n requireInteractive(opts, '`noir tui`');\n // LAZY load the Ink dashboard. The import path is computed at runtime\n // (not a string literal) so esbuild cannot statically follow it into the\n // tui graph during the bin build — that static analysis is what hoists\n // bin.ts's entry body (incl. the `isMainModule` realpath guard) into a\n // shared chunk, turning dist/bin.js into a facade and breaking a global\n // `noir` install under its symlink. The tui is built as a sibling entry\n // (see the second tsup config) so its react/ink dependency tree never\n // enters the main CLI graph; React stays out of `noir status` etc.\n const tuiUrl = new URL('./tui/index.js', import.meta.url).href;\n // React/Ink are external (resolved from node_modules at runtime), so the\n // react-reconciler DEV build is selected unless NODE_ENV is 'production'.\n // The dev build calls performance.measure() on every render, filling\n // Node's 1M-entry performance buffer and OOMing the TUI after a few\n // minutes (QwenLM PR #4462). Force production BEFORE the TUI loads.\n process.env.NODE_ENV ||= 'production';\n const { runTui } = await import(/* @vite-ignore */ tuiUrl);\n // Same dispatch seam as homeDeps.dispatch above — a fresh program per\n // dispatched argv, errors mapped to process.exitCode (never thrown out).\n const dispatch = async (argv: readonly string[]): Promise<void> => {\n const sub = createProgram();\n try {\n await sub.parseAsync([...argv], { from: 'user' });\n } catch (err) {\n handleError(err);\n }\n };\n await runTui(opts, dispatch);\n });\n\n // `noir palette` — the fuzzy command palette opened directly. Same lazy Ink\n // mount as `noir tui` but renders the App palette-first\n // (`{ kind: 'palette' }` initial mode), so the user can fuzzy-run any command\n // without first entering the dashboard. Interactive-only (requireInteractive →\n // exit 2 under non-TTY/--json/--no-input/CI/NO_COLOR), exactly like `noir tui`.\n program\n .command('palette')\n .description('fuzzy command palette — run any noir command (Ink)')\n .action(async (...args: unknown[]) => {\n const g = actionGlobals(args);\n const opts = toCliOptions(g);\n requireInteractive(opts, '`noir palette`');\n const tuiUrl = new URL('./tui/index.js', import.meta.url).href;\n process.env.NODE_ENV ||= 'production'; // see the `noir tui` action above\n const { runPalette } = await import(/* @vite-ignore */ tuiUrl);\n const dispatch = async (argv: readonly string[]): Promise<void> => {\n const sub = createProgram();\n try {\n await sub.parseAsync([...argv], { from: 'user' });\n } catch (err) {\n handleError(err);\n }\n };\n await runPalette(opts, dispatch);\n });\n\n // `noir run <prompt>` — drive the host CLI headless and render its\n // stream-json (the headless host-driving surface). Streams the host's output and\n // reports token/cost from the `result` event. `--command <binary>` overrides\n // the per-host default so users with multiple profiles (claude vs claude-work)\n // can point at their own binary. Scriptable under `--json`.\n program\n .command('run')\n .description('ask the host agent a question and print the answer')\n .argument('[prompt...]', 'prompt to send to the host')\n .addOption(new Option('--host <id>', 'host to drive (default claude)').choices(SUPPORTED_HOSTS))\n .option('--command <binary>', 'custom host binary (e.g. claude-work)')\n .option('--profile <name>', 'use a named run profile (run.profiles in .noir/config.yml)')\n .option('--list-profiles', 'list configured run profiles and exit')\n .action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n const g = cmd.optsWithGlobals();\n // A variadic `[prompt...]` argument arrives as a SINGLE array value in the\n // action's positional args, so read the parsed positional args off the\n // trailing Command (`cmd.args`) rather than filtering the raw `args`.\n const prompt = cmd.args.join(' ').trim();\n const opts: RunOptions = {\n ...toCliOptions(g),\n ...(typeof g.host === 'string' ? { host: g.host } : {}),\n ...(typeof g.command === 'string' ? { command: g.command } : {}),\n ...(typeof g.profile === 'string' ? { profile: g.profile } : {}),\n };\n if (g.listProfiles === true) {\n listProfilesCommand(opts);\n return;\n }\n await runHostCommand(prompt, opts);\n });\n\n /** `noir run --list-profiles`: show the configured run profiles (NAME / DEFAULT / BINARY).\n * Under --json the rows go to stdout as one {ok,data} envelope (the exit-code\n * contract —\n * the table/info helpers are silenced in json mode, so json is handled FIRST). */\n function listProfilesCommand(opts: CliOptions): void {\n const config = loadRunConfig(process.cwd());\n const rows = config ? listProfiles(config) : [];\n if (opts.json === true) {\n json({ ok: true, data: rows });\n return;\n }\n if (rows.length === 0) {\n info('(no run profiles configured — add a run.profiles block to .noir/config.yml)', opts);\n return;\n }\n table(rows, ['NAME', 'DEFAULT', 'BINARY'], opts);\n }\n\n program.action(async (...args: unknown[]) => {\n const cmd = trailingCmd(args);\n // Bare `noir` (no subcommand) → the home router. But a leftover positional\n // here is an UNKNOWN command: every registered subcommand consumes its own\n // leading arg via its own action, so anything still in `cmd.args` didn't\n // match. Reject it exit 3 (NOT_FOUND) per the exit-code contract instead of\n // silently routing the typo through home (which would exit 0).\n const leftovers = cmd.args;\n if (leftovers.length > 0) {\n fail(\n EXIT.NOT_FOUND,\n `unknown command '${leftovers[0]}' (no such subcommand). Run \\`noir --help\\` for the list.`,\n toCliOptions(cmd.optsWithGlobals()),\n );\n }\n await home(toCliOptions(actionGlobals(args)), homeDeps);\n });\n\n return program;\n}\n\n/** Singleton program used by the bin entry (`run`) and re-exported for convenience. */\nexport const program: Command = createProgram();\n\n// Home-menu deps, built ONCE at module scope. Defined\n// OUTSIDE createProgram so the palette commands are computed a single time and\n// never recurse (createProgram → homeDeps → createProgram → …). The bare-`noir`\n// action inside createProgram closes over this module-level const.\nconst homeDeps: HomeDeps = {\n dispatch: async (argv: readonly string[]): Promise<void> => {\n const sub = createProgram();\n try {\n await sub.parseAsync([...argv], { from: 'user' });\n } catch (err) {\n // Map the sub-command's failure to process.exitCode (never throw out\n // of dispatch — home returns and the outer program exit reflects this).\n handleError(err);\n }\n },\n // The grouped home menu resolves its sections against the LIVE palette\n // registry — the same source the TUI palette\n // uses — so the menu cannot drift from the commander tree.\n commands: buildPaletteCommands(createProgram()),\n};\n\n// ---------------------------------------------------------------------------\n// Error → exit-code mapping lives in `./output.js` (`handleError`); it never\n// throws and never calls `process.exit` (commander's `exitOverride` already\n// prevented that for commander's own errors).\n// ---------------------------------------------------------------------------\n\n/**\n * Parse `argv` (user-form: NO node/script prefix) on the singleton program and\n * return the resulting exit code. Sets `process.exitCode` for the real bin;\n * tests can either call this or drive `createProgram().parseAsync` directly.\n */\nexport async function run(argv: readonly string[] = []): Promise<number> {\n try {\n await program.parseAsync([...argv], { from: 'user' });\n } catch (err) {\n handleError(err);\n }\n return typeof process.exitCode === 'number' ? process.exitCode : EXIT.OK;\n}\n\n// Auto-invoke only when bin.ts is the entry point (real `noir` bin, or a tsx\n// subprocess as in gate1-stdio). When vitest imports bin.js as a module,\n// process.argv[1] is the runner, so this is skipped and the program stays idle.\nconst isMainModule = (() => {\n try {\n const entry = process.argv[1];\n if (typeof entry !== 'string' || entry.length === 0) return false;\n // Resolve symlinks: a global npm install invokes the bin via a symlink\n // (.../bin/noir -> .../lib/node_modules/@noir-ai/cli/dist/bin.js), so argv[1]\n // is the symlink path while import.meta.url is the RESOLVED real path. Compare\n // the REAL paths so `noir` runs under BOTH direct + symlinked invocation.\n // (Without this, a global `noir` install silently exits 0 — main() never runs.)\n return pathToFileURL(realpathSync(entry)).href === import.meta.url;\n } catch {\n return false;\n }\n})();\n\nif (isMainModule) {\n void run(process.argv.slice(2));\n}\n"],"mappings":"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","names":["envFlagSet","msg","c","data","paths","loadProjectInfo","readWorkspaceMarker","pidAlive","readWorkspaceDaemonRecord","ensured","loadProjectInfo","ensureDaemonRunning","pidAlive","readDaemonToken","readProjectDaemonRecord","errMsg","existsSync","readdirSync","readFileSync","join","resolveAdapter","loadProjectInfo","NOIR_VERSION","pidAlive","readProjectDaemonRecord","status","openStore","fail","c","scaffold","loadNoirEnv","loadProjectInfo","resolveModelConfig","env","report","join","atomicWriteFile","detectActiveMethod","loadProjectInfo","NOIR_VERSION","readInstallRecord","home","detected","detectActiveMethod","ensureShimExecutable","loadProjectInfo","readInstallRecord","readUpdateCache","runManagerCmd","semverLt","latest","loadProjectInfo","NOIR_VERSION","describeEmbedder","c","renderHuman","info","spawnSync","loadProjectInfo","s","status","c","mkdirSync","writeFileSync","join","resolveAdapter","loadProjectInfo","program","c","hostLaunchDirective","detectActiveMethod","latestVersionFromCache","loadProjectInfo","NOIR_VERSION","readInstallRecord","readUpdateCache","info","readFileSync","loadProjectInfo","parseLimit","toHit","failTool","probe","hits","data","res","sessions","env","spawnSync","status","env","PROBE_TIMEOUT_MS","resolve","c","spawn","error","resolve","signal","writeFileSync","CommanderError","stamp","constants","loadProjectInfo","env","existsSync","mkdirSync","writeFileSync","join","isDeniedEnvKey","loadNoirEnv","NOIR_DIR","paths","env","status","mkdirSync","readFileSync","writeFileSync","join","sha256Hex","c","resolve","existsSync","resolveAdapter","loadProjectInfo","paths","resolveAdapter","loadProjectInfo","emitSkillsToDir","status","data","loadProjectInfo","ensureDaemonRunning","resolve","resolveAdapter","loadProjectInfo","scaffold","emitSkillsToDir","existsSync","resolveAdapter","loadProjectInfo","paths","scaffold","emitSkillsToDir","SUPPORTED_HOSTS","applyNoirEnv","NOIR_VERSION","c","program","str","create","sync","install","update","run"]}
|