@agent-surface/cli 0.13.0 → 0.15.0

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.
Files changed (38) hide show
  1. package/dist/bin.js +41 -14
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{check-TJIX7NDE.js → check-VWRYNYLN.js} +66 -48
  4. package/dist/check-VWRYNYLN.js.map +1 -0
  5. package/dist/chunk-A5UCBF7D.js +246 -0
  6. package/dist/chunk-A5UCBF7D.js.map +1 -0
  7. package/dist/chunk-GYYWHZPM.js +532 -0
  8. package/dist/chunk-GYYWHZPM.js.map +1 -0
  9. package/dist/chunk-NFK3XWWH.js +1475 -0
  10. package/dist/chunk-NFK3XWWH.js.map +1 -0
  11. package/dist/chunk-Y2LSPEVK.js +61 -0
  12. package/dist/chunk-Y2LSPEVK.js.map +1 -0
  13. package/dist/index.d.ts +3 -2
  14. package/dist/{init-6XV64LK2.js → init-BVZR6CRS.js} +62 -40
  15. package/dist/init-BVZR6CRS.js.map +1 -0
  16. package/dist/{ink-QR7X7TAC.js → ink-ZCQ26EY4.js} +71 -17
  17. package/dist/ink-ZCQ26EY4.js.map +1 -0
  18. package/dist/{inspect-NJ4J3MPP.js → inspect-3YFPCYQJ.js} +88 -104
  19. package/dist/inspect-3YFPCYQJ.js.map +1 -0
  20. package/dist/snapshot-5QKLZKZI.js +130 -0
  21. package/dist/snapshot-5QKLZKZI.js.map +1 -0
  22. package/package.json +4 -4
  23. package/dist/check-TJIX7NDE.js.map +0 -1
  24. package/dist/chunk-3AJ343NA.js +0 -178
  25. package/dist/chunk-3AJ343NA.js.map +0 -1
  26. package/dist/chunk-IALBMW3R.js +0 -278
  27. package/dist/chunk-IALBMW3R.js.map +0 -1
  28. package/dist/chunk-L7GHSC2Z.js +0 -465
  29. package/dist/chunk-L7GHSC2Z.js.map +0 -1
  30. package/dist/chunk-UGCLJ5JX.js +0 -724
  31. package/dist/chunk-UGCLJ5JX.js.map +0 -1
  32. package/dist/chunk-VX6GBEP3.js +0 -539
  33. package/dist/chunk-VX6GBEP3.js.map +0 -1
  34. package/dist/init-6XV64LK2.js.map +0 -1
  35. package/dist/ink-QR7X7TAC.js.map +0 -1
  36. package/dist/inspect-NJ4J3MPP.js.map +0 -1
  37. package/dist/snapshot-ZGTAF2Y5.js +0 -77
  38. package/dist/snapshot-ZGTAF2Y5.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/render/model.ts","../src/coverage.ts","../src/extract.ts","../src/render/summary.ts"],"sourcesContent":["import type {\n AgentActionDescriptor,\n AgentObservationDescriptor,\n AgentProcedureDescriptor,\n AgentSurfaceSnapshot,\n} from \"@agent-surface/core\";\nimport type { CapabilityExplanation, SurfaceExplanation } from \"@agent-surface/core/explain\";\nimport type { CollectResult, RegistrationRejection } from \"../collect.js\";\n\n/**\n * One view model, two renderers. The Ink UI and the plain-text fallback both\n * consume this, so `--plain` can never drift into showing something different\n * from what a TTY shows.\n */\nexport interface CapabilityRow {\n capabilityId: string;\n /** Leaf name — the group heading already carries the rest of the id. */\n name: string;\n /**\n * The id minus its plane prefix. The table is flat, so its first column has\n * to carry the whole path; the grouped detail view uses `name`.\n */\n path: string;\n kind: \"observation\" | \"action\" | \"procedure\";\n plane: \"view\" | \"domain\";\n outcome: \"expose\" | \"disable\" | \"hide\";\n description: string;\n reason?: string;\n /** The effect, alone, for the table's own column. Observations have none. */\n effect?: string;\n /** What is left of `tags` once the effect has its own column. */\n flags: string[];\n /** Effect and flags together — what the grouped detail view prints. */\n tags: string[];\n policies?: CapabilityExplanation[\"policies\"];\n availability?: CapabilityExplanation[\"availability\"];\n schemas?: { input?: unknown; output?: unknown };\n}\n\nexport interface CapabilityGroup {\n heading: string;\n rows: CapabilityRow[];\n}\n\nexport interface SurfaceView {\n scenario: string;\n route?: string;\n /**\n * The scope the counts below were computed under (`AS-CLI-007`). A scope\n * filters the snapshot *and* the explanation, so without it on screen the\n * header reads as a statement about the whole surface when it is a statement\n * about one prefix of it.\n */\n scope?: string[];\n groups: CapabilityGroup[];\n counts: { callable: number; disabled: number; hidden: number };\n /** Refused during the mount — absent from both projections (`AS-CLI-006`). */\n rejections: RegistrationRejection[];\n explained: boolean;\n}\n\nexport interface ViewOptions {\n explain?: boolean;\n schemas?: boolean;\n}\n\n/**\n * The table is flat — `groups` exist for the grouped detail view, which is what\n * `--detail`, `--explain` and `--schemas` render. Flattening here rather than in\n * each renderer keeps the two views over one order.\n */\nexport function flatRows(view: SurfaceView): CapabilityRow[] {\n return view.groups.flatMap((group) => group.rows);\n}\n\nfunction pathOf(capabilityId: string): string {\n return capabilityId.replace(/^(view|domain):/, \"\");\n}\n\nfunction leafOf(capabilityId: string): string {\n const withoutPlane = pathOf(capabilityId);\n const dot = withoutPlane.lastIndexOf(\".\");\n return dot === -1 ? withoutPlane : withoutPlane.slice(dot + 1);\n}\n\n/**\n * The effect gets its own table column; everything else is a flag. An\n * observation reads state and has no effect at all, which the table shows as\n * an em dash rather than inventing one.\n */\nfunction actionFlags(action: AgentActionDescriptor): string[] {\n const flags: string[] = [];\n if (action.idempotent) flags.push(\"idempotent\");\n if (action.reversible) flags.push(\"reversible\");\n if (action.confirmation !== \"never\") flags.push(`confirmation:${action.confirmation}`);\n return flags;\n}\n\nfunction procedureFlags(procedure: AgentProcedureDescriptor): string[] {\n const flags: string[] = [];\n if (procedure.confirmation !== \"never\") flags.push(`confirmation:${procedure.confirmation}`);\n for (const field of procedure.boundFields) {\n flags.push(`${field.path} bound${field.locked ? \"+locked\" : \"\"}`);\n }\n return flags;\n}\n\nfunction explanationIndex(explanation: SurfaceExplanation): Map<string, CapabilityExplanation> {\n const index = new Map<string, CapabilityExplanation>();\n for (const capability of explanation.capabilities) {\n // Keyed by id + registration so two instances of one component stay apart.\n index.set(`${capability.capabilityId}\\u0000${capability.registrationId}`, capability);\n }\n return index;\n}\n\nexport function buildView(result: CollectResult, options: ViewOptions = {}): SurfaceView {\n const { snapshot, explanation } = result;\n const index = explanationIndex(explanation);\n const groups: CapabilityGroup[] = [];\n const counts = { callable: 0, disabled: 0, hidden: 0 };\n\n const enrich = (\n row: CapabilityRow,\n capabilityId: string,\n registrationId: string,\n ): CapabilityRow => {\n const explained = index.get(`${capabilityId}\\u0000${registrationId}`);\n if (options.explain && explained) {\n row.policies = explained.policies;\n row.availability = explained.availability;\n }\n return row;\n };\n\n for (const component of snapshot.components) {\n const rows: CapabilityRow[] = [];\n\n for (const observation of component.observations) {\n rows.push(\n enrich(\n rowFor(observation, \"observation\", undefined, [], options, {\n input: undefined,\n output: observation.outputSchema,\n }),\n observation.capabilityId,\n component.registrationId,\n ),\n );\n }\n for (const action of component.actions) {\n rows.push(\n enrich(\n rowFor(action, \"action\", action.effect, actionFlags(action), options, {\n input: action.inputSchema,\n output: action.outputSchema,\n }),\n action.capabilityId,\n component.registrationId,\n ),\n );\n }\n\n groups.push({\n heading:\n component.instanceId === \"default\"\n ? component.type\n : `${component.type}@${component.instanceId}`,\n rows,\n });\n }\n\n if (snapshot.procedures.length > 0) {\n groups.push({\n heading: \"authoritative (domain)\",\n rows: snapshot.procedures.map((procedure) =>\n enrich(\n {\n capabilityId: procedure.procedureId,\n name: procedure.procedureId.replace(/^domain:/, \"\"),\n path: pathOf(procedure.procedureId),\n kind: \"procedure\",\n plane: \"domain\",\n outcome: procedure.available ? \"expose\" : \"disable\",\n description: procedure.description,\n ...(procedure.unavailableReason ? { reason: procedure.unavailableReason } : {}),\n effect: procedure.effect,\n flags: procedureFlags(procedure),\n tags: [procedure.effect, ...procedureFlags(procedure)],\n ...(options.schemas\n ? { schemas: { input: procedure.inputSchema, output: procedure.outputSchema } }\n : {}),\n },\n procedure.procedureId,\n procedure.registrationId,\n ),\n ),\n });\n }\n\n // Hidden capabilities exist only in the explanation — that is the whole point\n // of it. They get their own group so nobody mistakes them for callable.\n //\n // Unconditional, not behind `--explain`, for the reason `AS-CLI-007` moved\n // the hidden *count* out from behind it: signed out, the example app rendered\n // `0 callable, 0 visible-disabled` over eleven perfectly good capabilities\n // that authority had hidden, and a reader who did not know to re-run with a\n // flag read that as an app which annotated nothing. The explanation is\n // collected on every run regardless, so this costs nothing. The policy\n // *attribution* still needs `--explain`; only the rows moved.\n const hidden = explanation.capabilities.filter((c) => c.outcome === \"hide\");\n if (hidden.length > 0) {\n groups.push({\n heading: \"hidden by policy (absent from the snapshot)\",\n rows: hidden.map((capability) => ({\n capabilityId: capability.capabilityId,\n name: leafOf(capability.capabilityId),\n path: pathOf(capability.capabilityId),\n kind: capability.kind,\n plane: capability.plane,\n outcome: \"hide\" as const,\n description: capability.description,\n // No reason line, deliberately. The reason a hidden capability carries\n // is its *availability* reason — \"The drawer is not open\" — and printing\n // that under a row marked `hidden` says the UI declined when authority\n // did. Authority hides, state discloses (D11/D12), and the two must\n // never look alike. Why it was hidden is a policy question, which is\n // what `--explain` answers.\n //\n // A hidden capability has no snapshot entry, so there is no effect to\n // report — the table prints an em dash rather than inventing one. The\n // capability path already carries the component type; only a non-default\n // instance adds anything.\n flags:\n capability.component.instanceId === \"default\"\n ? []\n : [`@${capability.component.instanceId}`],\n tags: [`${capability.component.type}@${capability.component.instanceId}`],\n ...(options.explain\n ? { policies: capability.policies, availability: capability.availability }\n : {}),\n })),\n });\n }\n\n for (const capability of explanation.capabilities) {\n if (capability.outcome === \"expose\") counts.callable += 1;\n else if (capability.outcome === \"disable\") counts.disabled += 1;\n else counts.hidden += 1;\n }\n\n return {\n scenario: result.scenario,\n ...(snapshot.route?.path ? { route: snapshot.route.path } : {}),\n ...(result.scope ? { scope: result.scope } : {}),\n groups,\n counts,\n rejections: result.rejections ?? [],\n explained: options.explain === true,\n };\n}\n\nfunction rowFor(\n descriptor: AgentObservationDescriptor | AgentActionDescriptor,\n kind: \"observation\" | \"action\",\n effect: string | undefined,\n flags: string[],\n options: ViewOptions,\n schemas: { input?: unknown; output?: unknown },\n): CapabilityRow {\n return {\n capabilityId: descriptor.capabilityId,\n name: descriptor.name,\n path: pathOf(descriptor.capabilityId),\n kind,\n plane: \"view\",\n outcome: descriptor.available ? \"expose\" : \"disable\",\n description: descriptor.description,\n ...(descriptor.unavailableReason ? { reason: descriptor.unavailableReason } : {}),\n ...(effect ? { effect } : {}),\n flags,\n // The grouped detail view prints one combined list, the way it always has.\n tags: effect ? [effect, ...flags] : [kind, ...flags],\n ...(options.schemas ? { schemas } : {}),\n };\n}\n","/**\n * `coverage` — authored minus reached (`AS-COVER-004…005`, D36).\n *\n * The inventory says what the codebase authors; the scenarios say what a mount\n * surfaces. Neither half alone answers \"which authored capability does no\n * scenario reach\", because that is a set difference no command computed.\n */\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { AuthoredCapability } from \"./extract.js\";\n\nexport const ALLOWLIST_FILE = \"coverage-allow.json\";\nexport const UNREAD_ALLOWLIST_FILE = \"unresolved-allow.json\";\n\n/**\n * A committed list of unreached capabilities a repository has decided not to\n * fix yet, each with a reason. Adoption has to ratchet rather than gate: a\n * codebase turning this on with 200 unreached capabilities cannot fix them in\n * one pull request, and a check that can only be adopted big-bang is a check\n * that never gets adopted.\n */\nexport type CoverageAllowlist = Record<string, string>;\n\nexport function allowlistPathFor(baselineDir: string): string {\n return join(baselineDir, ALLOWLIST_FILE);\n}\n\nexport function unreadAllowlistPathFor(baselineDir: string): string {\n return join(baselineDir, UNREAD_ALLOWLIST_FILE);\n}\n\nexport function readAllowlist(path: string, keyName = \"capabilityId\"): CoverageAllowlist {\n if (!existsSync(path)) return {};\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, \"utf8\"));\n } catch (error) {\n throw new Error(\n `could not parse ${path}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n throw new Error(`${path} must be a JSON object of { \"${keyName}\": \"reason\" }`);\n }\n const allowlist: CoverageAllowlist = {};\n for (const [id, reason] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof reason !== \"string\" || reason.trim() === \"\") {\n throw new Error(`${path}: \"${id}\" needs a non-empty reason string`);\n }\n allowlist[id] = reason;\n }\n return allowlist;\n}\n\n/**\n * The key an unread call site is allowlisted under: `file#reason#site`.\n *\n * Neither half alone works. The line number churns on every edit above the call\n * site, so a ratchet keyed on it fails for a reason that has nothing to do with\n * the surface. The `note` is prose written for a human and gets reworded — the\n * spread note changed in the release that introduced it — so a key built from it\n * would invalidate committed entries on an edit nobody thought was behavioural.\n *\n * The site fingerprint is built from the call's own text and the named\n * enclosures around it, and from nothing positional — so an edit above it, or\n * beside it, or a reformat, leaves a committed entry matching, while a second\n * site in the same file receives its own key. See `stableSite`.\n */\nexport function unreadKey(entry: AuthoredCapability): string {\n return `${entry.origin.file}#${entry.reason ?? \"unknown\"}#${entry.origin.site}`;\n}\n\nexport interface UnreachedCapability {\n capabilityId: string;\n origin: { file: string; line: number };\n}\n\nexport interface CoverageReport {\n /** Distinct capability ids the inventory resolved, within any active scope. */\n authored: number;\n /** How many of them at least one scenario surfaced. */\n reached: number;\n scenarios: string[];\n /**\n * The scope every number here was computed under (`AS-CLI-007`). A scope\n * filters the catalog *and* the mount, so `10 authored` without it on screen\n * reads as a claim about the whole codebase when it is a claim about one\n * prefix of it.\n */\n scope?: string[];\n /**\n * Allowlist entries outside the active scope, which a scoped run cannot\n * judge: not unreached (nothing looked), not stale (nothing reached them).\n * Counted rather than silently dropped, so a scoped run never reads as a\n * verdict on the whole allowlist.\n */\n allowlistOutOfScope: number;\n /** Authored, surfaced by no scenario, and not allowlisted — the finding. */\n unreached: UnreachedCapability[];\n /**\n * Present at runtime with no static origin: a dynamic registration, or a gap\n * in the extractor. `view:` only — see `domainReached`.\n */\n undeclared: string[];\n /**\n * `domain:` capabilities a scenario surfaced. Held apart from `undeclared`\n * because the inventory never claimed to analyze that plane: filing them as\n * \"no static origin\" would report the design's own stated boundary as a\n * defect, which is the misleading check this whole command rejects.\n */\n domainReached: string[];\n /** Runtime domain entries absent from an explicitly configured manifest. */\n unmanifestedDomain: string[];\n domainAuthoritative: boolean;\n /** Carried forward from the inventory, minus anything allowlisted. */\n unresolved: AuthoredCapability[];\n /** Unreached, but listed in the allowlist. */\n allowed: string[];\n /** Listed in the allowlist and reached anyway — the list has rotted. */\n staleAllowlist: string[];\n allowlistPath: string;\n /** Unread, but listed in `unresolved-allow.json`. Keys, not entries. */\n allowedUnread: string[];\n /** Listed there and no longer unread — that list has rotted too. */\n staleUnreadAllowlist: string[];\n unreadAllowlistPath: string;\n}\n\nexport interface BuildCoverageInput {\n authored: Set<string>;\n /** First origin seen for each authored id, for the report. */\n origins: Map<string, { file: string; line: number }>;\n /**\n * Every capability id any scenario's *explanation* held.\n *\n * The explanation, not the snapshot. A capability a policy hid **was**\n * reached: a scenario mounted it and the policy made a deliberate decision\n * about it. Classifying those as unreached would flood the report with the\n * library's own correct behaviour — in the example app the `anonymous`\n * scenario alone would contribute eleven false gaps.\n */\n reachedIds: Set<string>;\n scenarios: string[];\n scope?: string[];\n unresolved: AuthoredCapability[];\n /**\n * Already filtered to the active scope by the caller, which owns the scope\n * predicate. Entries outside it are counted in `allowlistOutOfScope`.\n */\n allowlist: CoverageAllowlist;\n allowlistOutOfScope?: number;\n allowlistPath: string;\n /** Keyed by `unreadKey()`. Absent is an empty list, not \"accept everything\". */\n unreadAllowlist?: CoverageAllowlist;\n unreadAllowlistPath: string;\n domainAuthoritative?: boolean;\n}\n\nexport function buildCoverageReport(input: BuildCoverageInput): CoverageReport {\n const unreached: UnreachedCapability[] = [];\n const allowed: string[] = [];\n\n for (const id of [...input.authored].sort()) {\n if (input.reachedIds.has(id)) continue;\n if (id in input.allowlist) {\n allowed.push(id);\n continue;\n }\n unreached.push({ capabilityId: id, origin: input.origins.get(id) ?? { file: \"?\", line: 0 } });\n }\n\n // An allowlist entry that is no longer unreached fails the command, so the\n // list shrinks and cannot silently rot — the same idiom as the baselines\n // `check` already commits.\n const staleAllowlist = Object.keys(input.allowlist)\n .filter((id) => input.reachedIds.has(id) || !input.authored.has(id))\n .sort();\n\n // The same ratchet, one bucket over. An unread call site a repository has\n // decided to live with — a shared wrapper hook, say — stops failing `check`\n // without turning the whole bucket off, and an entry that stops being unread\n // fails so the list shrinks.\n const unreadAllowlist = input.unreadAllowlist ?? {};\n const unread: AuthoredCapability[] = [];\n const allowedUnread = new Set<string>();\n for (const entry of input.unresolved) {\n const key = unreadKey(entry);\n if (key in unreadAllowlist) allowedUnread.add(key);\n else unread.push(entry);\n }\n const stillUnread = new Set(input.unresolved.map(unreadKey));\n const staleUnreadAllowlist = Object.keys(unreadAllowlist)\n .filter((key) => !stillUnread.has(key))\n .sort();\n\n const unaccounted = [...input.reachedIds].filter((id) => !input.authored.has(id)).sort();\n const domainReached = [...input.reachedIds].filter((id) => id.startsWith(\"domain:\")).sort();\n const unmanifestedDomain = input.domainAuthoritative\n ? unaccounted.filter((id) => id.startsWith(\"domain:\"))\n : [];\n const undeclared = unaccounted.filter((id) => !id.startsWith(\"domain:\"));\n\n return {\n authored: input.authored.size,\n reached: [...input.authored].filter((id) => input.reachedIds.has(id)).length,\n scenarios: input.scenarios,\n ...(input.scope ? { scope: input.scope } : {}),\n allowlistOutOfScope: input.allowlistOutOfScope ?? 0,\n unreached,\n undeclared,\n domainReached,\n unmanifestedDomain,\n domainAuthoritative: input.domainAuthoritative === true,\n unresolved: unread,\n allowed,\n staleAllowlist,\n allowlistPath: input.allowlistPath,\n allowedUnread: [...allowedUnread].sort(),\n staleUnreadAllowlist,\n unreadAllowlistPath: input.unreadAllowlistPath,\n };\n}\n\n/**\n * `0` clean, `1` a gap.\n *\n * `undeclared` deliberately does not fail (OQ-4): a dynamically registered\n * capability is legitimate, and from the outside it is indistinguishable from\n * an extractor that missed something. Failing on it would punish the honest\n * case to catch the other one. It is reported, loudly, and revisited when a\n * codebase does it deliberately.\n *\n * `unresolved` does fail, and `--allow-unresolved` is the only way past it\n * (`AS-COVER-003`). A partial understanding of a codebase that reports itself\n * as complete is the failure the whole static half exists to remove: `unreached`\n * is computed against the catalog, so a catalog with holes in it makes that\n * count a floor rather than an answer. Accepting the gap still prints it.\n */\nexport function coverageExitCode(\n report: CoverageReport,\n options: { allowUnresolved?: boolean } = {},\n): number {\n if (report.unreached.length > 0) return 1;\n if (report.unmanifestedDomain.length > 0) return 1;\n // `report.unresolved` already excludes allowlisted entries, so the per-entry\n // ratchet and the blanket flag compose: the list holds the sites you have\n // accepted, and the flag is still there for a codebase not ready to enumerate\n // them. Both stale lists fail regardless of either — a ratchet that can rot\n // is a ratchet that stops meaning anything.\n if (report.unresolved.length > 0 && !options.allowUnresolved) return 1;\n if (report.staleAllowlist.length > 0) return 1;\n if (report.staleUnreadAllowlist.length > 0) return 1;\n return 0;\n}\n","/**\n * The static capability inventory (`AS-COVER-001…003`, D35).\n *\n * `inspect` answers *what can an agent do on this page right now*. It cannot\n * answer *did we author something no scenario ever reaches*, because a surface\n * is a projection of what is mounted: a route no scenario visits registers\n * nothing, so there is nothing to report and nothing to diff. The denominator\n * has to come from somewhere that does not require mounting.\n *\n * It comes from here. A registration call site is far more static than the\n * surface it produces:\n *\n * ```tsx\n * useAgentComponent({\n * type: \"devices.table\", // string literal\n * actions: { sort: action({ … }) }, // capability name is a key\n * });\n * ```\n *\n * `view:devices.table.sort` is fully determined by source text. What is\n * genuinely dynamic — availability, policy outcome, binding — is the\n * *projection*, and none of it is claimed here.\n *\n * ## This creates no exposure path (directive §2.1)\n *\n * No DOM is scanned, nothing is registered, no annotation is suggested. This\n * module *reads the same reviewed registration code* a human reads and counts\n * what is already there. It lives in `@agent-surface/cli` — which no adapter\n * imports and no application ships — and must never be re-exported from\n * `@agent-surface/core`, mirroring `AS-EXPLAIN-004` (`AS-COVER-006`).\n *\n * ## Failure discipline is the substance, not a detail\n *\n * > Better a missing check than a misleading check.\n *\n * A call site this module cannot understand is **reported with its file and\n * line**, never dropped. An inventory that silently omitted the constructs it\n * failed to parse would understate the denominator, and a coverage number built\n * on it would claim completeness it never had.\n */\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport ts from \"typescript\";\n\n/** Identity could not be recovered from the call site at all. */\nexport const UNRESOLVED_ID = \"<unresolved>\";\n\nexport interface AuthoredCapability {\n /** Canonical id, instance-independent: `view:devices.table.sort`. */\n capabilityId: string;\n kind: \"observation\" | \"action\" | \"procedure\";\n /** Where a human can go and read it. */\n origin: { file: string; line: number; site: string };\n /** Literals recovered from the call site; absent when not statically known. */\n description?: string;\n effect?: string;\n /**\n * How much of this call site the extractor understood.\n *\n * `static` — identity and metadata both recovered from literals.\n * `partial` — identity resolved, some metadata or runtime presence dynamic.\n * The common case: a spread `instanceId`, a conditional capability, or a\n * description built from a template.\n * `unresolved` — identity NOT resolved. Reported, never dropped.\n */\n resolution: \"static\" | \"partial\" | \"unresolved\";\n /** Present on `partial`/`unresolved`: what defeated the extractor. */\n note?: string;\n /**\n * Present on `unresolved`: *which* construct defeated the extractor, as a\n * stable code rather than prose.\n *\n * `note` is written for a human and gets reworded — the spread note changed\n * in the same release that introduced it. Anything keyed on that prose would\n * silently invalidate itself on an edit no one thought was behavioural, which\n * is exactly what `unresolved-allow.json` must not do. This is the key.\n */\n reason?: UnreadReason;\n}\n\n/**\n * Why a registration could not be fully read. Stable identifiers: adding one is\n * fine, renaming one invalidates committed allowlists and is a breaking change.\n */\nexport type UnreadReason =\n | \"dynamic-type\"\n | \"dynamic-config\"\n | \"dynamic-group\"\n | \"dynamic-callee\"\n | \"spread-members\"\n | \"computed-name\"\n | \"granular-hook\";\n\nexport interface CapabilityInventory {\n capabilities: AuthoredCapability[];\n /** Absolute path to the tsconfig whose file list was analyzed. */\n tsconfig: string;\n /** Directory the analysis was rooted at — the surface config's own. */\n root: string;\n /** Files the program actually walked — the inventory's blast radius. */\n filesAnalyzed: number;\n /**\n * Agent-surface implementation files excluded outside `root`. First-party\n * workspace sources are analyzed; the implementation behind the authored\n * hooks is not a second app registration.\n */\n filesOutsideRoot: number;\n /**\n * The `domain:` plane is deliberately *not* analyzed here. Those capabilities\n * come from the oRPC router, which is already a static export (OQ-1), and\n * reporting zero of them would read as \"there are none\" rather than \"nobody\n * looked\".\n */\n domain: \"not-analyzed\";\n}\n\n/* ── locating the program ─────────────────────────────────────────────── */\n\nexport function findTsconfig(from: string): string | undefined {\n return ts.findConfigFile(resolve(from), ts.sys.fileExists, \"tsconfig.json\");\n}\n\n/** Reads a literal config `scope: [\"...\"]` without executing or mounting it. */\nexport function readLiteralConfigScope(configPath: string): string[] | undefined {\n const source = ts.createSourceFile(\n configPath,\n readFileSync(configPath, \"utf8\"),\n ts.ScriptTarget.Latest,\n true,\n configPath.endsWith(\"x\") ? ts.ScriptKind.TSX : ts.ScriptKind.TS,\n );\n let scope: string[] | undefined;\n const visit = (node: ts.Node): void => {\n if (scope) return;\n if (\n ts.isPropertyAssignment(node) &&\n propertyName(node.name) === \"scope\" &&\n ts.isArrayLiteralExpression(node.initializer)\n ) {\n const values = node.initializer.elements.map((entry) => literalText(entry));\n if (values.every((value): value is string => value !== undefined)) scope = values;\n }\n ts.forEachChild(node, visit);\n };\n visit(source);\n return scope;\n}\n\ninterface ProgramFiles {\n fileNames: string[];\n options: ts.CompilerOptions;\n}\n\nfunction readProgramFiles(tsconfigPath: string): ProgramFiles {\n const read = ts.readConfigFile(tsconfigPath, ts.sys.readFile);\n if (read.error) {\n throw new Error(\n `could not read ${tsconfigPath}: ${ts.flattenDiagnosticMessageText(read.error.messageText, \" \")}`,\n );\n }\n const parsed = ts.parseJsonConfigFileContent(\n read.config as object,\n ts.sys,\n dirname(tsconfigPath),\n );\n if (parsed.errors.length > 0 && parsed.fileNames.length === 0) {\n throw new Error(\n `could not resolve any files from ${tsconfigPath}: ${parsed.errors\n .map((error) => ts.flattenDiagnosticMessageText(error.messageText, \" \"))\n .join(\"; \")}`,\n );\n }\n return { fileNames: parsed.fileNames, options: parsed.options };\n}\n\n/* ── which local name is a registration API ───────────────────────────── */\n\n/**\n * The hooks a registration is written with, under the names *this library\n * exports them as* — not the names a codebase happens to call them by.\n *\n * `register` is deliberately absent. It is a method on a registry instance\n * rather than an import, so there is no binding to read; it stays matched by\n * name, with the extra `type` check `visitCall` already applies to it.\n */\nconst REGISTRATION_HOOKS = new Set([\"useAgentComponent\", \"useAgentAction\", \"useAgentObservation\"]);\n\n/** `@agent-surface/react`, `@agent-surface/core`, and any subpath of either. */\nfunction isRegistrationModule(specifier: string): boolean {\n return specifier.startsWith(\"@agent-surface/\");\n}\n\n/**\n * What a file's own import declarations say about which local names are ours.\n *\n * A registration is identified by *what was imported*, not by what this file\n * calls it. Matching the local identifier alone lost a whole registration to a\n * rename:\n *\n * ```tsx\n * import { useAgentComponent as useAC } from \"@agent-surface/react\";\n * useAC({ type: \"alias.panel\", … });\n * ```\n *\n * — no capability in the catalog, and no unread call site saying the catalog\n * was short. Every other gap in this module *reports*: a dynamic `type`, an\n * unreadable spread, a granular hook, a wrapper it cannot prove. This one was\n * silent, and a silent gap is the only kind that makes a coverage number lie.\n */\ninterface ImportedApi {\n /** Local name → the registration hook it is bound to. */\n locals: Map<string, string>;\n /** Locals bound to a whole module of ours by `import * as ns`. */\n namespaces: Set<string>;\n}\n\nconst NO_IMPORTS: ImportedApi = { locals: new Map(), namespaces: new Set() };\n\nfunction importedRegistrations(source: ts.SourceFile): ImportedApi {\n const locals = new Map<string, string>();\n const namespaces = new Set<string>();\n\n for (const statement of source.statements) {\n if (!ts.isImportDeclaration(statement)) continue;\n if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;\n if (!isRegistrationModule(statement.moduleSpecifier.text)) continue;\n\n // `import type { … }` binds no value, so it can call nothing. A default\n // import binds nothing of ours either: none of these packages has one.\n const clause = statement.importClause;\n if (!clause || clause.isTypeOnly || !clause.namedBindings) continue;\n\n if (ts.isNamespaceImport(clause.namedBindings)) {\n namespaces.add(clause.namedBindings.name.text);\n continue;\n }\n for (const element of clause.namedBindings.elements) {\n if (element.isTypeOnly) continue;\n const imported = (element.propertyName ?? element.name).text;\n if (REGISTRATION_HOOKS.has(imported)) locals.set(element.name.text, imported);\n }\n }\n return { locals, namespaces };\n}\n\n/**\n * Registration hooks this file re-exports under a name that is not their own.\n *\n * ```ts\n * export { useAgentComponent as useAC } from \"@agent-surface/react\";\n * ```\n *\n * Downstream every call site is spelled `useAC` and imported from a module that\n * is not ours, so nothing there proves it registers anything — and following\n * the chain to find out is the hop `callsWrapper` already refuses to take, for\n * the same reason: a wrong attribution fabricates catalog entries.\n *\n * So the gap is reported *here*, on the line that opens it, rather than left\n * unsaid at each of the call sites it hides. Re-exported under its own name it\n * needs no report at all — downstream then reads a name this module knows.\n */\nfunction renamedRegistrationExports(\n source: ts.SourceFile,\n imports: ImportedApi,\n): { node: ts.Node; hook: string; exported: string }[] {\n const renamed: { node: ts.Node; hook: string; exported: string }[] = [];\n\n for (const statement of source.statements) {\n if (!ts.isExportDeclaration(statement) || statement.isTypeOnly) continue;\n const clause = statement.exportClause;\n if (!clause || !ts.isNamedExports(clause)) continue;\n\n const from = statement.moduleSpecifier;\n // `export … from` some other module says nothing about our API; the local\n // form is read against what this file imported.\n const fromOurs =\n from !== undefined && ts.isStringLiteral(from) && isRegistrationModule(from.text);\n if (from && !fromOurs) continue;\n\n for (const element of clause.elements) {\n if (element.isTypeOnly) continue;\n const local = (element.propertyName ?? element.name).text;\n const hook = fromOurs\n ? REGISTRATION_HOOKS.has(local)\n ? local\n : undefined\n : imports.locals.get(local);\n // Renaming *back* to the hook's own name closes the gap rather than\n // opening one, so the comparison is against the export, not the local.\n if (hook === undefined || element.name.text === hook) continue;\n renamed.push({ node: element, hook, exported: element.name.text });\n }\n }\n return renamed;\n}\n\n/* ── small AST helpers ────────────────────────────────────────────────── */\n\n/** Whether `object.member` reads a registration hook off a namespace of ours. */\nfunction namespaceMember(object: ts.Expression, member: string, imports: ImportedApi): boolean {\n return (\n ts.isIdentifier(object) && imports.namespaces.has(object.text) && REGISTRATION_HOOKS.has(member)\n );\n}\n\nfunction calleeName(call: ts.CallExpression, imports: ImportedApi = NO_IMPORTS): string | undefined {\n const callee = call.expression;\n\n // An alias is the same API under another name, and the binding says which.\n if (ts.isIdentifier(callee)) return imports.locals.get(callee.text) ?? callee.text;\n\n if (ts.isPropertyAccessExpression(callee)) {\n // `AS.useAgentComponent()`: the namespace binding is what *proves* this one\n // is ours. It resolved before this existed, but only because the property\n // name happened to be spelled like the hook — a coincidence that would have\n // attributed `anything.useAgentComponent()` just as readily.\n if (namespaceMember(callee.expression, callee.name.text, imports)) return callee.name.text;\n // Otherwise the plain name, which is what reads `registry.register(…)`.\n // Narrowing this to proven bindings would *drop* registrations that resolve\n // today — a re-export under its own name, most of all — and a silent loss\n // is the failure this whole change exists to remove.\n return callee.name.text;\n }\n\n // `AS[\"useAgentComponent\"]()` is as readable as the dotted form.\n if (ts.isElementAccessExpression(callee)) {\n const member = literalText(callee.argumentExpression);\n if (member !== undefined && namespaceMember(callee.expression, member, imports)) return member;\n }\n return undefined;\n}\n\nfunction propertyName(name: ts.PropertyName): string | undefined {\n if (ts.isIdentifier(name) || ts.isStringLiteral(name)) return name.text;\n return undefined;\n}\n\nfunction propertyOf(\n object: ts.ObjectLiteralExpression,\n wanted: string,\n): ts.Expression | undefined {\n for (const property of object.properties) {\n if (ts.isPropertyAssignment(property) && propertyName(property.name) === wanted) {\n return property.initializer;\n }\n // `{ type }` is `{ type: type }`, and the shorthand is what a wrapper hook\n // forwarding a parameter actually writes. Reading only the long form made\n // every such config look like it had no `type` at all.\n if (ts.isShorthandPropertyAssignment(property) && property.name.text === wanted) {\n return property.name;\n }\n }\n return undefined;\n}\n\nfunction hasSpread(object: ts.ObjectLiteralExpression): boolean {\n return object.properties.some((property) => ts.isSpreadAssignment(property));\n}\n\n/** The capability groups a spread would have to contribute to matter here. */\nconst CAPABILITY_GROUPS = [\"observations\", \"actions\"] as const;\n\n/**\n * Every key a spread could contribute, or `undefined` when that is not knowable.\n *\n * This exists to separate two spreads that look alike and are not:\n *\n * ```tsx\n * ...(props.instance ? { instanceId: props.instance } : {}) // keys: instanceId\n * ...buildMembers() // keys: unknown\n * ```\n *\n * The first cannot contribute a capability, because its key set is written out\n * and `instanceId` is not part of a capability id. The second could contribute\n * any number of them. Reporting both would flood the documented common case;\n * reporting neither is what let a whole registration disappear.\n *\n * Resolution is deliberately shallow, matching `objectLiteralFor`'s one hop: a\n * literal, a conditional over two knowable branches, or a same-module `const`.\n * Anything else is unknown, and unknown is reported rather than assumed empty.\n */\nfunction spreadKeys(\n expression: ts.Expression,\n source: ts.SourceFile,\n depth = 0,\n): string[] | undefined {\n if (depth > 1) return undefined;\n\n if (ts.isParenthesizedExpression(expression)) {\n return spreadKeys(expression.expression, source, depth);\n }\n\n // `cond ? { a } : {}` contributes whichever branch runs, so the key set is\n // the union — knowable only if both branches are.\n if (ts.isConditionalExpression(expression)) {\n const whenTrue = spreadKeys(expression.whenTrue, source, depth);\n const whenFalse = spreadKeys(expression.whenFalse, source, depth);\n if (!whenTrue || !whenFalse) return undefined;\n return [...new Set([...whenTrue, ...whenFalse])];\n }\n\n const resolved = objectLiteralFor(expression, source);\n if (!resolved.object) return undefined;\n\n const keys: string[] = [];\n for (const property of resolved.object.properties) {\n // A spread inside a spread: recurse while the hop budget allows, then admit\n // defeat rather than reporting a partial key set as a complete one.\n if (ts.isSpreadAssignment(property)) {\n const nested = spreadKeys(property.expression, source, depth + 1);\n if (!nested) return undefined;\n keys.push(...nested);\n continue;\n }\n const name =\n ts.isPropertyAssignment(property) || ts.isMethodDeclaration(property)\n ? propertyName(property.name)\n : ts.isShorthandPropertyAssignment(property)\n ? property.name.text\n : undefined;\n // A computed key could be anything, including a capability group.\n if (name === undefined) return undefined;\n keys.push(name);\n }\n return [...new Set(keys)];\n}\n\nfunction literalText(node: ts.Expression | undefined): string | undefined {\n if (!node) return undefined;\n if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) return node.text;\n if (ts.isParenthesizedExpression(node)) return literalText(node.expression);\n // `\"one long \" + \"description split over two lines\"` is as statically known\n // as either half. Descriptions are the provider's cached prompt prefix (D28),\n // so they are long enough that authors wrap them — calling that `partial`\n // would report the codebase's most common formatting choice as a defect.\n if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.PlusToken) {\n const left = literalText(node.left);\n const right = literalText(node.right);\n if (left !== undefined && right !== undefined) return left + right;\n }\n return undefined;\n}\n\n/** A short name for whatever construct defeated us, for the `note`. */\nfunction describeConstruct(node: ts.Expression): string {\n if (ts.isCallExpression(node)) {\n const callee = calleeName(node);\n return callee ? `built by ${callee}()` : \"built by a call expression\";\n }\n if (ts.isIdentifier(node)) return `a variable (${node.text}) this extractor could not follow`;\n if (ts.isConditionalExpression(node)) return \"a conditional expression\";\n if (ts.isTemplateExpression(node)) return \"a template with substitutions\";\n if (ts.isPropertyAccessExpression(node)) return \"a property access\";\n return \"a non-literal expression\";\n}\n\n/**\n * Resolves a config argument to an object literal, following **one hop** to a\n * same-module `const`.\n *\n * One hop is the whole rule. `useAgentComponent(CONFIG)` where `CONFIG` is a\n * module constant is common and cheap; `useAgentComponent(buildConfig(props))`\n * is not resolvable at any depth worth implementing. Stopping at one hop keeps\n * the limit *visible in the output* rather than buried in the implementation —\n * the deeper case is reported as `unresolved` with the construct named, which\n * is the behaviour this module exists to guarantee.\n */\nfunction objectLiteralFor(\n expression: ts.Expression,\n source: ts.SourceFile,\n): { object?: ts.ObjectLiteralExpression; note?: string } {\n if (ts.isObjectLiteralExpression(expression)) return { object: expression };\n\n if (ts.isIdentifier(expression)) {\n const target = expression.text;\n let found: ts.ObjectLiteralExpression | undefined;\n const visit = (node: ts.Node): void => {\n if (found) return;\n if (\n ts.isVariableDeclaration(node) &&\n ts.isIdentifier(node.name) &&\n node.name.text === target &&\n node.initializer &&\n ts.isObjectLiteralExpression(node.initializer)\n ) {\n found = node.initializer;\n return;\n }\n ts.forEachChild(node, visit);\n };\n visit(source);\n if (found) return { object: found };\n return {\n note: `the config is \\`${target}\\`, which is not a same-module object literal — the extractor follows one hop only`,\n };\n }\n\n return { note: `the config is ${describeConstruct(expression)}` };\n}\n\n/* ── the extraction itself ────────────────────────────────────────────── */\n\n/** Hooks that register one capability against the enclosing render scope. */\nconst GRANULAR_HOOKS = new Set([\"useAgentAction\", \"useAgentObservation\"]);\n\ninterface Emitter {\n push(capability: AuthoredCapability): void;\n origin(node: ts.Node): { file: string; line: number; site: string };\n}\n\nfunction capabilitiesFromGroup(\n group: ts.Expression | undefined,\n kind: \"observation\" | \"action\",\n componentType: string,\n componentPartial: string | undefined,\n emit: Emitter,\n source: ts.SourceFile,\n): void {\n if (!group) return;\n\n const resolved = objectLiteralFor(group, source);\n if (!resolved.object) {\n emit.push({\n capabilityId: `view:${componentType}.${UNRESOLVED_ID}`,\n kind,\n origin: emit.origin(group),\n resolution: \"unresolved\",\n reason: \"dynamic-group\",\n note: `\\`${kind}s\\` on \"${componentType}\" is not an object literal: ${resolved.note}`,\n });\n return;\n }\n\n for (const property of resolved.object.properties) {\n // A capability-map spread is only an identity gap when its keys cannot be\n // read. Readable keys are authored capabilities even when runtime presence\n // is conditional; the catalog is deliberately an upper bound.\n if (ts.isSpreadAssignment(property)) {\n const keys = spreadKeys(property.expression, source);\n if (keys === undefined) {\n emit.push({\n capabilityId: `view:${componentType}.${UNRESOLVED_ID}`,\n kind,\n origin: emit.origin(property),\n resolution: \"unresolved\",\n reason: \"spread-members\",\n note: `\\`${kind}s\\` on \"${componentType}\" spreads another object, which may contribute capabilities this inventory cannot name`,\n });\n continue;\n }\n\n for (const name of keys) {\n const notes = [\n ...(componentPartial ? [componentPartial] : []),\n `\\`${name}\\` is contributed by a spread, so its definition metadata or runtime presence may be dynamic`,\n ];\n emit.push({\n capabilityId: `view:${componentType}.${name}`,\n kind,\n origin: emit.origin(property),\n resolution: \"partial\",\n note: notes.join(\"; \"),\n });\n }\n continue;\n }\n\n const name =\n ts.isPropertyAssignment(property) || ts.isMethodDeclaration(property)\n ? propertyName(property.name)\n : ts.isShorthandPropertyAssignment(property)\n ? property.name.text\n : undefined;\n\n if (name === undefined) {\n emit.push({\n capabilityId: `view:${componentType}.${UNRESOLVED_ID}`,\n kind,\n origin: emit.origin(property),\n resolution: \"unresolved\",\n reason: \"computed-name\",\n note: `a capability on \"${componentType}\" has a computed name`,\n });\n continue;\n }\n\n // Identity is recovered from the key alone; the value only carries metadata.\n const capability: AuthoredCapability = {\n capabilityId: `view:${componentType}.${name}`,\n kind,\n origin: emit.origin(property),\n resolution: \"static\",\n };\n const notes: string[] = [];\n if (componentPartial) notes.push(componentPartial);\n\n const value = ts.isPropertyAssignment(property) ? property.initializer : undefined;\n const definition =\n value && ts.isCallExpression(value) && value.arguments.length > 0\n ? value.arguments[0]\n : value;\n\n if (definition && ts.isObjectLiteralExpression(definition)) {\n const description = literalText(propertyOf(definition, \"description\"));\n if (description !== undefined) capability.description = description;\n else notes.push(\"description is not a string literal\");\n\n if (kind === \"action\") {\n const effect = literalText(propertyOf(definition, \"effect\"));\n if (effect !== undefined) capability.effect = effect;\n else notes.push(\"effect is not a string literal\");\n }\n if (hasSpread(definition)) notes.push(\"the definition spreads another object\");\n } else {\n notes.push(\n value\n ? `the definition is ${describeConstruct(value)}`\n : \"the definition is not an object literal\",\n );\n }\n\n if (notes.length > 0) {\n capability.resolution = \"partial\";\n capability.note = notes.join(\"; \");\n }\n emit.push(capability);\n }\n}\n\nfunction visitCall(\n call: ts.CallExpression,\n emit: Emitter,\n source: ts.SourceFile,\n imports: ImportedApi,\n deferred: DeferredWrapper[],\n enclosing: EnclosingFunction | undefined,\n): void {\n const callee = calleeName(call, imports);\n if (callee === undefined) {\n // The callee is not a name at all. Where it reads a computed member of a\n // namespace of ours, that much *is* known: a call into the registration API\n // whose export cannot be named, and so whose registration — if it is one —\n // is nowhere in this catalog. Anywhere else it is simply not our call.\n const object = ts.isElementAccessExpression(call.expression)\n ? call.expression.expression\n : undefined;\n if (object && ts.isIdentifier(object) && imports.namespaces.has(object.text)) {\n emit.push({\n capabilityId: UNRESOLVED_ID,\n kind: \"action\",\n origin: emit.origin(call),\n resolution: \"unresolved\",\n reason: \"dynamic-callee\",\n note: `a call reads a computed member of \\`${object.text}\\`, a namespace of this library — which export it calls, and so whether it registers anything, cannot be read here`,\n });\n }\n return;\n }\n\n if (GRANULAR_HOOKS.has(callee)) {\n // OQ-3: the granular hooks register through a render-scope link rather than\n // one aggregated descriptor, so the component `type` is not at this call\n // site at all. Reporting the call site as unresolved is the honest state\n // until that join key is settled — silently ignoring it would make a\n // codebase that uses them look fully covered.\n emit.push({\n capabilityId: UNRESOLVED_ID,\n kind: callee === \"useAgentAction\" ? \"action\" : \"observation\",\n origin: emit.origin(call),\n resolution: \"unresolved\",\n reason: \"granular-hook\",\n note: `${callee}() registers against a render-scope link, so its component type is not at this call site`,\n });\n return;\n }\n\n if (callee !== \"useAgentComponent\" && callee !== \"register\") return;\n const argument = call.arguments[0];\n if (!argument) return;\n\n const resolved = objectLiteralFor(argument, source);\n if (!resolved.object) {\n emit.push({\n capabilityId: UNRESOLVED_ID,\n kind: \"action\",\n origin: emit.origin(call),\n resolution: \"unresolved\",\n reason: \"dynamic-config\",\n note: `${callee}() call site could not be read: ${resolved.note}`,\n });\n return;\n }\n\n const config = resolved.object;\n const typeNode = propertyOf(config, \"type\");\n const type = literalText(typeNode);\n if (type === undefined) {\n // `register` is a common method name; only treat it as ours once the call\n // actually looks like a registration. A `type` that exists but is dynamic\n // *is* ours, and is a genuine finding.\n if (callee === \"register\" && typeNode === undefined) return;\n\n // A `type` that is a parameter of the enclosing function is not dynamic —\n // it is decided one frame up, at the wrapper's call sites, and those are\n // string literals often enough to be worth following (OQ-13). Defer it;\n // the second pass either resolves it or reports it unread as before.\n const slot =\n enclosing && typeNode && ts.isIdentifier(typeNode)\n ? parameterSlot(typeNode.text, enclosing.fn)\n : undefined;\n if (slot && enclosing?.name) {\n deferred.push({ config, source, emit, wrapperName: enclosing.name, slot, site: call });\n return;\n }\n\n emit.push({\n capabilityId: UNRESOLVED_ID,\n kind: \"action\",\n origin: emit.origin(call),\n resolution: \"unresolved\",\n reason: \"dynamic-type\",\n note: `\\`type\\` is not a string literal, so no capability id on this component can be determined`,\n });\n return;\n }\n\n // A spread at the component level is the documented common case — the\n // conditional `...(props.instance ? { instanceId } : {})`. `instanceId` is not\n // part of a capability id, so identity survives; metadata may not.\n const componentPartial = hasSpread(config)\n ? \"the component config spreads another object, so some metadata here may be dynamic\"\n : undefined;\n\n // A spread whose key set cannot be read may carry `observations` or `actions`,\n // and those capabilities are then nowhere in this program's reach. Left\n // unreported, the registration disappears from *both* lists: absent from the\n // catalog, and absent from the unread call sites that exist to say the catalog\n // is incomplete. The count then claims a completeness it does not have, which\n // is the one failure this whole module is built to prevent.\n //\n // Enumerating the literal groups below is not enough on its own, either: a\n // config with a literal `observations` *and* an unreadable spread is missing\n // whatever `actions` the spread contributes, and the half that resolved would\n // otherwise read as the whole.\n for (const property of config.properties) {\n if (!ts.isSpreadAssignment(property)) continue;\n const keys = spreadKeys(property.expression, source);\n if (keys && !keys.some((key) => CAPABILITY_GROUPS.includes(key as \"observations\" | \"actions\"))) {\n continue;\n }\n emit.push({\n capabilityId: `view:${type}.${UNRESOLVED_ID}`,\n kind: \"action\",\n origin: emit.origin(property),\n resolution: \"unresolved\",\n reason: \"spread-members\",\n note: keys\n ? `\"${type}\" spreads ${describeConstruct(property.expression)}, which contributes \\`${keys\n .filter((key) => CAPABILITY_GROUPS.includes(key as \"observations\" | \"actions\"))\n .join(\"`/`\")}\\` this inventory cannot name`\n : `\"${type}\" spreads ${describeConstruct(property.expression)}, whose keys this inventory cannot read — it may contribute capabilities not listed here`,\n });\n }\n\n capabilitiesFromGroup(\n propertyOf(config, \"observations\"),\n \"observation\",\n type,\n componentPartial,\n emit,\n source,\n );\n capabilitiesFromGroup(\n propertyOf(config, \"actions\"),\n \"action\",\n type,\n componentPartial,\n emit,\n source,\n );\n}\n\n/* ── wrapper hooks: one hop *up* the call graph (OQ-13) ───────────────── */\n\n/**\n * A registration whose `type` is a parameter of the enclosing function.\n *\n * ```tsx\n * function useRegisteredPanel(type: string) {\n * useAgentComponent({ type, observations: { … } }); // ← here\n * }\n * useRegisteredPanel(\"devices.table\"); // ← type lives here\n * ```\n *\n * The capability ids are still fully determined by source text; they are just\n * determined *one frame up*. Reported unread, a single such wrapper hides the\n * whole surface built on it — 91% of one real application's capabilities\n * ([#31](https://github.com/Wiseair-srl/agent-surface/issues/31)).\n *\n * Resolution is deferred to a second pass because a call site may live in a\n * file walked before the wrapper's own.\n */\ninterface DeferredWrapper {\n config: ts.ObjectLiteralExpression;\n source: ts.SourceFile;\n emit: Emitter;\n /** The name a call site would use. */\n wrapperName: string;\n /** Where `type` sits in the wrapper's signature. */\n slot: { index: number; property?: string };\n /** The node to blame when this cannot be resolved. */\n site: ts.Node;\n}\n\ninterface CallSite {\n call: ts.CallExpression;\n source: ts.SourceFile;\n}\n\n/**\n * The function a call sits inside, and the name a caller would use for it.\n *\n * Tracked on the way *down* the tree rather than read from `node.parent`:\n * `ts.createProgram` leaves parent pointers unset until something forces the\n * binder to run, and forcing it means constructing a type checker — real work\n * on a large program, for an answer the walk already has in hand.\n */\ninterface EnclosingFunction {\n fn: ts.SignatureDeclaration;\n /** `function useX()` and `const useX = () => {}` both name the wrapper. */\n name?: string;\n}\n\nfunction functionLike(node: ts.Node): ts.SignatureDeclaration | undefined {\n if (\n ts.isFunctionDeclaration(node) ||\n ts.isFunctionExpression(node) ||\n ts.isArrowFunction(node) ||\n ts.isMethodDeclaration(node)\n ) {\n return node;\n }\n return undefined;\n}\n\n/**\n * Where a `type` identifier comes from in the enclosing signature, if it is a\n * parameter at all. Both spellings authors actually use:\n *\n * ```ts\n * function useX(type: string) // { index: 0 }\n * function useX({ type }: Props) // { index: 0, property: \"type\" }\n * ```\n */\nfunction parameterSlot(\n name: string,\n fn: ts.SignatureDeclaration,\n): { index: number; property?: string } | undefined {\n for (const [index, parameter] of fn.parameters.entries()) {\n if (ts.isIdentifier(parameter.name)) {\n if (parameter.name.text === name) return { index };\n continue;\n }\n if (ts.isObjectBindingPattern(parameter.name)) {\n for (const element of parameter.name.elements) {\n if (!ts.isIdentifier(element.name) || element.name.text !== name) continue;\n // `{ type: kind }` binds `kind` locally but reads the `type` property.\n const property =\n element.propertyName && ts.isIdentifier(element.propertyName)\n ? element.propertyName.text\n : name;\n return { index, property };\n }\n }\n }\n return undefined;\n}\n\n/**\n * Whether `site` is provably a call of `wrapper`, and not of something else\n * that happens to share its name.\n *\n * This predicate is the whole safety argument. Attributing a wrapper's\n * capabilities to the wrong call site would put ids in the catalog that no\n * component authors — **fabricating** entries, a failure this package has never\n * had and must not acquire. Reporting unread is always the safe answer, so\n * anything short of certainty returns `false`:\n *\n * - declared in the same file — lexically certain;\n * - imported, and the specifier resolves to the wrapper's own file;\n * - anything else — a re-export chain, a namespace import, a dynamic import —\n * is not certain, and is left unread.\n */\nfunction callsWrapper(\n site: CallSite,\n wrapper: DeferredWrapper,\n compilerOptions: ts.CompilerOptions,\n): boolean {\n const callee = site.call.expression;\n if (!ts.isIdentifier(callee) || callee.text !== wrapper.wrapperName) return false;\n\n if (site.source.fileName === wrapper.source.fileName) {\n // Same file: the only way this is a different function is a local shadow,\n // which would also shadow it for the reader.\n return true;\n }\n\n for (const statement of site.source.statements) {\n if (!ts.isImportDeclaration(statement)) continue;\n const clause = statement.importClause;\n if (!clause) continue;\n\n const named =\n clause.name?.text === wrapper.wrapperName ||\n (clause.namedBindings &&\n ts.isNamedImports(clause.namedBindings) &&\n clause.namedBindings.elements.some((element) => element.name.text === wrapper.wrapperName));\n if (!named) continue;\n if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;\n\n const resolved = ts.resolveModuleName(\n statement.moduleSpecifier.text,\n site.source.fileName,\n compilerOptions,\n ts.sys,\n ).resolvedModule;\n if (resolved?.resolvedFileName === wrapper.source.fileName) return true;\n }\n return false;\n}\n\nexport interface ExtractOptions {\n /** Directory the analysis is rooted at — normally the surface config's dir. */\n root: string;\n /** Explicit tsconfig; found upward from `root` when omitted. */\n tsconfig?: string;\n}\n\n/** Whitespace-insensitive source text, so a reformat is not a different site. */\nfunction normalizedText(node: ts.Node, source: ts.SourceFile): string {\n return node.getText(source).replace(/\\s+/g, \" \").trim();\n}\n\ninterface SiteIdentity {\n /** Named enclosures, outermost first. */\n labels: string[];\n enclosingCall: string;\n /** Innermost named enclosure, or the file — the subtree a twin could be in. */\n scope: ts.Node;\n}\n\nfunction siteIdentity(source: ts.SourceFile, node: ts.Node): SiteIdentity {\n const labels: string[] = [];\n let enclosingCall = \"\";\n let scope: ts.Node | undefined;\n\n for (let parent = node.parent; parent && parent !== source; parent = parent.parent) {\n if (!enclosingCall && ts.isCallExpression(parent)) {\n enclosingCall = normalizedText(parent, source);\n }\n const named =\n (ts.isFunctionDeclaration(parent) || ts.isMethodDeclaration(parent)) &&\n parent.name &&\n (ts.isIdentifier(parent.name) || ts.isStringLiteral(parent.name))\n ? parent.name.text\n : ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)\n ? parent.name.text\n : undefined;\n if (named !== undefined) {\n labels.push(named);\n scope ??= parent;\n }\n }\n return { labels: labels.reverse(), enclosingCall, scope: scope ?? source };\n}\n\n/**\n * How many identical nodes precede this one inside its named enclosure.\n *\n * Read from source *positions*, never from the order this module visits them:\n * the deferred wrapper pass emits origins for one file while walking another,\n * so a running counter would hand out a different answer depending on which ran\n * first — and a fingerprint that depends on traversal order is not one.\n */\nfunction occurrence(scope: ts.Node, node: ts.Node, source: ts.SourceFile): number {\n const text = normalizedText(node, source);\n const start = node.getStart(source);\n let rank = 0;\n const visit = (candidate: ts.Node): void => {\n if (\n candidate.kind === node.kind &&\n candidate.getStart(source) < start &&\n normalizedText(candidate, source) === text\n ) {\n rank += 1;\n }\n ts.forEachChild(candidate, visit);\n };\n ts.forEachChild(scope, visit);\n return rank;\n}\n\n/**\n * Which site this is, as something a committed allowance can be keyed on.\n *\n * Identity is the node's own text and the named enclosures around it. Nothing\n * positional goes in: not the line, and — the bug this replaces — not the\n * surrounding source either. An earlier version hashed a window of neighbouring\n * lines to tell two otherwise identical sites apart. It did tell them apart,\n * and it also moved the fingerprint whenever a comment changed within ten\n * lines, silently invalidating the entry and failing `check` for an edit nobody\n * thought was behavioural. That is the exact churn this key exists to survive,\n * and `file#reason` survived it before.\n *\n * Genuine twins — byte-identical calls in the same named enclosure — are told\n * apart by their rank within it instead. Inserting a third after them leaves\n * both keys alone; only a twin inserted *before* one shifts it, which is a\n * change to the very thing the key names.\n */\nfunction stableSite(source: ts.SourceFile, node: ts.Node): string {\n const { labels, enclosingCall, scope } = siteIdentity(source, node);\n return createHash(\"sha256\")\n .update(\n `${labels.join(\"/\")}\\0${enclosingCall}\\0${normalizedText(node, source)}\\0${occurrence(\n scope,\n node,\n source,\n )}`,\n )\n .digest(\"hex\")\n .slice(0, 12);\n}\n\nconst packageNameCache = new Map<string, string | undefined>();\n\nfunction packageNameFor(file: string): string | undefined {\n let dir = dirname(file);\n for (;;) {\n if (packageNameCache.has(dir)) return packageNameCache.get(dir);\n const packagePath = join(dir, \"package.json\");\n if (existsSync(packagePath)) {\n let name: string | undefined;\n try {\n const parsed = JSON.parse(readFileSync(packagePath, \"utf8\")) as { name?: unknown };\n if (typeof parsed.name === \"string\") name = parsed.name;\n } catch {\n // A malformed package boundary cannot make a file trusted/excluded.\n }\n packageNameCache.set(dir, name);\n return name;\n }\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nconst IMPLEMENTATION_PACKAGES = new Set([\n \"@agent-surface/core\",\n \"@agent-surface/react\",\n \"@agent-surface/orpc\",\n \"@agent-surface/testing\",\n \"@agent-surface/webmcp\",\n \"@agent-surface/cli\",\n]);\n\nfunction isAgentSurfaceImplementation(file: string): boolean {\n return IMPLEMENTATION_PACKAGES.has(packageNameFor(file) ?? \"\");\n}\n\n/**\n * Reads the program and returns every capability its registration call sites\n * author. Nothing is executed: no Vite server, no jsdom, no scenarios, no mount.\n */\nexport function extractCapabilities(options: ExtractOptions): CapabilityInventory {\n const root = resolve(options.root);\n const tsconfigPath = options.tsconfig\n ? isAbsolute(options.tsconfig)\n ? options.tsconfig\n : join(root, options.tsconfig)\n : findTsconfig(root);\n\n if (!tsconfigPath || !existsSync(tsconfigPath)) {\n throw new Error(\n `no tsconfig.json found from ${root} — \\`capabilities\\` reads the TypeScript program, ` +\n \"so it needs one (pass --tsconfig to point at it)\",\n );\n }\n\n const { fileNames, options: compilerOptions } = readProgramFiles(tsconfigPath);\n const program = ts.createProgram(fileNames, compilerOptions);\n\n const capabilities: AuthoredCapability[] = [];\n let filesAnalyzed = 0;\n\n let filesOutsideRoot = 0;\n\n // Registrations whose `type` comes from a parameter, and every call that\n // could supply it. Both are filled during the single walk below; the join\n // happens after, because a call site may live in a file walked earlier.\n const deferred: DeferredWrapper[] = [];\n const callsByName = new Map<string, CallSite[]>();\n\n for (const source of program.getSourceFiles()) {\n if (source.isDeclarationFile) continue;\n if (source.fileName.includes(\"/node_modules/\")) continue;\n // Workspace sources are part of the authored denominator even when they\n // live beside the config package. Only agent-surface's own implementation\n // packages are excluded: their `registry.register(definition)` is the hook\n // implementation, not another authored registration.\n if (!isInside(root, source.fileName) && isAgentSurfaceImplementation(source.fileName)) {\n filesOutsideRoot += 1;\n continue;\n }\n filesAnalyzed += 1;\n\n const emit: Emitter = {\n push: (capability) => capabilities.push(capability),\n origin: (node) => ({\n file: relative(root, source.fileName),\n line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1,\n site: stableSite(source, node),\n }),\n };\n\n // Read once per file: every call site in it is identified against these.\n const imports = importedRegistrations(source);\n for (const renamed of renamedRegistrationExports(source, imports)) {\n emit.push({\n capabilityId: UNRESOLVED_ID,\n kind: \"action\",\n origin: emit.origin(renamed.node),\n resolution: \"unresolved\",\n reason: \"dynamic-callee\",\n note: `${renamed.hook}() leaves this module as \\`${renamed.exported}\\`, so nothing at its call sites elsewhere proves they register anything — whatever they author is not in this catalog`,\n });\n }\n\n let pendingName: string | undefined;\n const visit = (node: ts.Node, enclosing?: EnclosingFunction): void => {\n const fn = functionLike(node);\n if (fn) {\n // A variable declaration names the arrow it initialises, and the walk\n // sees the declaration first — so the name is already in hand here.\n const named = ts.isFunctionDeclaration(node) && node.name ? node.name.text : pendingName;\n enclosing = { fn, ...(named ? { name: named } : {}) };\n pendingName = undefined;\n } else if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {\n pendingName = node.name.text;\n }\n\n if (ts.isCallExpression(node)) {\n visitCall(node, emit, source, imports, deferred, enclosing);\n // Every identifier-callee call is a potential wrapper call site. Indexed\n // by name here so the second pass does not re-walk the program.\n if (ts.isIdentifier(node.expression)) {\n const name = node.expression.text;\n const sites = callsByName.get(name) ?? [];\n sites.push({ call: node, source });\n callsByName.set(name, sites);\n }\n }\n ts.forEachChild(node, (child) => visit(child, enclosing));\n };\n visit(source);\n }\n\n // Second pass: one hop *up* the call graph. The first pass follows one hop\n // sideways to a same-module `const`; this is the same budget pointed the\n // other way, and it is what turns a shared wrapper hook from a single unread\n // line into the capabilities it actually authors.\n for (const wrapper of deferred) {\n const sites = (callsByName.get(wrapper.wrapperName) ?? []).filter((site) =>\n callsWrapper(site, wrapper, compilerOptions),\n );\n\n const types = new Map<string, ts.CallExpression>();\n const dynamic: CallSite[] = [];\n for (const site of sites) {\n const argument = site.call.arguments[wrapper.slot.index];\n const value =\n wrapper.slot.property && argument && ts.isObjectLiteralExpression(argument)\n ? propertyOf(argument, wrapper.slot.property)\n : argument;\n const text = value ? literalText(value) : undefined;\n if (text !== undefined) types.set(text, site.call);\n else dynamic.push(site);\n }\n\n // Partial resolution beats all-or-nothing: 15 literals plus 2 unread lines\n // is a truer catalog than 17 unread lines, and it stays honest about which\n // two it could not read.\n for (const type of [...types.keys()].sort()) {\n const componentPartial = hasSpread(wrapper.config)\n ? \"the component config spreads another object, so some metadata here may be dynamic\"\n : undefined;\n for (const [group, kind] of [\n [\"observations\", \"observation\"],\n [\"actions\", \"action\"],\n ] as const) {\n capabilitiesFromGroup(\n propertyOf(wrapper.config, group),\n kind,\n type,\n componentPartial,\n wrapper.emit,\n wrapper.source,\n );\n }\n }\n\n if (types.size === 0 || dynamic.length > 0) {\n // Nothing resolved, or some call sites pass a non-literal. Either way the\n // catalog is a floor here and has to say so.\n wrapper.emit.push({\n capabilityId: UNRESOLVED_ID,\n kind: \"action\",\n origin: wrapper.emit.origin(wrapper.site),\n resolution: \"unresolved\",\n reason: \"dynamic-type\",\n note:\n types.size === 0\n ? `\\`type\\` is a parameter of ${wrapper.wrapperName}(), and no call site of it in this program passes a string literal`\n : `\\`type\\` is a parameter of ${wrapper.wrapperName}(); ${types.size} call site${\n types.size === 1 ? \"\" : \"s\"\n } resolved, ${dynamic.length} pass${dynamic.length === 1 ? \"es\" : \"\"} a non-literal`,\n });\n }\n }\n\n capabilities.sort(\n (a, b) =>\n a.capabilityId.localeCompare(b.capabilityId) ||\n a.origin.file.localeCompare(b.origin.file) ||\n a.origin.line - b.origin.line,\n );\n\n return {\n capabilities,\n tsconfig: tsconfigPath,\n root,\n filesAnalyzed,\n filesOutsideRoot,\n domain: \"not-analyzed\",\n };\n}\n\n/** True when `file` lives under `root` — the analyzed boundary. */\nfunction isInside(root: string, file: string): boolean {\n const rel = relative(root, file);\n return rel !== \"\" && !rel.startsWith(\"..\") && !isAbsolute(rel);\n}\n\n/** Distinct capability ids the inventory resolved — the coverage denominator. */\nexport function authoredIds(inventory: CapabilityInventory): Set<string> {\n const ids = new Set<string>();\n for (const capability of inventory.capabilities) {\n if (capability.resolution === \"unresolved\") continue;\n if (capability.capabilityId.endsWith(UNRESOLVED_ID)) continue;\n ids.add(capability.capabilityId);\n }\n return ids;\n}\n\nexport function unresolved(inventory: CapabilityInventory): AuthoredCapability[] {\n return inventory.capabilities.filter((capability) => capability.resolution === \"unresolved\");\n}\n","/**\n * The report model every renderer draws: labelled rows, tables, and findings.\n *\n * `init`, `inspect`, `snapshot` and `check` answer different questions, but they\n * answer them about the same surface and a reader moves between them. So the\n * *shapes* are shared — a run header, a status matrix, a findings section — and\n * only the content differs. A second copy of \"how a finding looks\" is how two\n * commands drift into disagreeing about what they found, and a second copy of\n * \"how a block is laid out\" is how one report ends up with two text columns.\n *\n * Everything here is data. A command builds [`ReportPart`s](#ReportPart) and\n * hands them to the presenter; plain text renders them in `plain.ts`, the\n * terminal UI in `ink.tsx`, and neither can invent a row the other does not\n * have.\n */\nimport { relative } from \"node:path\";\nimport type { ScenarioFailure } from \"../analysis.js\";\nimport type { CollectResult } from \"../collect.js\";\nimport type { Depth } from \"../contract.js\";\nimport type { CoverageReport } from \"../coverage.js\";\nimport { unreadKey } from \"../coverage.js\";\nimport { authoredIds, unresolved, type CapabilityInventory } from \"../extract.js\";\nimport type { CapabilityRow, SurfaceView } from \"./model.js\";\n\nexport type ReportStatus = \"PASS\" | \"WARN\" | \"FAIL\" | \"ERROR\";\n\n/** Colour hint for the terminal UI. Plain text ignores it — words carry it. */\nexport type ReportTone = \"good\" | \"warn\" | \"bad\";\n\n/** One `label [STATUS] text` line. */\nexport interface ReportRow {\n label: string;\n status?: ReportStatus;\n tone?: ReportTone;\n text: string;\n}\n\nexport interface ReportBlock {\n title?: string;\n rows: ReportRow[];\n}\n\n/**\n * A finding: a heading that says what it is, a gloss that says why it matters,\n * and its entries as either a table or a list. `hint` is what to do about it,\n * printed with the finding rather than left for the reader to infer.\n */\nexport interface FindingSection {\n title: string;\n gloss: string;\n count: number;\n /** `notice` is reported but gates nothing — rendered without alarm. */\n tone?: \"finding\" | \"notice\";\n headers?: string[];\n rows?: TableRow[];\n lines?: string[];\n hint?: string;\n}\n\n/** A grid row. `note` is prose too long for a cell, printed under the row. */\nexport interface TableRow {\n cells: string[];\n note?: string;\n}\n\n/**\n * Which stream a part belongs on (`AS-CLI-004`). `out` is the command's answer;\n * `err` is everything a reader needs *about* the answer — what failed, what is\n * missing, what to run next — so a redirected `--json` or a captured report\n * still carries it.\n */\nexport type ReportStream = \"out\" | \"err\";\n\n/**\n * One piece of a report, as data.\n *\n * A command emits a sequence of these and never touches a renderer, which is\n * the whole point: the choice between the terminal UI and plain text is made\n * once, for the run, instead of at each of thirty call sites where three of them\n * will be forgotten. Before this existed, `check` had no terminal UI at all,\n * `snapshot` had no header, and `inspect` printed its static catalog as raw\n * text in the middle of a rendered one.\n */\nexport type ReportPart = { stream?: ReportStream } & (\n | { kind: \"blocks\"; blocks: ReportBlock[] }\n | { kind: \"table\"; title: string; lead?: string; headers: string[]; rows: TableRow[] }\n | { kind: \"findings\"; sections: FindingSection[] }\n | { kind: \"surface\"; view: SurfaceView; detail?: boolean }\n /** `muted` is an aside — a closing hint, never something the report turns on. */\n | { kind: \"note\"; title?: string; lines: string[]; muted?: boolean }\n | { kind: \"steps\"; title: string; steps: string[] }\n);\n\n/**\n * Where a report's text column starts, for every block in every command.\n *\n * Fixed rather than derived per block, so the `Coverage`/`Baselines` matrix in\n * `check`, the `Config`/`Depth` header above it and the `Capabilities` row of a\n * catalog all line up as one grid. A block that indents itself differently\n * reads as a different kind of thing — which is exactly what happened while\n * each renderer sized its own label column from whatever rows it happened to be\n * handed.\n *\n * Wide enough for every label this package prints; `reportGrid` still widens\n * for a longer one rather than crushing it, and the presenter keeps that width\n * for the rest of the report so the grid can only ever grow once.\n */\nexport const LABEL_WIDTH = 14;\nexport const STATUS_WIDTH = 7;\n\n/**\n * What a command is waiting for, in the words every command uses for it.\n *\n * The TypeScript program is read synchronously and the app loads after it —\n * seconds on a real repository, and a terminal showing nothing for them looks\n * wedged. `mountingLabel` names which scenario and how much is left, because a\n * spinner that only spins cannot be told apart from one that is stuck.\n */\nexport const READING_SOURCE = \"reading the source\";\n\nexport function mountingLabel(scenarios: string[], index: number): string {\n const position = scenarios.length > 1 ? ` (${index + 1} of ${scenarios.length})` : \"\";\n return `mounting ${scenarios[index]}${position}`;\n}\n\n/** The column widths a set of blocks needs, never narrower than the shared grid. */\nexport function reportGrid(\n blocks: ReportBlock[],\n minimum = LABEL_WIDTH,\n): { label: number; statuses: boolean } {\n return {\n label: Math.max(minimum, ...blocks.flatMap((b) => b.rows.map((row) => row.label.length + 2))),\n statuses: blocks.some((block) => block.rows.some((row) => row.status)),\n };\n}\n\n/**\n * A path as the reader will type it. `relative` alone turns a baseline\n * directory outside the working tree into a stack of `..` nobody can read, let\n * alone paste — so anything that escapes the working directory prints absolute.\n */\nexport function displayPath(path: string): string {\n const rel = relative(process.cwd(), path);\n return rel && !rel.startsWith(\"..\") ? rel : path;\n}\n\nconst DEPTH_TEXT: Record<Depth, string> = {\n full: \"full — the source is read and every scenario is mounted\",\n static: \"static — the source only; nothing is mounted\",\n runtime: \"runtime — the scenarios only; the source is not read\",\n};\n\nexport interface RunContext {\n configPath: string;\n depth: Depth;\n /** The effective scope: `--scope` when given, else the config's. */\n scope?: string[];\n /** Scenarios this run covers. Absent when the depth mounts nothing. */\n scenarios?: string[];\n /** Every scenario the config declares, for `n of m` when one was named. */\n declaredScenarios?: string[];\n}\n\n/**\n * What the numbers below are about, before there are any (`AS-CLI-007`).\n *\n * A report that opens with its counts asks the reader to hold them until the\n * qualifier arrives — and on a scoped run, or a run against a config they did\n * not choose, the unqualified number is simply the wrong one.\n */\nexport function runContextRows(context: RunContext): ReportRow[] {\n const rows: ReportRow[] = [\n { label: \"Config\", text: displayPath(context.configPath) },\n { label: \"Depth\", text: DEPTH_TEXT[context.depth] },\n {\n label: \"Scope\",\n text:\n context.scope && context.scope.length > 0\n ? `${context.scope.join(\" · \")} — every count below is relative to it`\n : \"whole surface — no component-type prefix filter\",\n },\n ];\n if (context.scenarios) {\n const declared = context.declaredScenarios ?? context.scenarios;\n const named = context.scenarios.length < declared.length;\n rows.push({\n label: \"Scenarios\",\n text:\n `${named ? `${context.scenarios.length} of ${declared.length}` : context.scenarios.length}` +\n ` — ${context.scenarios.join(\", \")}`,\n });\n }\n return rows;\n}\n\n/**\n * The static catalog's summary (`AS-COVER-001…003`). \"Upper bound\" is in the\n * text on purpose: a tsconfig's include globs are wider than what a bundle\n * reaches, so a capability in a component no route renders any more is counted\n * here. That is dead code — a different finding, not a false positive — and the\n * reader has to be told which number they are holding.\n */\nexport function catalogRows(\n inventory: CapabilityInventory,\n options: { domainCapabilities?: number; mounted?: boolean } = {},\n): ReportRow[] {\n const resolved = inventory.capabilities.filter((c) => c.resolution !== \"unresolved\");\n const unreadEntries = unresolved(inventory);\n const dynamicMetadata = resolved.filter((c) => c.resolution === \"partial\").length;\n const authored = authoredIds(inventory).size + (options.domainCapabilities ?? 0);\n\n return [\n {\n label: \"STATUS\",\n tone: unreadEntries.length > 0 ? \"warn\" : \"good\",\n text:\n unreadEntries.length > 0\n ? `INCOMPLETE — ${unreadEntries.length} unread capability identit${\n unreadEntries.length === 1 ? \"y\" : \"ies\"\n }`\n : \"COMPLETE — every capability identity resolved\",\n },\n {\n label: \"Capabilities\",\n text: `${authored} authored (upper bound) · ${resolved.length} resolved call site${\n resolved.length === 1 ? \"\" : \"s\"\n }`,\n },\n {\n label: \"Program\",\n text:\n `${inventory.filesAnalyzed} file${inventory.filesAnalyzed === 1 ? \"\" : \"s\"} analyzed` +\n (inventory.filesOutsideRoot > 0\n ? ` · ${inventory.filesOutsideRoot} agent-surface implementation file${\n inventory.filesOutsideRoot === 1 ? \"\" : \"s\"\n } excluded`\n : \"\"),\n },\n {\n label: \"Metadata\",\n text:\n `${dynamicMetadata} call site${dynamicMetadata === 1 ? \"\" : \"s\"} partially read` +\n (dynamicMetadata > 0 ? \" · identity remains resolved\" : \"\"),\n },\n {\n // Three different statements, and only one of them is a number. \"Nobody\n // looked\" and \"there is nothing to look at\" must not read alike (OQ-1).\n label: \"Domain\",\n ...(options.mounted && options.domainCapabilities === undefined\n ? { tone: \"warn\" as const }\n : {}),\n text:\n options.domainCapabilities !== undefined\n ? `${options.domainCapabilities} manifest capabilit${\n options.domainCapabilities === 1 ? \"y\" : \"ies\"\n }`\n : options.mounted\n ? \"no authoritative oRPC manifest configured — that plane has no denominator\"\n : \"not analyzed at static depth; full depth reads the oRPC manifest\",\n },\n ];\n}\n\n/**\n * The header a run opens with: what it was pointed at, and the catalog it read\n * before mounting anything.\n */\nexport function runHeaderBlocks(\n title: string,\n context: RunContext,\n inventory?: CapabilityInventory,\n domainCapabilities?: number,\n): ReportBlock[] {\n return [\n { title, rows: runContextRows(context) },\n ...(inventory\n ? [\n {\n title: \"STATIC CATALOG\",\n rows: catalogRows(inventory, {\n ...(domainCapabilities === undefined ? {} : { domainCapabilities }),\n ...(context.depth === \"static\" ? {} : { mounted: true }),\n }),\n },\n ]\n : []),\n ];\n}\n\nfunction componentOf(capabilityId: string): string {\n const path = capabilityId.replace(/^(view|domain):/, \"\");\n const dot = path.lastIndexOf(\".\");\n return dot === -1 ? path : path.slice(0, dot);\n}\n\nexport interface CatalogDetailOptions {\n /** Show the raw call-site table, origins, notes and diagnostic prose. */\n detail?: boolean;\n}\n\n/**\n * The catalog *below* its summary: which component authors what, and every call\n * site the extractor could not read.\n *\n * Only `--depth static` prints this. At `--depth full` the scenario tables name\n * every capability a scenario reached, the `UNREACHED` section names the ones it\n * did not, and the verdict carries the unread call sites — so printing it here\n * would be the same information a second time, above the answer instead of in it.\n */\nexport function catalogDetailParts(\n inventory: CapabilityInventory,\n options: CatalogDetailOptions = {},\n): ReportPart[] {\n const parts: ReportPart[] = [];\n const resolved = inventory.capabilities.filter((c) => c.resolution !== \"unresolved\");\n const unreadEntries = unresolved(inventory);\n\n const components = new Map<string, { ids: Set<string>; sites: number; partial: number }>();\n for (const capability of resolved) {\n const component = componentOf(capability.capabilityId);\n const current = components.get(component) ?? { ids: new Set<string>(), sites: 0, partial: 0 };\n current.ids.add(capability.capabilityId);\n current.sites += 1;\n if (capability.resolution === \"partial\") current.partial += 1;\n components.set(component, current);\n }\n\n if (components.size > 0) {\n parts.push({\n kind: \"table\",\n title: `COMPONENTS (${components.size})`,\n headers: [\"COMPONENT\", \"CAPABILITIES\", \"CALL SITES\", \"DYNAMIC META\"],\n rows: [...components.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([component, data]) => ({\n cells: [\n `view:${component}`,\n String(data.ids.size),\n String(data.sites),\n data.partial > 0 ? String(data.partial) : NONE,\n ],\n note: [...data.ids].sort().join(\" · \"),\n })),\n });\n }\n\n if (unreadEntries.length > 0) {\n const groups = new Map<string, number>();\n for (const entry of unreadEntries) {\n const key = `${entry.origin.file}\\0${entry.reason ?? \"unknown\"}`;\n groups.set(key, (groups.get(key) ?? 0) + 1);\n }\n parts.push({\n kind: \"table\",\n title: `UNREAD SITES (${unreadEntries.length})`,\n lead: \"Counts above are a floor until these sites are resolved or explicitly accepted.\",\n headers: [\"FILE\", \"REASON\", \"SITES\"],\n rows: [...groups.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, count]) => {\n const [file, reason] = key.split(\"\\0\");\n return { cells: [file ?? \"?\", reason ?? \"unknown\", String(count)] };\n }),\n });\n // Spelled out, and never wrapped: the key is `file#reason#site` where the\n // site is a hash rather than the line the reader is looking at, so it is\n // copied, not typed.\n parts.push({\n kind: \"note\",\n title: \"ALLOWLIST KEYS\",\n lines: unreadEntries.map((entry) => ` allowlist key: ${unreadKey(entry)}`),\n });\n }\n\n if (options.detail) {\n const byId = [...resolved].sort((a, b) => a.capabilityId.localeCompare(b.capabilityId));\n if (byId.length > 0) {\n parts.push({\n kind: \"table\",\n title: `CAPABILITY DETAILS (${byId.length} call sites)`,\n headers: [\"CAPABILITY\", \"KIND\", \"ORIGIN\", \"READ\"],\n rows: byId.map((capability) => ({\n cells: [\n capability.capabilityId,\n capability.kind,\n `${capability.origin.file}:${capability.origin.line}`,\n capability.resolution,\n ],\n ...(capability.note ? { note: capability.note } : {}),\n })),\n });\n }\n if (unreadEntries.length > 0) {\n parts.push({ kind: \"findings\", sections: [unreadSection(unreadEntries)] });\n }\n } else if (unreadEntries.length > 0 || resolved.length > 0) {\n // The keys are above, in full: promising them behind a flag that has\n // already been satisfied is how a reader learns to distrust the footer.\n parts.push({\n kind: \"note\",\n muted: true,\n lines: [\"Details: re-run with --detail for origins, per-site notes, and diagnostics.\"],\n });\n }\n return parts;\n}\n\n/** Per-scenario totals — the row a reader compares scenarios across. */\nexport interface ScenarioStats {\n scenario: string;\n route?: string;\n callable: number;\n disabled: number;\n hidden: number;\n rejected: number;\n /** Set when the scenario threw instead of mounting. */\n failed?: boolean;\n /** Why it threw, printed under its row rather than in a column. */\n failure?: string;\n /** `check` only: how the committed baseline compared. */\n baseline?: string;\n}\n\nexport function scenarioStats(result: CollectResult): ScenarioStats {\n const counts = { expose: 0, disable: 0, hide: 0 };\n for (const capability of result.explanation.capabilities) counts[capability.outcome] += 1;\n return {\n scenario: result.scenario,\n ...(result.snapshot.route?.path ? { route: result.snapshot.route.path } : {}),\n callable: counts.expose,\n disabled: counts.disable,\n hidden: counts.hide,\n rejected: result.rejections.length,\n };\n}\n\nconst NONE = \"—\";\n\n/** One row per scenario: the comparison a list of names cannot make. */\nexport function scenarioTable(\n stats: ScenarioStats[],\n options: { baselines?: boolean } = {},\n): { headers: string[]; rows: TableRow[] } {\n const headers = [\"SCENARIO\", \"ROUTE\", \"CALLABLE\", \"DISABLED\", \"HIDDEN\", \"REJECTED\"];\n if (options.baselines) headers.push(\"BASELINE\");\n return {\n headers,\n rows: stats.map((entry) => ({\n // The baseline column already says `did not mount`, so the note carries\n // only what a reader cannot get anywhere else: why.\n ...(entry.failed\n ? {\n note: `${options.baselines ? \"\" : \"did not mount — \"}${\n entry.failure ?? \"the scenario threw during mount\"\n }`,\n }\n : {}),\n cells: [\n entry.scenario,\n entry.route ?? NONE,\n ...(entry.failed\n ? [NONE, NONE, NONE, NONE]\n : [\n String(entry.callable),\n String(entry.disabled),\n String(entry.hidden),\n entry.rejected > 0 ? String(entry.rejected) : NONE,\n ]),\n ...(options.baselines ? [entry.failed ? \"did not mount\" : (entry.baseline ?? NONE)] : []),\n ],\n })),\n };\n}\n\n/**\n * What every scenario together made of one capability.\n *\n * `unreached` answers \"did anything mount this\". This answers the question one\n * step in: *it mounted, and could any scenario actually call it?* A drawer\n * every scenario leaves closed registers its `close` action in every snapshot\n * and is callable in none of them — reached by the coverage join, and never\n * exercised. Neither half sees that alone.\n */\nexport interface CapabilityReach {\n capabilityId: string;\n /** The best outcome any scenario produced: expose beats disable beats hide. */\n best: \"expose\" | \"disable\" | \"hide\";\n effect?: string;\n flags: string[];\n /** Why it was not callable, from the scenario that came closest. */\n note?: string;\n scenarios: string[];\n}\n\nconst RANK = { hide: 0, disable: 1, expose: 2 } as const;\n\nexport function trackReach(\n reach: Map<string, CapabilityReach>,\n rows: CapabilityRow[],\n scenario: string,\n): void {\n for (const row of rows) {\n const current = reach.get(row.capabilityId);\n if (!current) {\n reach.set(row.capabilityId, {\n capabilityId: row.capabilityId,\n best: row.outcome,\n ...(row.effect ? { effect: row.effect } : {}),\n flags: row.flags,\n ...(row.reason ? { note: row.reason } : {}),\n scenarios: [scenario],\n });\n continue;\n }\n current.scenarios.push(scenario);\n // The effect is a property of the descriptor, so any scenario that carries\n // one carries the same one — but a policy-hidden row has no snapshot entry\n // to read it from, so the first scenario to see it wins.\n if (!current.effect && row.effect) current.effect = row.effect;\n if (current.flags.length === 0 && row.flags.length > 0) current.flags = row.flags;\n if (RANK[row.outcome] > RANK[current.best]) {\n current.best = row.outcome;\n if (row.reason) current.note = row.reason;\n else delete current.note;\n }\n }\n}\n\nexport function neverCallable(reach: Map<string, CapabilityReach>): CapabilityReach[] {\n return [...reach.values()]\n .filter((entry) => entry.best !== \"expose\")\n .sort((a, b) => a.capabilityId.localeCompare(b.capabilityId));\n}\n\nconst MUTATING = new Set([\"server-mutation\", \"external-side-effect\", \"destructive\"]);\n\ninterface Risk {\n present: number;\n destructive: number;\n mutating: number;\n confirmed: number;\n bound: number;\n}\n\n/**\n * What an agent could do here, as opposed to how much surface there is.\n *\n * Counted over everything the snapshot carries — `callable` *and*\n * `visible-disabled`. A disabled capability is disclosed to the agent and\n * becomes callable the moment the UI state it waits on arrives, so leaving it\n * out would report a surface with a destructive action on it as one without.\n * A hidden capability is genuinely absent: authority removed it, and counting\n * it here would report a policy working as a risk.\n */\nfunction riskOf(\n entries: Array<{ outcome: \"expose\" | \"disable\" | \"hide\"; effect?: string; flags: string[] }>,\n): Risk {\n const present = entries.filter((entry) => entry.outcome !== \"hide\");\n return {\n present: present.length,\n destructive: present.filter((entry) => entry.effect === \"destructive\").length,\n mutating: present.filter((entry) => entry.effect && MUTATING.has(entry.effect)).length,\n confirmed: present.filter((entry) =>\n entry.flags.some((flag) => flag.startsWith(\"confirmation:\")),\n ).length,\n bound: present.filter((entry) => entry.flags.some((flag) => flag.includes(\" bound\"))).length,\n };\n}\n\nfunction riskParts(risk: Risk): string[] {\n return [\n ...(risk.destructive > 0 ? [`${risk.destructive} destructive`] : []),\n ...(risk.mutating > risk.destructive ? [`${risk.mutating - risk.destructive} mutating`] : []),\n ...(risk.confirmed > 0 ? [`${risk.confirmed} confirmation-gated`] : []),\n ];\n}\n\n/**\n * The scenario header's risk clause. Empty when there is nothing to say, so a\n * read-only surface does not carry a row of zeroes to be read and dismissed.\n */\nexport function riskClause(rows: CapabilityRow[]): string {\n const parts = riskParts(riskOf(rows));\n return parts.join(\", \");\n}\n\n/** The same question over every scenario at once, for the closing summary. */\nfunction riskText(reach: Map<string, CapabilityReach>): string {\n const risk = riskOf([...reach.values()].map((entry) => ({ ...entry, outcome: entry.best })));\n const parts = riskParts(risk);\n if (risk.present === 0) {\n return \"nothing is on the surface at all — every capability was hidden by policy\";\n }\n if (parts.length === 0) {\n return (\n `nothing mutating or destructive is on the surface · ${risk.present} ` +\n `read-only or local-state capabilit${risk.present === 1 ? \"y\" : \"ies\"}`\n );\n }\n return [...parts, ...(risk.bound > 0 ? [`${risk.bound} with bound input`] : [])].join(\" · \");\n}\n\nexport interface SurfaceSummaryInput {\n depth: Depth;\n coverage?: CoverageReport;\n scenarios: ScenarioStats[];\n failures: number;\n reach: Map<string, CapabilityReach>;\n}\n\n/**\n * `inspect`'s closing block — the thing a reader who scrolled to the bottom\n * stops on. It states no PASS/FAIL: `inspect` reports findings and never gates\n * on them, and a status word beside a number would read as the exit code it is\n * not.\n */\nexport function surfaceSummaryRows(input: SurfaceSummaryInput): ReportRow[] {\n const rows: ReportRow[] = [];\n const coverage = input.coverage;\n\n if (coverage) {\n rows.push({\n label: \"Reach\",\n tone: coverage.unreached.length > 0 ? \"bad\" : \"good\",\n text:\n `${coverage.reached}/${coverage.authored} authored capabilit${\n coverage.authored === 1 ? \"y\" : \"ies\"\n } reached` +\n (coverage.unreached.length > 0 ? ` · ${coverage.unreached.length} unreached` : \"\") +\n (coverage.allowed.length > 0 ? ` · ${coverage.allowed.length} allowlisted` : \"\"),\n });\n }\n\n const mounted = input.reach.size;\n const dark = neverCallable(input.reach);\n if (mounted > 0) {\n const stuck = dark.filter((entry) => entry.best === \"disable\").length;\n const why = [\n ...(stuck > 0 ? [`${stuck} disabled`] : []),\n ...(dark.length > stuck ? [`${dark.length - stuck} hidden`] : []),\n ].join(\", \");\n rows.push({\n label: \"Callable\",\n tone: dark.length > 0 ? \"warn\" : \"good\",\n text:\n `${mounted - dark.length}/${mounted} mounted capabilit${\n mounted === 1 ? \"y is\" : \"ies are\"\n } callable in at least one scenario` +\n (dark.length > 0 ? ` · ${dark.length} never callable (${why})` : \"\"),\n });\n rows.push({ label: \"Risk\", text: riskText(input.reach) });\n }\n\n if (coverage) {\n if (coverage.domainReached.length > 0 || coverage.domainAuthoritative) {\n rows.push({\n label: \"Domain\",\n tone: coverage.unmanifestedDomain.length > 0 ? \"bad\" : \"good\",\n text:\n coverage.unmanifestedDomain.length > 0\n ? `${coverage.unmanifestedDomain.length} mounted capabilit${\n coverage.unmanifestedDomain.length === 1 ? \"y is\" : \"ies are\"\n } absent from the oRPC manifest`\n : `${coverage.domainReached.length} capabilit${\n coverage.domainReached.length === 1 ? \"y\" : \"ies\"\n } reached${\n coverage.domainAuthoritative\n ? \" against the authoritative oRPC manifest\"\n : \" and held apart — configure the manifest to cover that plane\"\n }`,\n });\n }\n rows.push({\n label: \"Catalog\",\n tone: coverage.unresolved.length > 0 ? \"warn\" : \"good\",\n text:\n coverage.unresolved.length > 0\n ? `${coverage.unresolved.length} unread call site${\n coverage.unresolved.length === 1 ? \"\" : \"s\"\n } — every count above is a floor`\n : `every call site read${\n coverage.allowedUnread.length > 0\n ? ` · ${coverage.allowedUnread.length} allowlisted`\n : \"\"\n }`,\n });\n }\n\n rows.push({\n label: \"Scenarios\",\n tone: input.failures > 0 ? \"bad\" : undefined,\n text:\n `${input.scenarios.filter((entry) => !entry.failed).length} mounted` +\n (input.failures > 0 ? ` · ${input.failures} did not mount` : \"\"),\n });\n\n rows.push({ label: \"Verdict\", tone: verdictTone(input), text: verdictText(input) });\n return rows;\n}\n\nfunction verdictTone(input: SurfaceSummaryInput): ReportTone {\n if (input.failures > 0) return \"bad\";\n if (!input.coverage) return \"warn\";\n const clean =\n input.coverage.unreached.length === 0 &&\n input.coverage.unresolved.length === 0 &&\n input.coverage.staleAllowlist.length === 0 &&\n input.coverage.staleUnreadAllowlist.length === 0 &&\n input.coverage.unmanifestedDomain.length === 0;\n return clean ? \"good\" : \"bad\";\n}\n\n/** The one sentence a reader takes away. Never a status word — see above. */\nfunction verdictText(input: SurfaceSummaryInput): string {\n if (input.failures > 0) {\n return \"a scenario did not mount, so no coverage verdict was computed at all\";\n }\n const coverage = input.coverage;\n if (!coverage) {\n return input.depth === \"runtime\"\n ? \"the source was not read at this depth — a statement about these scenarios only\"\n : \"no coverage verdict at this depth\";\n }\n if (coverage.unreached.length > 0) {\n return `${coverage.unreached.length} authored capabilit${\n coverage.unreached.length === 1 ? \"y is\" : \"ies are\"\n } reached by no scenario`;\n }\n if (coverage.unresolved.length > 0) {\n return \"every capability the catalog could read is reached — and the catalog has holes in it\";\n }\n return coverage.allowed.length > 0\n ? \"no new coverage gaps — the allowlist still holds the known ones\"\n : \"every authored capability is reached by a scenario\";\n}\n\nexport interface CheckOverview {\n status: \"PASS\" | \"FAIL\" | \"ERROR\";\n coverage?: CoverageReport;\n unresolvedAllowed: boolean;\n baselineCurrent: number;\n baselineTotal: number;\n scenarioManifestOk: boolean;\n rejected: number;\n mountFailures: number;\n /** What the run was pointed at. Printed above the matrix (`AS-CLI-007`). */\n context?: RunContext;\n /** Per-scenario totals, one row each — including the ones that threw. */\n stats: ScenarioStats[];\n}\n\n/** The health matrix: one row per class of finding, whether or not it fired. */\nfunction checkMatrixRows(input: CheckOverview): ReportRow[] {\n const rows: ReportRow[] = [];\n const coverage = input.coverage;\n\n if (coverage) {\n rows.push({\n label: \"Coverage\",\n status:\n coverage.unreached.length > 0 || coverage.staleAllowlist.length > 0\n ? \"FAIL\"\n : coverage.allowed.length > 0\n ? \"WARN\"\n : \"PASS\",\n text:\n `${coverage.reached}/${coverage.authored} authored capabilities reached` +\n (coverage.unreached.length > 0 ? ` · ${coverage.unreached.length} unreached` : \"\") +\n (coverage.allowed.length > 0 ? ` · ${coverage.allowed.length} unreached allowlisted` : \"\") +\n (coverage.staleAllowlist.length > 0\n ? ` · ${coverage.staleAllowlist.length} stale allowlist entr${\n coverage.staleAllowlist.length === 1 ? \"y\" : \"ies\"\n }`\n : \"\"),\n });\n\n const unread = coverage.unresolved.length;\n const accepted = coverage.allowedUnread.length;\n rows.push({\n label: \"Catalog\",\n status:\n coverage.staleUnreadAllowlist.length > 0 || (unread > 0 && !input.unresolvedAllowed)\n ? \"FAIL\"\n : unread > 0 || accepted > 0\n ? \"WARN\"\n : \"PASS\",\n text:\n coverage.staleUnreadAllowlist.length > 0\n ? `${coverage.staleUnreadAllowlist.length} stale unread allowlist entr${\n coverage.staleUnreadAllowlist.length === 1 ? \"y\" : \"ies\"\n }`\n : unread > 0\n ? `${unread} unread static site${unread === 1 ? \"\" : \"s\"}${\n input.unresolvedAllowed ? \" accepted by --allow-unresolved\" : \"\"\n }`\n : accepted > 0\n ? `${accepted} unread static site${accepted === 1 ? \"\" : \"s\"} allowlisted`\n : \"all static sites resolved\",\n });\n\n rows.push({\n label: \"Domain\",\n status:\n coverage.unmanifestedDomain.length > 0\n ? \"FAIL\"\n : coverage.domainAuthoritative\n ? \"PASS\"\n : \"WARN\",\n text:\n coverage.unmanifestedDomain.length > 0\n ? `${coverage.unmanifestedDomain.length} mounted capabilit${\n coverage.unmanifestedDomain.length === 1 ? \"y\" : \"ies\"\n } absent from manifest`\n : coverage.domainAuthoritative\n ? `${coverage.domainReached.length} manifest capabilit${\n coverage.domainReached.length === 1 ? \"y\" : \"ies\"\n } reached`\n : \"authoritative manifest not configured\",\n });\n } else {\n rows.push({\n label: \"Coverage\",\n status: input.status === \"ERROR\" ? \"ERROR\" : \"WARN\",\n text:\n input.status === \"ERROR\"\n ? \"no verdict; runtime analysis incomplete\"\n : \"not evaluated — statement about these scenarios only; re-run with --depth full\",\n });\n }\n\n const baselineOk = input.baselineCurrent === input.baselineTotal && input.scenarioManifestOk;\n rows.push({\n label: \"Baselines\",\n status: baselineOk ? \"PASS\" : \"FAIL\",\n text:\n `${input.baselineCurrent}/${input.baselineTotal} scenario baselines current` +\n (input.scenarioManifestOk ? \"\" : \" · scenario manifest differs\"),\n });\n\n const mounted = input.stats.filter((entry) => !entry.failed).length;\n rows.push({\n label: \"Runtime\",\n status: input.mountFailures > 0 ? \"ERROR\" : input.rejected > 0 ? \"FAIL\" : \"PASS\",\n text:\n input.mountFailures > 0\n ? `${input.mountFailures} scenario${input.mountFailures === 1 ? \"\" : \"s\"} did not mount`\n : input.rejected > 0\n ? `${input.rejected} registration${input.rejected === 1 ? \"\" : \"s\"} rejected`\n : `${mounted} scenario${mounted === 1 ? \"\" : \"s\"} mounted`,\n });\n\n return rows;\n}\n\n/** `check`'s first screen: the verdict, what it was computed over, then health. */\nexport function checkOverviewParts(input: CheckOverview): ReportPart[] {\n const table = scenarioTable(input.stats, { baselines: true });\n return [\n {\n kind: \"blocks\",\n blocks: [\n {\n title: `SURFACE CHECK ${input.status}`,\n rows: input.context ? runContextRows(input.context) : [],\n },\n { rows: checkMatrixRows(input) },\n ],\n },\n {\n kind: \"table\",\n title: `SCENARIOS (${input.stats.length})`,\n headers: table.headers,\n rows: table.rows,\n },\n ];\n}\n\n/**\n * The findings a coverage report carries. Shared by `inspect`, `snapshot` and\n * `check`, so the gate and the viewer cannot describe the same gap differently.\n */\nexport function coverageSections(\n report: CoverageReport,\n options: { compact?: boolean; detail?: boolean } = {},\n): FindingSection[] {\n const sections: FindingSection[] = [];\n\n if (report.unreached.length > 0) {\n sections.push({\n title: \"UNREACHED\",\n gloss: \"authored, and no scenario mounts it\",\n count: report.unreached.length,\n headers: [\"CAPABILITY\", \"ORIGIN\"],\n rows: report.unreached.map((entry) => ({\n cells: [entry.capabilityId, `${entry.origin.file}:${entry.origin.line}`],\n })),\n hint:\n \"add a scenario that mounts them, delete the dead component, or record the decision \" +\n `in ${displayPath(report.allowlistPath)}`,\n });\n }\n\n if (report.undeclared.length > 0) {\n if (!options.compact || options.detail) {\n sections.push({\n title: \"UNDECLARED\",\n gloss: \"present at runtime with no static origin — a dynamic registration, or a gap here\",\n count: report.undeclared.length,\n tone: \"notice\",\n lines: report.undeclared,\n });\n } else {\n sections.push({\n title: \"NOTICE\",\n gloss: `${report.undeclared.length} runtime capabilit${\n report.undeclared.length === 1 ? \"y has\" : \"ies have\"\n } no static origin; re-run with --detail to list them`,\n count: 0,\n tone: \"notice\",\n });\n }\n }\n\n if (report.unmanifestedDomain.length > 0) {\n sections.push({\n title: \"UNMANIFESTED DOMAIN\",\n gloss: \"mounted, but absent from the authoritative oRPC manifest\",\n count: report.unmanifestedDomain.length,\n lines: report.unmanifestedDomain,\n hint: \"add them to the manifest, or stop mounting a router the manifest does not describe\",\n });\n }\n\n if (report.staleAllowlist.length > 0) {\n sections.push({\n title: \"STALE ALLOWLIST\",\n gloss: \"a scenario reaches these now, so delete them before the list rots\",\n count: report.staleAllowlist.length,\n lines: report.staleAllowlist,\n hint: `delete these keys from ${displayPath(report.allowlistPath)}`,\n });\n }\n\n if (report.staleUnreadAllowlist.length > 0) {\n sections.push({\n title: \"STALE UNREAD ALLOWLIST\",\n gloss: \"the extractor reads these now, so delete them before the list rots\",\n count: report.staleUnreadAllowlist.length,\n lines: report.staleUnreadAllowlist,\n hint: `delete these keys from ${displayPath(report.unreadAllowlistPath)}`,\n });\n }\n\n if (report.unresolved.length > 0) {\n sections.push(unreadSection(report.unresolved, report.unreadAllowlistPath));\n }\n\n return sections;\n}\n\n/**\n * Call sites the extractor could not read. Reported with file and line, never\n * dropped: an inventory that silently omitted what it failed to parse would\n * understate the denominator, and every number built on it would claim a\n * completeness it never had.\n *\n * The allowlist key is spelled out because it is `file#reason#site` and the\n * site is a hash — not the line the reader is looking at — so leaving them to\n * infer it guarantees a wrong guess and an entry that never matches.\n */\nexport function unreadSection(\n entries: Array<Parameters<typeof unreadKey>[0]>,\n allowlistPath?: string,\n): FindingSection {\n return {\n title: \"UNREAD CALL SITES\",\n gloss: \"the catalog is incomplete, so every count above is a floor\",\n count: entries.length,\n lines: entries.flatMap((entry) => [\n `${entry.origin.file}:${entry.origin.line}`,\n ` ${entry.note ?? \"the extractor could not read this call site\"}`,\n ` allowlist key: ${unreadKey(entry)}`,\n ]),\n hint: allowlistPath\n ? `make the call site readable, or accept each key in ${displayPath(allowlistPath)}`\n : \"make the call site readable, or accept each key in .agent-surface/unresolved-allow.json\",\n };\n}\n\n/**\n * A scenario the config declares whose mount threw.\n *\n * A finding like any other, and it has to look like one: this is the class that\n * invalidates every count in the report, so a reader must not have to notice\n * that it was printed in a different style from the findings above it.\n */\nfunction firstUsefulStackFrame(stack: string | undefined): string | undefined {\n if (!stack) return undefined;\n const frames = stack\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.startsWith(\"at \"));\n return frames.find((line) => !line.includes(\"node_modules\")) ?? frames[0];\n}\n\nfunction failureLines(failure: ScenarioFailure): string[] {\n const message = failure.message.trim() || \"Unknown scenario failure (no message)\";\n const componentFrames = (failure.componentStack ?? \"\")\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean)\n .slice(0, 6);\n const frame = firstUsefulStackFrame(failure.stack);\n return [\n failure.scenario,\n ` ${message}`,\n ...(failure.cause ? [` caused by: ${failure.cause}`] : []),\n ...(componentFrames.length > 0\n ? [\" React component stack:\", ...componentFrames.map((line) => ` ${line}`)]\n : []),\n ...(frame ? [` ${frame}`] : []),\n ];\n}\n\nexport function failureSection(failures: ScenarioFailure[]): FindingSection {\n return {\n title: \"DID NOT MOUNT\",\n gloss: \"these scenarios threw, and were skipped\",\n count: failures.length,\n lines: failures.flatMap(failureLines),\n };\n}\n\n/**\n * Why there is no coverage verdict. Never silence: a reader who asked for the\n * complete answer and got a partial one has to be told which part is missing,\n * or the partial one reads as the complete one.\n *\n * The failures themselves are listed directly above by `failureSection`. This\n * says what their absence costs the rest of the report, and nothing else.\n */\nexport function noVerdictSection(failures: ScenarioFailure[]): FindingSection {\n return {\n title: \"NO COVERAGE VERDICT\",\n gloss: \"a scenario did not mount, so nothing reached anything\",\n count: failures.length,\n lines: [\n \"Every capability those scenarios would have surfaced would be reported unreached,\",\n \"so no verdict is printed at all. Fix the mount, or name a scenario that works.\",\n ],\n };\n}\n\n/**\n * Mounted, and callable in no scenario. A finding `inspect` reports and the\n * gate does not: unlike an unreached capability, this one *is* covered by a\n * scenario — what is missing is a scenario that puts the app in the state, or\n * under the authority, where it can be used. That is a judgement about the\n * scenarios rather than a defect in the surface.\n *\n * Worth printing only when more than one scenario ran. Over a single scenario\n * \"never callable\" is the same statement its own table already made, one line\n * per capability, and a report that says everything twice is read once.\n */\nexport function neverCallableSection(entries: CapabilityReach[]): FindingSection {\n const stuck = entries.filter((entry) => entry.best === \"disable\").length;\n const hidden = entries.length - stuck;\n return {\n title: \"NEVER CALLABLE\",\n gloss: \"every scenario mounted these, and none of them could call one\",\n count: entries.length,\n tone: \"notice\",\n headers: [\"CAPABILITY\", \"BEST STATE\", \"WHY\"],\n rows: entries.map((entry) => ({\n cells: [\n entry.capabilityId,\n entry.best === \"disable\" ? \"disabled\" : \"hidden\",\n entry.best === \"disable\"\n ? (entry.note ?? \"the UI reported it unavailable in every scenario\")\n : \"a policy hid it in every scenario\",\n ],\n })),\n hint: [\n ...(stuck > 0\n ? [\n \"add a scenario that reaches the state these need — an open drawer, a filled list, \" +\n \"a selected row\",\n ]\n : []),\n ...(hidden > 0\n ? [\n `add a scenario whose consumer carries the authority ${\n stuck > 0 ? \"the hidden ones are\" : \"these are\"\n } waiting for`,\n ]\n : []),\n ].join(\"; \"),\n };\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","name","notes","relative","relative"]}
@@ -0,0 +1,61 @@
1
+ import {
2
+ normalize
3
+ } from "./chunk-GYYWHZPM.js";
4
+ import {
5
+ buildView,
6
+ flatRows
7
+ } from "./chunk-NFK3XWWH.js";
8
+
9
+ // src/report.ts
10
+ import { basename, relative } from "path";
11
+ function scenarioReport(result, options = {}) {
12
+ const view = buildView(result, {
13
+ ...options.attribution ? { explain: true } : {},
14
+ ...options.schemas ? { schemas: true } : {}
15
+ });
16
+ const capabilities = flatRows(view);
17
+ return {
18
+ scenario: result.scenario,
19
+ ...result.scope ? { scope: result.scope } : {},
20
+ snapshot: normalize(result.snapshot),
21
+ // Includes expose, disable and hide. Rows never contain runtime ids.
22
+ capabilities,
23
+ rejections: [...result.rejections].sort(
24
+ (a, b) => a.componentType.localeCompare(b.componentType) || a.instanceId.localeCompare(b.instanceId) || a.reason.localeCompare(b.reason)
25
+ ),
26
+ ...options.attribution ? { explanation: { capabilities } } : {}
27
+ };
28
+ }
29
+ function scenarioBaseline(result) {
30
+ const report = scenarioReport(result);
31
+ return {
32
+ ...report.snapshot,
33
+ capabilities: report.capabilities,
34
+ rejections: report.rejections
35
+ };
36
+ }
37
+ function inventoryReport(inventory, domainCapabilities) {
38
+ if (!inventory) return null;
39
+ return {
40
+ ...inventory,
41
+ root: ".",
42
+ tsconfig: relative(inventory.root, inventory.tsconfig) || "tsconfig.json",
43
+ ...domainCapabilities ? { domain: { source: "manifest", capabilities: [...domainCapabilities].sort() } } : {}
44
+ };
45
+ }
46
+ function coverageReport(report) {
47
+ if (!report) return null;
48
+ return {
49
+ ...report,
50
+ allowlistPath: basename(report.allowlistPath),
51
+ unreadAllowlistPath: basename(report.unreadAllowlistPath)
52
+ };
53
+ }
54
+
55
+ export {
56
+ scenarioReport,
57
+ scenarioBaseline,
58
+ inventoryReport,
59
+ coverageReport
60
+ };
61
+ //# sourceMappingURL=chunk-Y2LSPEVK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/report.ts"],"sourcesContent":["import { basename, relative } from \"node:path\";\nimport type { CollectResult } from \"./collect.js\";\nimport type { CoverageReport } from \"./coverage.js\";\nimport type { CapabilityInventory } from \"./extract.js\";\nimport { normalize } from \"./baseline.js\";\nimport { buildView, flatRows, type CapabilityRow } from \"./render/model.js\";\n\n/** Stable, complete per-scenario document shared by JSON, baselines and check. */\nexport interface ScenarioReport {\n scenario: string;\n scope?: string[];\n snapshot: unknown;\n capabilities: CapabilityRow[];\n rejections: CollectResult[\"rejections\"];\n explanation?: { capabilities: CapabilityRow[] };\n}\n\nexport function scenarioReport(\n result: CollectResult,\n options: { attribution?: boolean; schemas?: boolean } = {},\n): ScenarioReport {\n const view = buildView(result, {\n ...(options.attribution ? { explain: true } : {}),\n ...(options.schemas ? { schemas: true } : {}),\n });\n const capabilities = flatRows(view);\n return {\n scenario: result.scenario,\n ...(result.scope ? { scope: result.scope } : {}),\n snapshot: normalize(result.snapshot),\n // Includes expose, disable and hide. Rows never contain runtime ids.\n capabilities,\n rejections: [...result.rejections].sort(\n (a, b) =>\n a.componentType.localeCompare(b.componentType) ||\n a.instanceId.localeCompare(b.instanceId) ||\n a.reason.localeCompare(b.reason),\n ),\n ...(options.attribution ? { explanation: { capabilities } } : {}),\n };\n}\n\n/** Baseline payload: same semantic document, without invocation-only labels. */\nexport function scenarioBaseline(result: CollectResult): Record<string, unknown> {\n const report = scenarioReport(result);\n return {\n ...(report.snapshot as Record<string, unknown>),\n capabilities: report.capabilities,\n rejections: report.rejections,\n };\n}\n\n/** Machine output must not contain checkout-specific absolute paths. */\nexport function inventoryReport(\n inventory: CapabilityInventory | undefined,\n domainCapabilities?: string[],\n): unknown {\n if (!inventory) return null;\n return {\n ...inventory,\n root: \".\",\n tsconfig: relative(inventory.root, inventory.tsconfig) || \"tsconfig.json\",\n ...(domainCapabilities\n ? { domain: { source: \"manifest\", capabilities: [...domainCapabilities].sort() } }\n : {}),\n };\n}\n\nexport function coverageReport(report: CoverageReport | undefined): unknown {\n if (!report) return null;\n return {\n ...report,\n allowlistPath: basename(report.allowlistPath),\n unreadAllowlistPath: basename(report.unreadAllowlistPath),\n };\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,UAAU,gBAAgB;AAiB5B,SAAS,eACd,QACA,UAAwD,CAAC,GACzC;AAChB,QAAM,OAAO,UAAU,QAAQ;AAAA,IAC7B,GAAI,QAAQ,cAAc,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,IAC/C,GAAI,QAAQ,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,EAC7C,CAAC;AACD,QAAM,eAAe,SAAS,IAAI;AAClC,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,IACjB,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,IAC9C,UAAU,UAAU,OAAO,QAAQ;AAAA;AAAA,IAEnC;AAAA,IACA,YAAY,CAAC,GAAG,OAAO,UAAU,EAAE;AAAA,MACjC,CAAC,GAAG,MACF,EAAE,cAAc,cAAc,EAAE,aAAa,KAC7C,EAAE,WAAW,cAAc,EAAE,UAAU,KACvC,EAAE,OAAO,cAAc,EAAE,MAAM;AAAA,IACnC;AAAA,IACA,GAAI,QAAQ,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,IAAI,CAAC;AAAA,EACjE;AACF;AAGO,SAAS,iBAAiB,QAAgD;AAC/E,QAAM,SAAS,eAAe,MAAM;AACpC,SAAO;AAAA,IACL,GAAI,OAAO;AAAA,IACX,cAAc,OAAO;AAAA,IACrB,YAAY,OAAO;AAAA,EACrB;AACF;AAGO,SAAS,gBACd,WACA,oBACS;AACT,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM;AAAA,IACN,UAAU,SAAS,UAAU,MAAM,UAAU,QAAQ,KAAK;AAAA,IAC1D,GAAI,qBACA,EAAE,QAAQ,EAAE,QAAQ,YAAY,cAAc,CAAC,GAAG,kBAAkB,EAAE,KAAK,EAAE,EAAE,IAC/E,CAAC;AAAA,EACP;AACF;AAEO,SAAS,eAAe,QAA6C;AAC1E,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,eAAe,SAAS,OAAO,aAAa;AAAA,IAC5C,qBAAqB,SAAS,OAAO,mBAAmB;AAAA,EAC1D;AACF;","names":[]}
package/dist/index.d.ts CHANGED
@@ -64,8 +64,9 @@ interface AuthoredCapability {
64
64
  * How much of this call site the extractor understood.
65
65
  *
66
66
  * `static` — identity and metadata both recovered from literals.
67
- * `partial` — identity resolved, some metadata dynamic. The common case: a
68
- * spread `instanceId`, or a description built from a template.
67
+ * `partial` — identity resolved, some metadata or runtime presence dynamic.
68
+ * The common case: a spread `instanceId`, a conditional capability, or a
69
+ * description built from a template.
69
70
  * `unresolved` — identity NOT resolved. Reported, never dropped.
70
71
  */
71
72
  resolution: "static" | "partial" | "unresolved";
@@ -1,21 +1,25 @@
1
- import "./chunk-IALBMW3R.js";
1
+ import {
2
+ createPresenter
3
+ } from "./chunk-A5UCBF7D.js";
2
4
  import {
3
5
  UsageError,
4
6
  isPlain,
5
7
  loadInk,
6
- write,
7
8
  writeError
8
- } from "./chunk-3AJ343NA.js";
9
+ } from "./chunk-GYYWHZPM.js";
9
10
  import {
11
+ READING_SOURCE,
10
12
  authoredIds,
13
+ catalogDetailParts,
14
+ catalogRows,
15
+ displayPath,
11
16
  extractCapabilities,
12
- findTsconfig,
13
- unresolved
14
- } from "./chunk-UGCLJ5JX.js";
17
+ findTsconfig
18
+ } from "./chunk-NFK3XWWH.js";
15
19
 
16
20
  // src/commands/init.tsx
17
21
  import { existsSync, writeFileSync } from "fs";
18
- import { join, relative } from "path";
22
+ import { join } from "path";
19
23
  import { jsx } from "react/jsx-runtime";
20
24
  var CONFIG_NAME = "agent-surface.config.tsx";
21
25
  var ENTRY_CANDIDATES = [
@@ -54,7 +58,7 @@ async function runInit(options) {
54
58
  const configPath = join(options.cwd, CONFIG_NAME);
55
59
  if (existsSync(configPath)) {
56
60
  throw new UsageError(
57
- `${relative(process.cwd(), configPath)} already exists \u2014 edit it, or delete it and re-run`
61
+ `${displayPath(configPath)} already exists \u2014 edit it, or delete it and re-run`
58
62
  );
59
63
  }
60
64
  const tsconfig = options.tsconfig ?? findTsconfig(options.cwd);
@@ -63,48 +67,66 @@ async function runInit(options) {
63
67
  `no tsconfig.json found from ${options.cwd} \u2014 agent-surface reads your TypeScript program to find registration call sites, and cannot do that without one`
64
68
  );
65
69
  }
70
+ const present = await createPresenter(options);
71
+ await present.wait(READING_SOURCE);
66
72
  const inventory = extractCapabilities({ root: options.cwd, tsconfig });
67
73
  const ids = authoredIds(inventory);
68
- const unread = unresolved(inventory);
69
- const components = new Set(
70
- [...ids].map((id) => id.replace(/^view:/, "").split(".").slice(0, -1).join("."))
71
- );
72
- write(`Read ${inventory.filesAnalyzed} file${inventory.filesAnalyzed === 1 ? "" : "s"} from ${relative(process.cwd(), tsconfig) || "tsconfig.json"}`);
73
- write("");
74
- write(` authored capabilities ${ids.size}`);
75
- write(` components ${components.size}`);
76
- write(` unread call sites ${unread.length}`);
74
+ const entry = ENTRY_CANDIDATES.find((candidate) => existsSync(join(options.cwd, candidate)));
75
+ const parts = [
76
+ {
77
+ kind: "blocks",
78
+ blocks: [
79
+ {
80
+ title: "SURFACE INIT",
81
+ rows: [
82
+ { label: "Tsconfig", text: displayPath(tsconfig) },
83
+ { label: "Config", text: `${displayPath(configPath)} \u2014 to be written` }
84
+ ]
85
+ },
86
+ { title: "STATIC CATALOG", rows: catalogRows(inventory) }
87
+ ]
88
+ },
89
+ ...ids.size > 0 ? catalogDetailParts(inventory) : []
90
+ ];
77
91
  if (ids.size === 0) {
78
- write("");
79
- write(
80
- "Nothing is annotated yet \u2014 that is the default, and it is the safe one: a capability exists only where someone wrote one. Start with `useAgentComponent` in a component that owns state worth acting on, then re-run this."
81
- );
82
- } else {
83
- write("");
84
- for (const component of [...components].sort()) write(` ${component}`);
92
+ parts.push({
93
+ kind: "note",
94
+ lines: [
95
+ "Nothing is annotated yet \u2014 that is the default, and it is the safe one: a capability",
96
+ "exists only where someone wrote one. Start with `useAgentComponent` in a component",
97
+ "that owns state worth acting on, then re-run this."
98
+ ]
99
+ });
85
100
  }
86
- const entry = ENTRY_CANDIDATES.find((candidate) => existsSync(join(options.cwd, candidate)));
87
- write("");
88
- write(`Write ${relative(process.cwd(), configPath)}?`);
89
- write(
90
- entry ? ` it will import from ./${entry}, which you will still have to wire into a mount()` : " no app entry found, so the import line is a placeholder you will have to point somewhere"
91
- );
101
+ parts.push({
102
+ kind: "note",
103
+ title: "SCAFFOLD",
104
+ lines: [
105
+ ` ${displayPath(configPath)}`,
106
+ entry ? ` imports ./${entry}, which you will still have to wire into a mount()` : " no app entry found, so the import line is a placeholder you will have to point somewhere"
107
+ ]
108
+ });
109
+ await present.emit(...parts);
92
110
  if (!options.yes) {
93
111
  const answered = await ask(options, `Write ${CONFIG_NAME}?`);
94
112
  if (!answered) {
95
- write("");
96
- write("Nothing written.");
113
+ await present.emit({ kind: "note", lines: ["Nothing written."] });
97
114
  return 0;
98
115
  }
99
116
  }
100
117
  writeFileSync(configPath, scaffold(entry), "utf8");
101
- write("");
102
- write(`wrote ${relative(process.cwd(), configPath)}`);
103
- write("");
104
- write("Next:");
105
- write(" 1. fill in mount() \u2014 it should call your existing composition root");
106
- write(" 2. `agent-surface inspect` to see what an agent can reach");
107
- write(" 3. `agent-surface snapshot` to commit the baseline, then `check` in CI");
118
+ await present.emit(
119
+ { kind: "note", lines: [`wrote ${displayPath(configPath)}`] },
120
+ {
121
+ kind: "steps",
122
+ title: "NEXT STEPS",
123
+ steps: [
124
+ "fill in mount() \u2014 it should call your existing composition root",
125
+ "`agent-surface inspect` to see what an agent can reach",
126
+ "`agent-surface snapshot` to commit the baseline, then `check` in CI"
127
+ ]
128
+ }
129
+ );
108
130
  return 0;
109
131
  }
110
132
  async function ask(options, question) {
@@ -139,4 +161,4 @@ async function ask(options, question) {
139
161
  export {
140
162
  runInit
141
163
  };
142
- //# sourceMappingURL=init-6XV64LK2.js.map
164
+ //# sourceMappingURL=init-BVZR6CRS.js.map