@noir-ai/cli 1.3.0-beta.2 → 1.3.0-beta.4

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/dist/bin.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  doctor,
4
4
  serve
5
- } from "./chunk-J5HAAQI4.js";
5
+ } from "./chunk-3QMQXBII.js";
6
6
  import {
7
7
  init
8
8
  } from "./chunk-J2Y3BOVK.js";
@@ -317,7 +317,16 @@ function checkHostArtifacts(checks, root, project) {
317
317
  return { active: host, expected, missing };
318
318
  }
319
319
  function checkNestedNoir(checks, root) {
320
- const candidates = [".noir/.noir", ".noir/CLAUDE.md", ".noir/.mcp.json", ".noir/.claude"];
320
+ const candidates = [
321
+ ".noir/.noir",
322
+ ".noir/CLAUDE.md",
323
+ ".noir/.mcp.json",
324
+ ".noir/.claude",
325
+ ".noir/.gitignore",
326
+ ".noir/.dockerignore",
327
+ ".noir/.npmignore",
328
+ ".noir/.prettierignore"
329
+ ];
321
330
  const found = candidates.filter((rel) => existsSync(join(root, rel)));
322
331
  const detected = found.length > 0;
323
332
  checks.push({
@@ -622,4 +631,4 @@ export {
622
631
  doctor,
623
632
  serve
624
633
  };
625
- //# sourceMappingURL=chunk-J5HAAQI4.js.map
634
+ //# sourceMappingURL=chunk-3QMQXBII.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/doctor.ts","../src/serve.ts"],"sourcesContent":["// S9 t6 — `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 / t6):\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/DS-6). 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 DS-4): `--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 } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, NOIR_VERSION, type ProjectInfo, paths } from '@noir-ai/core';\nimport { CURRENT_SCAFFOLD_VERSION, readScaffoldVersion } from '@noir-ai/create';\nimport { pidAlive, readDaemonRecord } from '@noir-ai/daemon';\nimport { resolveModelConfig } from '@noir-ai/model';\nimport {\n type CliOptions,\n EXIT,\n error as err,\n log,\n NoirCliError,\n success,\n table,\n warn,\n} from '../output.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 (R5). `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}\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[]): Promise<void> {\n const rec = readDaemonRecord();\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 const res = await fetch(`http://127.0.0.1:${rec.port}/health`);\n const body =\n res.status === 200 ? ((await res.json()) as { ok?: boolean; uptimeSec?: number }) : null;\n if (body && body.ok === true) {\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\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(checks: CheckResult[], project: ProjectInfo | undefined): 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 const key = p.hasKey ? 'key present' : p.apiKeyEnv ? `missing ${p.apiKeyEnv}` : '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\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 (R5). 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 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: c.status.toUpperCase(),\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);\n const { vecOk } = await checkNativeDeps(checks);\n await checkStore(checks, project, root);\n checkEmbedder(checks, project, vecOk);\n checkProvider(checks, project);\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 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 };\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","import { loadProjectInfo } from '@noir-ai/core';\nimport { ensureDaemonRunning, startStdioServer } from '@noir-ai/daemon';\n\nexport async function serve(opts: { stdio: boolean }): Promise<void> {\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 });\n process.stderr.write(\n `Noir daemon available at ${url}. (For HTTP clients, use this URL in .mcp.json.)\\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"],"mappings":"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","names":["loadProjectInfo"]}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  doctor,
4
4
  serve
5
- } from "./chunk-J5HAAQI4.js";
5
+ } from "./chunk-3QMQXBII.js";
6
6
  import {
7
7
  init
8
8
  } from "./chunk-J2Y3BOVK.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noir-ai/cli",
3
- "version": "1.3.0-beta.2",
3
+ "version": "1.3.0-beta.4",
4
4
  "description": "Noir CLI — the `noir` command tree (commander + @clack/prompts), the shell entry point to the Noir AI toolkit.",
5
5
  "license": "MIT",
6
6
  "author": "agaaaptr",
@@ -52,14 +52,14 @@
52
52
  "commander": "^12.1.0",
53
53
  "ora": "^8.1.0",
54
54
  "picocolors": "^1.1.1",
55
- "@noir-ai/adapters": "1.3.0-beta.2",
56
- "@noir-ai/context": "1.3.0-beta.2",
57
- "@noir-ai/create": "1.3.0-beta.2",
58
- "@noir-ai/core": "1.3.0-beta.2",
59
- "@noir-ai/model": "1.3.0-beta.2",
60
- "@noir-ai/skills": "1.3.0-beta.2",
61
- "@noir-ai/store": "1.3.0-beta.2",
62
- "@noir-ai/daemon": "1.3.0-beta.2"
55
+ "@noir-ai/adapters": "1.3.0-beta.4",
56
+ "@noir-ai/core": "1.3.0-beta.4",
57
+ "@noir-ai/create": "1.3.0-beta.4",
58
+ "@noir-ai/skills": "1.3.0-beta.4",
59
+ "@noir-ai/daemon": "1.3.0-beta.4",
60
+ "@noir-ai/model": "1.3.0-beta.4",
61
+ "@noir-ai/context": "1.3.0-beta.4",
62
+ "@noir-ai/store": "1.3.0-beta.4"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/node": "^26.1.1"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/doctor.ts","../src/serve.ts"],"sourcesContent":["// S9 t6 — `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 / t6):\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/DS-6). 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 DS-4): `--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 } from 'node:fs';\nimport { dirname, join, relative } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { type HostId, resolveAdapter } from '@noir-ai/adapters';\nimport { loadProjectInfo, NOIR_VERSION, type ProjectInfo, paths } from '@noir-ai/core';\nimport { CURRENT_SCAFFOLD_VERSION, readScaffoldVersion } from '@noir-ai/create';\nimport { pidAlive, readDaemonRecord } from '@noir-ai/daemon';\nimport { resolveModelConfig } from '@noir-ai/model';\nimport {\n type CliOptions,\n EXIT,\n error as err,\n log,\n NoirCliError,\n success,\n table,\n warn,\n} from '../output.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 (R5). `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}\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[]): Promise<void> {\n const rec = readDaemonRecord();\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 const res = await fetch(`http://127.0.0.1:${rec.port}/health`);\n const body =\n res.status === 200 ? ((await res.json()) as { ok?: boolean; uptimeSec?: number }) : null;\n if (body && body.ok === true) {\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\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(checks: CheckResult[], project: ProjectInfo | undefined): 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 const key = p.hasKey ? 'key present' : p.apiKeyEnv ? `missing ${p.apiKeyEnv}` : '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\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 (R5). 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/`). 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 = ['.noir/.noir', '.noir/CLAUDE.md', '.noir/.mcp.json', '.noir/.claude'];\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 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: c.status.toUpperCase(),\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);\n const { vecOk } = await checkNativeDeps(checks);\n await checkStore(checks, project, root);\n checkEmbedder(checks, project, vecOk);\n checkProvider(checks, project);\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 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 };\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","import { loadProjectInfo } from '@noir-ai/core';\nimport { ensureDaemonRunning, startStdioServer } from '@noir-ai/daemon';\n\nexport async function serve(opts: { stdio: boolean }): Promise<void> {\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 });\n process.stderr.write(\n `Noir daemon available at ${url}. (For HTTP clients, use this URL in .mcp.json.)\\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"],"mappings":"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","names":["loadProjectInfo"]}