@agent-surface/cli 0.10.0 → 0.11.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.
- package/README.md +64 -23
- package/dist/bin.js +63 -41
- package/dist/bin.js.map +1 -1
- package/dist/check-BYQ34OVQ.js +122 -0
- package/dist/check-BYQ34OVQ.js.map +1 -0
- package/dist/chunk-DYDSJM7R.js +170 -0
- package/dist/chunk-DYDSJM7R.js.map +1 -0
- package/dist/{chunk-4AEQKM2X.js → chunk-GXXKZTQB.js} +261 -206
- package/dist/chunk-GXXKZTQB.js.map +1 -0
- package/dist/{chunk-FYEXHWGG.js → chunk-QIVOZAWX.js} +52 -2
- package/dist/chunk-QIVOZAWX.js.map +1 -0
- package/dist/chunk-RXX63JSL.js +298 -0
- package/dist/chunk-RXX63JSL.js.map +1 -0
- package/dist/index.d.ts +15 -1
- package/dist/init-LYYVXFEQ.js +141 -0
- package/dist/init-LYYVXFEQ.js.map +1 -0
- package/dist/{ink-HBPOQTRS.js → ink-P23VKP4H.js} +102 -33
- package/dist/ink-P23VKP4H.js.map +1 -0
- package/dist/inspect-3CBRKTDM.js +108 -0
- package/dist/inspect-3CBRKTDM.js.map +1 -0
- package/dist/snapshot-DJ22WCT4.js +59 -0
- package/dist/snapshot-DJ22WCT4.js.map +1 -0
- package/package.json +4 -4
- package/dist/capabilities-OLFYMHCL.js +0 -37
- package/dist/capabilities-OLFYMHCL.js.map +0 -1
- package/dist/check-IN4XKAND.js +0 -84
- package/dist/check-IN4XKAND.js.map +0 -1
- package/dist/chunk-4AEQKM2X.js.map +0 -1
- package/dist/chunk-A27Y7ALQ.js +0 -51
- package/dist/chunk-A27Y7ALQ.js.map +0 -1
- package/dist/chunk-FYEXHWGG.js.map +0 -1
- package/dist/chunk-ODUIFFPM.js +0 -104
- package/dist/chunk-ODUIFFPM.js.map +0 -1
- package/dist/coverage-HCHLJTDD.js +0 -133
- package/dist/coverage-HCHLJTDD.js.map +0 -1
- package/dist/ink-HBPOQTRS.js.map +0 -1
- package/dist/inspect-NJNB6CAS.js +0 -213
- package/dist/inspect-NJNB6CAS.js.map +0 -1
- package/dist/snapshot-JQAB73OV.js +0 -38
- package/dist/snapshot-JQAB73OV.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/extract.ts","../src/render/plain.ts"],"sourcesContent":["/**\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 { existsSync } 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 };\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 dynamic. The common case: a\n * spread `instanceId`, or a 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\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 * Program files skipped for living outside `root` — workspace packages the\n * app's tsconfig aliases in, typically the library's own source. Reported\n * rather than dropped silently: a boundary nobody can see is a boundary\n * nobody can check.\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\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/* ── small AST helpers ────────────────────────────────────────────────── */\n\nfunction calleeName(call: ts.CallExpression): string | undefined {\n if (ts.isIdentifier(call.expression)) return call.expression.text;\n if (ts.isPropertyAccessExpression(call.expression)) return call.expression.name.text;\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 }\n return undefined;\n}\n\nfunction hasSpread(object: ts.ObjectLiteralExpression): boolean {\n return object.properties.some((property) => ts.isSpreadAssignment(property));\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 };\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 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 spread inside the capability map can add capabilities this extractor\n // cannot name. That is an identity gap, not a metadata gap.\n if (ts.isSpreadAssignment(property)) {\n emit.push({\n capabilityId: `view:${componentType}.${UNRESOLVED_ID}`,\n kind,\n origin: emit.origin(property),\n resolution: \"unresolved\",\n note: `\\`${kind}s\\` on \"${componentType}\" spreads another object, which may contribute capabilities this inventory cannot name`,\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 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(call: ts.CallExpression, emit: Emitter, source: ts.SourceFile): void {\n const callee = calleeName(call);\n if (callee === undefined) return;\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 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 note: `${callee}() call site could not be read: ${resolved.note}`,\n });\n return;\n }\n\n const config = resolved.object;\n const type = literalText(propertyOf(config, \"type\"));\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\" && propertyOf(config, \"type\") === undefined) return;\n emit.push({\n capabilityId: UNRESOLVED_ID,\n kind: \"action\",\n origin: emit.origin(call),\n resolution: \"unresolved\",\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 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\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/**\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 for (const source of program.getSourceFiles()) {\n if (source.isDeclarationFile) continue;\n if (source.fileName.includes(\"/node_modules/\")) continue;\n // A tsconfig with workspace path aliases pulls the library's *own* source\n // into the program, where `registry.register(definition)` inside\n // `useAgentComponent` reads as an unresolvable registration call site. It\n // is not one: it is the implementation every real call site goes through.\n // The inventory covers the app the surface config points at, and says so\n // rather than quietly widening or narrowing.\n if (!isInside(root, 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 }),\n };\n\n const visit = (node: ts.Node): void => {\n if (ts.isCallExpression(node)) visitCall(node, emit, source);\n ts.forEachChild(node, visit);\n };\n visit(source);\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","import type { CapabilityRow, SurfaceView } from \"./model.js\";\nimport type { DiffEntry } from \"../baseline.js\";\nimport { formatValue } from \"../baseline.js\";\nimport { authoredIds, unresolved, type CapabilityInventory } from \"../extract.js\";\nimport type { CoverageReport } from \"../coverage.js\";\n\n/**\n * The no-colour, no-cursor rendering used when stdout is piped or when\n * `--plain`, `CI` or `NO_COLOR` is set. Same view model as the Ink UI, so the\n * two cannot disagree about what the surface contains.\n */\n\nconst MARK = { expose: \"+\", disable: \"~\", hide: \"-\" } as const;\n\nfunction renderRow(row: CapabilityRow, lines: string[]): void {\n const tags = row.tags.length > 0 ? ` [${row.tags.join(\", \")}]` : \"\";\n lines.push(` ${MARK[row.outcome]} ${row.name}${tags}`);\n lines.push(` ${row.description}`);\n if (row.reason) lines.push(` reason: ${row.reason}`);\n\n if (row.policies) {\n if (row.policies.length === 0) {\n lines.push(\" policies: none\");\n } else {\n for (const policy of row.policies) {\n const vote = policy.discovery\n ? policy.discovery.decision === \"disable\"\n ? `disable — ${policy.discovery.reason}`\n : policy.discovery.decision\n : \"no discovery hook\";\n const phases = policy.phases.length > 0 ? policy.phases.join(\"/\") : \"—\";\n const flags = [\n policy.threw ? \"THREW\" : \"\",\n policy.confirmationEscalation ? \"escalates-confirmation\" : \"\",\n ]\n .filter(Boolean)\n .join(\", \");\n lines.push(\n ` policy ${policy.name} (${policy.scope}, ${phases}): ${vote}${\n flags ? ` [${flags}]` : \"\"\n }`,\n );\n }\n }\n if (row.availability && !row.availability.available) {\n lines.push(\n ` availability: unavailable${\n row.availability.reason ? ` — ${row.availability.reason}` : \"\"\n }`,\n );\n }\n }\n\n if (row.schemas) {\n if (row.schemas.input !== undefined) {\n lines.push(` input: ${JSON.stringify(row.schemas.input)}`);\n }\n if (row.schemas.output !== undefined) {\n lines.push(` output: ${JSON.stringify(row.schemas.output)}`);\n }\n }\n}\n\n/**\n * The counts line, and everything it is relative to (`AS-CLI-007`).\n *\n * `hidden` is printed unconditionally. It is computed on every run — the\n * explanation is always collected — and suppressing it outside `--explain`\n * meant a surface with a policy-hidden half rendered as a complete one. The\n * *attribution* still needs `--explain`; only the count moved.\n */\nexport function renderCountsPlain(view: SurfaceView): string {\n return (\n `${view.counts.callable} callable, ${view.counts.disabled} visible-disabled, ` +\n `${view.counts.hidden} hidden` +\n (view.rejections.length > 0\n ? `, ${view.rejections.length} registration${view.rejections.length === 1 ? \"\" : \"s\"} rejected`\n : \"\")\n );\n}\n\nfunction renderRejections(view: SurfaceView, lines: string[]): void {\n if (view.rejections.length === 0) return;\n lines.push(\"\");\n lines.push(`rejected during mount (${view.rejections.length})`);\n for (const rejection of view.rejections) {\n const why =\n rejection.reason === \"duplicate\"\n ? \"duplicate — an earlier registration holds this key\"\n : \"guard — onRegister rejected this registration\";\n lines.push(` ! ${rejection.componentType} (${rejection.instanceId}) ${why}`);\n }\n}\n\nexport function renderSurfacePlain(view: SurfaceView): string {\n const lines: string[] = [];\n lines.push(\n `scenario ${view.scenario}${view.route ? ` route ${view.route}` : \"\"}${\n view.scope && view.scope.length > 0 ? ` scope ${view.scope.join(\" \")}` : \"\"\n }`,\n );\n lines.push(renderCountsPlain(view));\n\n renderRejections(view, lines);\n\n const populated = view.groups.filter((group) => group.rows.length > 0);\n if (populated.length === 0) {\n lines.push(\"\");\n // \"Nothing is registered\" is only true when nothing was hidden. Saying it\n // over a surface a policy emptied sends the reader to the wrong file.\n if (view.counts.hidden > 0) {\n lines.push(\n `Nothing is callable here — all ${view.counts.hidden} registered capabilities were hidden by policy.`,\n );\n if (!view.explained) lines.push(\"Re-run with --explain to see which policy hid them.\");\n } else {\n lines.push(\"Nothing is registered for this scenario — the agent has no surface here.\");\n if (!view.explained) {\n lines.push(\"Re-run with --explain to see whether a policy hid it.\");\n }\n }\n return lines.join(\"\\n\");\n }\n\n for (const group of populated) {\n lines.push(\"\");\n lines.push(`${group.heading} (${group.rows.length})`);\n for (const row of group.rows) renderRow(row, lines);\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * The static inventory (`AS-COVER-001…003`). The summary says \"upper bound\" in\n * so many words: 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 in here.\n * 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 renderInventoryPlain(inventory: CapabilityInventory): string {\n const lines: string[] = [];\n const resolved = inventory.capabilities.filter((c) => c.resolution !== \"unresolved\");\n const unresolvedEntries = unresolved(inventory);\n const ids = authoredIds(inventory);\n\n lines.push(\n `${ids.size} authored (upper bound), ${resolved.length} call site${\n resolved.length === 1 ? \"\" : \"s\"\n } across ${inventory.filesAnalyzed} file${inventory.filesAnalyzed === 1 ? \"\" : \"s\"}`,\n );\n lines.push(\"domain: not analyzed — domain capabilities come from the oRPC router (OQ-1)\");\n if (inventory.filesOutsideRoot > 0) {\n // Relative, not absolute: plain output is byte-stable across runs\n // (`AS-CLI-003`), and an absolute path makes it machine-specific the moment\n // two people diff a CI log.\n lines.push(\n `${inventory.filesOutsideRoot} program file${\n inventory.filesOutsideRoot === 1 ? \"\" : \"s\"\n } outside the config's directory were not analyzed`,\n );\n }\n\n const byId = [...resolved].sort((a, b) => a.capabilityId.localeCompare(b.capabilityId));\n if (byId.length > 0) {\n lines.push(\"\");\n for (const capability of byId) {\n const mark = capability.resolution === \"static\" ? \" \" : \"~\";\n lines.push(` ${mark} ${capability.capabilityId}`);\n lines.push(` ${capability.origin.file}:${capability.origin.line} [${capability.kind}]`);\n if (capability.description) lines.push(` ${capability.description}`);\n if (capability.note) lines.push(` partial: ${capability.note}`);\n }\n }\n\n if (unresolvedEntries.length > 0) {\n lines.push(\"\");\n lines.push(`unresolved (${unresolvedEntries.length})`);\n for (const capability of unresolvedEntries) {\n lines.push(` ? ${capability.origin.file}:${capability.origin.line}`);\n lines.push(` ${capability.note ?? \"the extractor could not read this call site\"}`);\n }\n lines.push(\"\");\n lines.push(\n `${unresolvedEntries.length} call site${\n unresolvedEntries.length === 1 ? \"\" : \"s\"\n } could not be resolved — fix them, or re-run with --allow-unresolved to accept the gap`,\n );\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * The coverage report (`AS-COVER-004…005`). `unreached` is the finding this\n * command exists for; the other two buckets are reported rather than buried,\n * for the same reason the inventory reports what it could not parse.\n */\nexport function renderCoveragePlain(report: CoverageReport): string {\n const lines: string[] = [];\n lines.push(\n `${report.authored} authored (upper bound), ${report.reached} reached across ${\n report.scenarios.length\n } scenario${report.scenarios.length === 1 ? \"\" : \"s\"} (${report.scenarios.join(\", \")})`,\n );\n\n if (report.unreached.length > 0) {\n lines.push(\"\");\n lines.push(`unreached (${report.unreached.length})`);\n for (const entry of report.unreached) {\n lines.push(` ${entry.capabilityId}`);\n lines.push(` ${entry.origin.file}:${entry.origin.line} — no scenario mounts it`);\n }\n }\n\n if (report.domainReached.length > 0) {\n lines.push(\"\");\n lines.push(\n `domain (not analyzed) (${report.domainReached.length}) — reached, and outside this inventory by design`,\n );\n for (const id of report.domainReached) lines.push(` ${id}`);\n }\n\n if (report.undeclared.length > 0) {\n lines.push(\"\");\n lines.push(`undeclared (${report.undeclared.length})`);\n lines.push(\" present at runtime with no static origin — a dynamic registration, or a gap here\");\n for (const id of report.undeclared) lines.push(` ${id}`);\n }\n\n if (report.unresolved.length > 0) {\n lines.push(\"\");\n lines.push(`unresolved (${report.unresolved.length})`);\n for (const capability of report.unresolved) {\n lines.push(` ? ${capability.origin.file}:${capability.origin.line}`);\n lines.push(` ${capability.note ?? \"the extractor could not read this call site\"}`);\n }\n }\n\n if (report.allowed.length > 0) {\n lines.push(\"\");\n lines.push(\n `${report.allowed.length} unreached capabilit${\n report.allowed.length === 1 ? \"y is\" : \"ies are\"\n } allowlisted in ${report.allowlistPath}`,\n );\n }\n\n if (report.staleAllowlist.length > 0) {\n lines.push(\"\");\n lines.push(`stale allowlist entries (${report.staleAllowlist.length})`);\n lines.push(\" these are reached now — delete them so the list cannot silently rot\");\n for (const id of report.staleAllowlist) lines.push(` ${id}`);\n }\n\n lines.push(\"\");\n // Each bucket gets its own remedy. \"Add a scenario, or delete the component\"\n // is the right advice for an unreached capability and useless advice for a\n // call site the extractor could not read.\n const verdicts: string[] = [];\n if (report.unreached.length > 0) {\n verdicts.push(\n `surface coverage gap in ${report.unreached.length} capabilit${\n report.unreached.length === 1 ? \"y\" : \"ies\"\n } — add a scenario, or delete the component`,\n );\n }\n if (report.unresolved.length > 0) {\n verdicts.push(\n `${report.unresolved.length} call site${\n report.unresolved.length === 1 ? \"\" : \"s\"\n } could not be read, so this report is incomplete — fix them, or accept the gap knowingly`,\n );\n }\n if (report.staleAllowlist.length > 0) {\n verdicts.push(\n `${report.staleAllowlist.length} allowlist entr${\n report.staleAllowlist.length === 1 ? \"y is\" : \"ies are\"\n } stale — remove them so the list cannot silently rot`,\n );\n }\n if (verdicts.length === 0) {\n verdicts.push(\n report.allowed.length > 0\n ? \"no new surface coverage gaps — the allowlist still holds the known ones\"\n : \"every authored capability is reached by a scenario\",\n );\n }\n lines.push(...verdicts);\n return lines.join(\"\\n\");\n}\n\nexport function renderDiffPlain(scenario: string, entries: DiffEntry[]): string {\n const lines = [`${scenario}: ${entries.length} change${entries.length === 1 ? \"\" : \"s\"}`];\n for (const entry of entries) {\n const where = entry.subject ? `${entry.subject} (${entry.path})` : entry.path;\n if (entry.kind === \"added\") lines.push(` + ${where} ${formatValue(entry.after)}`);\n else if (entry.kind === \"removed\") lines.push(` - ${where} ${formatValue(entry.before)}`);\n else {\n lines.push(` ~ ${where}`);\n lines.push(` before: ${formatValue(entry.before)}`);\n lines.push(` after: ${formatValue(entry.after)}`);\n }\n }\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-A27Y7ALQ.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
// src/output.ts
|
|
2
|
-
function isPlain(flags) {
|
|
3
|
-
if (flags.json) return true;
|
|
4
|
-
if (flags.plain) return true;
|
|
5
|
-
if (process.env["CI"]) return true;
|
|
6
|
-
if (process.env["NO_COLOR"]) return true;
|
|
7
|
-
if (process.stdout.isTTY !== true) return true;
|
|
8
|
-
return !process.stdout.columns;
|
|
9
|
-
}
|
|
10
|
-
function write(text) {
|
|
11
|
-
process.stdout.write(`${text}
|
|
12
|
-
`);
|
|
13
|
-
}
|
|
14
|
-
function writeError(text) {
|
|
15
|
-
process.stderr.write(`${text}
|
|
16
|
-
`);
|
|
17
|
-
}
|
|
18
|
-
var cached;
|
|
19
|
-
async function loadInk() {
|
|
20
|
-
if (cached !== void 0) return cached;
|
|
21
|
-
try {
|
|
22
|
-
cached = await import("./ink-HBPOQTRS.js");
|
|
23
|
-
} catch {
|
|
24
|
-
cached = null;
|
|
25
|
-
}
|
|
26
|
-
return cached;
|
|
27
|
-
}
|
|
28
|
-
async function paint(element) {
|
|
29
|
-
const { render } = await import("ink");
|
|
30
|
-
const instance = render(element);
|
|
31
|
-
instance.unmount();
|
|
32
|
-
await instance.waitUntilExit();
|
|
33
|
-
}
|
|
34
|
-
async function transient(element) {
|
|
35
|
-
const { render } = await import("ink");
|
|
36
|
-
const instance = render(element);
|
|
37
|
-
return () => {
|
|
38
|
-
instance.clear();
|
|
39
|
-
instance.unmount();
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export {
|
|
44
|
-
isPlain,
|
|
45
|
-
write,
|
|
46
|
-
writeError,
|
|
47
|
-
loadInk,
|
|
48
|
-
paint,
|
|
49
|
-
transient
|
|
50
|
-
};
|
|
51
|
-
//# sourceMappingURL=chunk-A27Y7ALQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/output.ts"],"sourcesContent":["import type { ReactElement } from \"react\";\n\nexport interface OutputFlags {\n plain?: boolean;\n json?: boolean;\n}\n\n/**\n * Terminal-aware only when there is a terminal. Piped output, `--plain`, `CI`\n * and `NO_COLOR` all fall back to plain text — a CLI whose output changes shape\n * when redirected is unusable in a build log.\n */\nexport function isPlain(flags: OutputFlags): boolean {\n if (flags.json) return true;\n if (flags.plain) return true;\n if (process.env[\"CI\"]) return true;\n if (process.env[\"NO_COLOR\"]) return true;\n if (process.stdout.isTTY !== true) return true;\n // A TTY that cannot report its width (some CI ptys, `script` on macOS) makes\n // Ink lay out at zero columns and emit one character per line. Plain text is\n // the only honest rendering for a terminal whose size is unknown.\n return !process.stdout.columns;\n}\n\nexport function write(text: string): void {\n process.stdout.write(`${text}\\n`);\n}\n\nexport function writeError(text: string): void {\n process.stderr.write(`${text}\\n`);\n}\n\ntype InkModule = typeof import(\"./render/ink.js\");\n\nlet cached: InkModule | null | undefined;\n\n/**\n * Loads the Ink renderer, or returns `null` when it cannot run here.\n *\n * Two reasons this is lazy rather than a top-level import. It keeps `--plain`\n * and `--json` from paying for a terminal UI they never draw — and Ink drives\n * React through `react-reconciler`, which reads React 19 internals, so a host\n * that pins React 18 globally cannot load it at all. Neither is a reason to\n * fail a command that was about to print text.\n */\nexport async function loadInk(): Promise<InkModule | null> {\n if (cached !== undefined) return cached;\n try {\n cached = await import(\"./render/ink.js\");\n } catch {\n cached = null;\n }\n return cached;\n}\n\n/** Paints an Ink element once and returns when the frame has been flushed. */\nexport async function paint(element: ReactElement): Promise<void> {\n const { render } = await import(\"ink\");\n const instance = render(element);\n instance.unmount();\n await instance.waitUntilExit();\n}\n\n/** A live Ink frame (spinner) that is cleared before the real output lands. */\nexport async function transient(element: ReactElement): Promise<() => void> {\n const { render } = await import(\"ink\");\n const instance = render(element);\n return () => {\n instance.clear();\n instance.unmount();\n };\n}\n"],"mappings":";AAYO,SAAS,QAAQ,OAA6B;AACnD,MAAI,MAAM,KAAM,QAAO;AACvB,MAAI,MAAM,MAAO,QAAO;AACxB,MAAI,QAAQ,IAAI,IAAI,EAAG,QAAO;AAC9B,MAAI,QAAQ,IAAI,UAAU,EAAG,QAAO;AACpC,MAAI,QAAQ,OAAO,UAAU,KAAM,QAAO;AAI1C,SAAO,CAAC,QAAQ,OAAO;AACzB;AAEO,SAAS,MAAM,MAAoB;AACxC,UAAQ,OAAO,MAAM,GAAG,IAAI;AAAA,CAAI;AAClC;AAEO,SAAS,WAAW,MAAoB;AAC7C,UAAQ,OAAO,MAAM,GAAG,IAAI;AAAA,CAAI;AAClC;AAIA,IAAI;AAWJ,eAAsB,UAAqC;AACzD,MAAI,WAAW,OAAW,QAAO;AACjC,MAAI;AACF,aAAS,MAAM,OAAO,mBAAiB;AAAA,EACzC,QAAQ;AACN,aAAS;AAAA,EACX;AACA,SAAO;AACT;AAGA,eAAsB,MAAM,SAAsC;AAChE,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK;AACrC,QAAM,WAAW,OAAO,OAAO;AAC/B,WAAS,QAAQ;AACjB,QAAM,SAAS,cAAc;AAC/B;AAGA,eAAsB,UAAU,SAA4C;AAC1E,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK;AACrC,QAAM,WAAW,OAAO,OAAO;AAC/B,SAAO,MAAM;AACX,aAAS,MAAM;AACf,aAAS,QAAQ;AAAA,EACnB;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/load.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { createServer, type ViteDevServer } from \"vite\";\nimport { ViteNodeServer } from \"vite-node/server\";\nimport { ViteNodeRunner } from \"vite-node/client\";\nimport { installSourcemapsSupport } from \"vite-node/source-map\";\nimport type { CollectOptions, CollectResult } from \"./collect.js\";\nimport type { SurfaceConfig } from \"./config.js\";\n\nconst CONFIG_NAMES = [\n \"agent-surface.config.tsx\",\n \"agent-surface.config.ts\",\n \"agent-surface.config.mjs\",\n \"agent-surface.config.js\",\n];\n\n/** Walks up from `from` looking for an `agent-surface.config.*`. */\nexport function findConfig(from: string = process.cwd()): string | undefined {\n let dir = resolve(from);\n for (;;) {\n for (const name of CONFIG_NAMES) {\n const candidate = join(dir, name);\n if (existsSync(candidate)) return candidate;\n }\n const parent = dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\n/** `dist/collect.js` when installed; `src/collect.ts` when run from source. */\nfunction collectorPath(): string {\n for (const ext of [\"js\", \"ts\"]) {\n const candidate = fileURLToPath(new URL(`./collect.${ext}`, import.meta.url));\n if (existsSync(candidate)) return candidate;\n }\n throw new Error(\"could not locate the agent-surface collector module\");\n}\n\nexport interface SurfaceRunner {\n config: SurfaceConfig;\n scenarioNames: string[];\n collect(options: CollectOptions): Promise<CollectResult>;\n close(): Promise<void>;\n}\n\n/**\n * Boots a Vite dev server on the app's own config, so the config file and the\n * app modules it imports are transformed and resolved exactly as the app\n * resolves them — its aliases, its plugins, its TSX.\n */\nexport async function createSurfaceRunner(configPath: string): Promise<SurfaceRunner> {\n const absoluteConfig = isAbsolute(configPath) ? configPath : resolve(configPath);\n if (!existsSync(absoluteConfig)) {\n throw new Error(`config not found: ${absoluteConfig}`);\n }\n const root = dirname(absoluteConfig);\n\n let server: ViteDevServer;\n try {\n server = await createServer({\n root,\n logLevel: \"error\",\n // `serve` so plugins behave as they do in dev; nothing is ever served.\n server: { middlewareMode: true, watch: null, fs: { strict: false } },\n optimizeDeps: { noDiscovery: true, include: [] },\n resolve: {\n // Both halves of the graph must agree on these. React because two\n // copies break hooks; core because `explainSurface` finds the registry\n // through a Symbol, which is per-module-instance (see collect.ts).\n dedupe: [\n \"react\",\n \"react-dom\",\n \"@agent-surface/core\",\n \"@agent-surface/react\",\n \"@agent-surface/testing\",\n ],\n },\n });\n } catch (error) {\n throw new Error(\n `could not start Vite for ${root}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n try {\n await server.pluginContainer.buildStart({});\n } catch {\n // Vite keeps moving this; a plugin that needs buildStart will say so itself.\n }\n\n const nodeServer = new ViteNodeServer(server);\n installSourcemapsSupport({ getSourceMap: (source) => nodeServer.getSourceMap(source) });\n\n const runner = new ViteNodeRunner({\n root: server.config.root,\n base: server.config.base,\n fetchModule: (id) => nodeServer.fetchModule(id),\n resolveId: (id, importer) => nodeServer.resolveId(id, importer),\n });\n\n const close = async (): Promise<void> => {\n await server.close();\n };\n\n try {\n const configModule = (await runner.executeFile(absoluteConfig)) as {\n default?: SurfaceConfig;\n };\n const config = configModule.default;\n if (!config || typeof config.mount !== \"function\") {\n throw new Error(\n `${absoluteConfig} must \\`export default defineSurface({ mount, scenarios })\\``,\n );\n }\n const scenarioNames = Object.keys(config.scenarios ?? {});\n if (scenarioNames.length === 0) {\n throw new Error(`${absoluteConfig} defines no scenarios`);\n }\n\n // Same runner ⇒ same module graph ⇒ the collector shares React and core\n // with the app tree it is about to mount.\n const collector = (await runner.executeFile(collectorPath())) as {\n collect(config: SurfaceConfig, options: CollectOptions): Promise<CollectResult>;\n };\n\n return {\n config,\n scenarioNames,\n collect: async (options) => {\n // Scoped to the mount, never process-wide: `act()` needs it, and Ink\n // renders its own React tree afterwards — with the flag still set,\n // every frame of the CLI's own UI prints React's \"not wrapped in\n // act(...)\" warning at the user.\n const globals = globalThis as Record<string, unknown>;\n const previous = globals[\"IS_REACT_ACT_ENVIRONMENT\"];\n globals[\"IS_REACT_ACT_ENVIRONMENT\"] = true;\n try {\n return await collector.collect(config, options);\n } finally {\n globals[\"IS_REACT_ACT_ENVIRONMENT\"] = previous;\n }\n },\n close,\n };\n } catch (error) {\n await close();\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-ODUIFFPM.js
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
// src/baseline.ts
|
|
2
|
-
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
-
import { dirname, join, resolve } from "path";
|
|
4
|
-
import { serializeSurfaceSnapshot } from "@agent-surface/testing";
|
|
5
|
-
var DEFAULT_BASELINE_DIR = ".agent-surface";
|
|
6
|
-
function baselineDirFor(configPath, configured) {
|
|
7
|
-
return resolve(dirname(configPath), configured ?? DEFAULT_BASELINE_DIR);
|
|
8
|
-
}
|
|
9
|
-
function baselinePath(dir, scenario) {
|
|
10
|
-
return join(dir, `${scenario}.json`);
|
|
11
|
-
}
|
|
12
|
-
function normalize(snapshot) {
|
|
13
|
-
return serializeSurfaceSnapshot(snapshot);
|
|
14
|
-
}
|
|
15
|
-
function readBaseline(path) {
|
|
16
|
-
try {
|
|
17
|
-
return JSON.parse(readFileSync(path, "utf8"));
|
|
18
|
-
} catch {
|
|
19
|
-
return void 0;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
function writeBaseline(path, value) {
|
|
23
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
24
|
-
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
25
|
-
`, "utf8");
|
|
26
|
-
}
|
|
27
|
-
var PATH_SEGMENT = /([^.[\]]+)|\[(\d+)\]/g;
|
|
28
|
-
function subjectFor(document, path) {
|
|
29
|
-
let node = document;
|
|
30
|
-
let subject;
|
|
31
|
-
for (const match of path.matchAll(PATH_SEGMENT)) {
|
|
32
|
-
if (typeof node !== "object" || node === null) return subject;
|
|
33
|
-
const record = node;
|
|
34
|
-
const candidate = record["capabilityId"] ?? record["procedureId"];
|
|
35
|
-
if (typeof candidate === "string") subject = candidate;
|
|
36
|
-
const key = match[1] ?? match[2];
|
|
37
|
-
if (key === void 0) return subject;
|
|
38
|
-
node = record[key];
|
|
39
|
-
}
|
|
40
|
-
if (typeof node === "object" && node !== null) {
|
|
41
|
-
const record = node;
|
|
42
|
-
const candidate = record["capabilityId"] ?? record["procedureId"];
|
|
43
|
-
if (typeof candidate === "string") subject = candidate;
|
|
44
|
-
}
|
|
45
|
-
return subject;
|
|
46
|
-
}
|
|
47
|
-
function annotate(entries, after, before) {
|
|
48
|
-
return entries.map((entry) => {
|
|
49
|
-
const subject = subjectFor(after, entry.path) ?? subjectFor(before, entry.path);
|
|
50
|
-
return subject ? { ...entry, subject } : entry;
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
function diff(before, after, path = "") {
|
|
54
|
-
if (Object.is(before, after)) return [];
|
|
55
|
-
const bothArrays = Array.isArray(before) && Array.isArray(after);
|
|
56
|
-
const bothObjects = !bothArrays && typeof before === "object" && typeof after === "object" && before !== null && after !== null;
|
|
57
|
-
if (bothArrays) {
|
|
58
|
-
const entries = [];
|
|
59
|
-
const max = Math.max(before.length, after.length);
|
|
60
|
-
for (let i = 0; i < max; i++) {
|
|
61
|
-
const at = `${path}[${i}]`;
|
|
62
|
-
if (i >= before.length) entries.push({ path: at, kind: "added", after: after[i] });
|
|
63
|
-
else if (i >= after.length) entries.push({ path: at, kind: "removed", before: before[i] });
|
|
64
|
-
else entries.push(...diff(before[i], after[i], at));
|
|
65
|
-
}
|
|
66
|
-
return entries;
|
|
67
|
-
}
|
|
68
|
-
if (bothObjects) {
|
|
69
|
-
const entries = [];
|
|
70
|
-
const beforeRecord = before;
|
|
71
|
-
const afterRecord = after;
|
|
72
|
-
const keys = /* @__PURE__ */ new Set([...Object.keys(beforeRecord), ...Object.keys(afterRecord)]);
|
|
73
|
-
for (const key of [...keys].sort()) {
|
|
74
|
-
const at = path ? `${path}.${key}` : key;
|
|
75
|
-
if (!(key in beforeRecord)) {
|
|
76
|
-
entries.push({ path: at, kind: "added", after: afterRecord[key] });
|
|
77
|
-
} else if (!(key in afterRecord)) {
|
|
78
|
-
entries.push({ path: at, kind: "removed", before: beforeRecord[key] });
|
|
79
|
-
} else {
|
|
80
|
-
entries.push(...diff(beforeRecord[key], afterRecord[key], at));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return entries;
|
|
84
|
-
}
|
|
85
|
-
if (JSON.stringify(before) === JSON.stringify(after)) return [];
|
|
86
|
-
return [{ path: path || "<root>", kind: "changed", before, after }];
|
|
87
|
-
}
|
|
88
|
-
function formatValue(value) {
|
|
89
|
-
if (value === void 0) return "\u2014";
|
|
90
|
-
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
91
|
-
return text.length > 120 ? `${text.slice(0, 117)}\u2026` : text;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export {
|
|
95
|
-
baselineDirFor,
|
|
96
|
-
baselinePath,
|
|
97
|
-
normalize,
|
|
98
|
-
readBaseline,
|
|
99
|
-
writeBaseline,
|
|
100
|
-
annotate,
|
|
101
|
-
diff,
|
|
102
|
-
formatValue
|
|
103
|
-
};
|
|
104
|
-
//# sourceMappingURL=chunk-ODUIFFPM.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/baseline.ts"],"sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { serializeSurfaceSnapshot } from \"@agent-surface/testing\";\nimport type { AgentSurfaceSnapshot } from \"@agent-surface/core\";\n\nexport const DEFAULT_BASELINE_DIR = \".agent-surface\";\n\nexport function baselineDirFor(configPath: string, configured?: string): string {\n return resolve(dirname(configPath), configured ?? DEFAULT_BASELINE_DIR);\n}\n\nexport function baselinePath(dir: string, scenario: string): string {\n return join(dir, `${scenario}.json`);\n}\n\n/**\n * The committed form. `serializeSurfaceSnapshot` is the same normalizer the\n * Vitest matcher uses: registration ids become stable placeholders and the\n * volatile fields (surfaceId, capturedAt, version) drop out, so a baseline\n * diff is a diff of *what agents can see* and nothing else.\n */\nexport function normalize(snapshot: AgentSurfaceSnapshot): unknown {\n return serializeSurfaceSnapshot(snapshot);\n}\n\nexport function readBaseline(path: string): unknown | undefined {\n try {\n return JSON.parse(readFileSync(path, \"utf8\")) as unknown;\n } catch {\n return undefined;\n }\n}\n\nexport function writeBaseline(path: string, value: unknown): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, `${JSON.stringify(value, null, 2)}\\n`, \"utf8\");\n}\n\nexport interface DiffEntry {\n path: string;\n kind: \"added\" | \"removed\" | \"changed\";\n before?: unknown;\n after?: unknown;\n /**\n * The capability the change belongs to. A reviewer needs to know that\n * `view:devices.table.sort` changed — `components[3].actions[1]` is the same\n * fact in a form nobody can act on.\n */\n subject?: string;\n}\n\nconst PATH_SEGMENT = /([^.[\\]]+)|\\[(\\d+)\\]/g;\n\n/** Nearest enclosing capability id for a diff path, if the path sits inside one. */\nfunction subjectFor(document: unknown, path: string): string | undefined {\n let node: unknown = document;\n let subject: string | undefined;\n for (const match of path.matchAll(PATH_SEGMENT)) {\n if (typeof node !== \"object\" || node === null) return subject;\n const record = node as Record<string, unknown>;\n const candidate = record[\"capabilityId\"] ?? record[\"procedureId\"];\n if (typeof candidate === \"string\") subject = candidate;\n const key = match[1] ?? match[2];\n if (key === undefined) return subject;\n node = record[key];\n }\n if (typeof node === \"object\" && node !== null) {\n const record = node as Record<string, unknown>;\n const candidate = record[\"capabilityId\"] ?? record[\"procedureId\"];\n if (typeof candidate === \"string\") subject = candidate;\n }\n return subject;\n}\n\n/** Labels each entry with the capability it belongs to, when there is one. */\nexport function annotate(entries: DiffEntry[], after: unknown, before: unknown): DiffEntry[] {\n return entries.map((entry) => {\n const subject = subjectFor(after, entry.path) ?? subjectFor(before, entry.path);\n return subject ? { ...entry, subject } : entry;\n });\n}\n\n/**\n * Structural diff, deliberately total: every difference is drift, including a\n * changed description. Descriptions are the provider's cached prompt prefix\n * (D28) — a silent edit re-bills every conversation, so it is exactly the kind\n * of change a reviewer should see.\n */\nexport function diff(before: unknown, after: unknown, path = \"\"): DiffEntry[] {\n if (Object.is(before, after)) return [];\n\n const bothArrays = Array.isArray(before) && Array.isArray(after);\n const bothObjects =\n !bothArrays &&\n typeof before === \"object\" &&\n typeof after === \"object\" &&\n before !== null &&\n after !== null;\n\n if (bothArrays) {\n const entries: DiffEntry[] = [];\n const max = Math.max(before.length, after.length);\n for (let i = 0; i < max; i++) {\n const at = `${path}[${i}]`;\n if (i >= before.length) entries.push({ path: at, kind: \"added\", after: after[i] });\n else if (i >= after.length) entries.push({ path: at, kind: \"removed\", before: before[i] });\n else entries.push(...diff(before[i], after[i], at));\n }\n return entries;\n }\n\n if (bothObjects) {\n const entries: DiffEntry[] = [];\n const beforeRecord = before as Record<string, unknown>;\n const afterRecord = after as Record<string, unknown>;\n const keys = new Set([...Object.keys(beforeRecord), ...Object.keys(afterRecord)]);\n for (const key of [...keys].sort()) {\n const at = path ? `${path}.${key}` : key;\n if (!(key in beforeRecord)) {\n entries.push({ path: at, kind: \"added\", after: afterRecord[key] });\n } else if (!(key in afterRecord)) {\n entries.push({ path: at, kind: \"removed\", before: beforeRecord[key] });\n } else {\n entries.push(...diff(beforeRecord[key], afterRecord[key], at));\n }\n }\n return entries;\n }\n\n if (JSON.stringify(before) === JSON.stringify(after)) return [];\n return [{ path: path || \"<root>\", kind: \"changed\", before, after }];\n}\n\nexport function formatValue(value: unknown): string {\n if (value === undefined) return \"—\";\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n return text.length > 120 ? `${text.slice(0, 117)}…` : text;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
authoredIds,
|
|
3
|
-
extractCapabilities,
|
|
4
|
-
renderCoveragePlain,
|
|
5
|
-
unresolved
|
|
6
|
-
} from "./chunk-4AEQKM2X.js";
|
|
7
|
-
import {
|
|
8
|
-
createSurfaceRunner
|
|
9
|
-
} from "./chunk-FYEXHWGG.js";
|
|
10
|
-
import {
|
|
11
|
-
write
|
|
12
|
-
} from "./chunk-A27Y7ALQ.js";
|
|
13
|
-
import {
|
|
14
|
-
baselineDirFor
|
|
15
|
-
} from "./chunk-ODUIFFPM.js";
|
|
16
|
-
|
|
17
|
-
// src/commands/coverage.ts
|
|
18
|
-
import { dirname } from "path";
|
|
19
|
-
|
|
20
|
-
// src/coverage.ts
|
|
21
|
-
import { existsSync, readFileSync } from "fs";
|
|
22
|
-
import { join } from "path";
|
|
23
|
-
var ALLOWLIST_FILE = "coverage-allow.json";
|
|
24
|
-
function allowlistPathFor(baselineDir) {
|
|
25
|
-
return join(baselineDir, ALLOWLIST_FILE);
|
|
26
|
-
}
|
|
27
|
-
function readAllowlist(path) {
|
|
28
|
-
if (!existsSync(path)) return {};
|
|
29
|
-
let parsed;
|
|
30
|
-
try {
|
|
31
|
-
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
32
|
-
} catch (error) {
|
|
33
|
-
throw new Error(
|
|
34
|
-
`could not parse ${path}: ${error instanceof Error ? error.message : String(error)}`
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
38
|
-
throw new Error(`${path} must be a JSON object of { "capabilityId": "reason" }`);
|
|
39
|
-
}
|
|
40
|
-
const allowlist = {};
|
|
41
|
-
for (const [id, reason] of Object.entries(parsed)) {
|
|
42
|
-
if (typeof reason !== "string" || reason.trim() === "") {
|
|
43
|
-
throw new Error(`${path}: "${id}" needs a non-empty reason string`);
|
|
44
|
-
}
|
|
45
|
-
allowlist[id] = reason;
|
|
46
|
-
}
|
|
47
|
-
return allowlist;
|
|
48
|
-
}
|
|
49
|
-
function buildCoverageReport(input) {
|
|
50
|
-
const unreached = [];
|
|
51
|
-
const allowed = [];
|
|
52
|
-
for (const id of [...input.authored].sort()) {
|
|
53
|
-
if (input.reachedIds.has(id)) continue;
|
|
54
|
-
if (id in input.allowlist) {
|
|
55
|
-
allowed.push(id);
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
unreached.push({ capabilityId: id, origin: input.origins.get(id) ?? { file: "?", line: 0 } });
|
|
59
|
-
}
|
|
60
|
-
const staleAllowlist = Object.keys(input.allowlist).filter((id) => input.reachedIds.has(id) || !input.authored.has(id)).sort();
|
|
61
|
-
const unaccounted = [...input.reachedIds].filter((id) => !input.authored.has(id)).sort();
|
|
62
|
-
const domainReached = unaccounted.filter((id) => id.startsWith("domain:"));
|
|
63
|
-
const undeclared = unaccounted.filter((id) => !id.startsWith("domain:"));
|
|
64
|
-
return {
|
|
65
|
-
authored: input.authored.size,
|
|
66
|
-
reached: [...input.authored].filter((id) => input.reachedIds.has(id)).length,
|
|
67
|
-
scenarios: input.scenarios,
|
|
68
|
-
unreached,
|
|
69
|
-
undeclared,
|
|
70
|
-
domainReached,
|
|
71
|
-
unresolved: input.unresolved,
|
|
72
|
-
allowed,
|
|
73
|
-
staleAllowlist,
|
|
74
|
-
allowlistPath: input.allowlistPath
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
function coverageExitCode(report) {
|
|
78
|
-
if (report.unreached.length > 0) return 1;
|
|
79
|
-
if (report.unresolved.length > 0) return 1;
|
|
80
|
-
if (report.staleAllowlist.length > 0) return 1;
|
|
81
|
-
return 0;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// src/commands/coverage.ts
|
|
85
|
-
async function runCoverage(options) {
|
|
86
|
-
const root = dirname(options.configPath);
|
|
87
|
-
const inventory = extractCapabilities({
|
|
88
|
-
root,
|
|
89
|
-
...options.tsconfig ? { tsconfig: options.tsconfig } : {}
|
|
90
|
-
});
|
|
91
|
-
const authored = authoredIds(inventory);
|
|
92
|
-
const origins = /* @__PURE__ */ new Map();
|
|
93
|
-
for (const capability of inventory.capabilities) {
|
|
94
|
-
if (!origins.has(capability.capabilityId)) origins.set(capability.capabilityId, capability.origin);
|
|
95
|
-
}
|
|
96
|
-
const runner = await createSurfaceRunner(options.configPath);
|
|
97
|
-
try {
|
|
98
|
-
const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;
|
|
99
|
-
const reachedIds = /* @__PURE__ */ new Set();
|
|
100
|
-
for (const scenario of scenarios) {
|
|
101
|
-
const result = await runner.collect({
|
|
102
|
-
scenario,
|
|
103
|
-
...options.scope ? { scope: options.scope } : {}
|
|
104
|
-
});
|
|
105
|
-
for (const capability of result.explanation.capabilities) {
|
|
106
|
-
reachedIds.add(capability.capabilityId);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const dir = baselineDirFor(
|
|
110
|
-
options.configPath,
|
|
111
|
-
options.baselineDir ?? runner.config.baselineDir
|
|
112
|
-
);
|
|
113
|
-
const allowlistPath = allowlistPathFor(dir);
|
|
114
|
-
const report = buildCoverageReport({
|
|
115
|
-
authored,
|
|
116
|
-
origins,
|
|
117
|
-
reachedIds,
|
|
118
|
-
scenarios,
|
|
119
|
-
unresolved: unresolved(inventory),
|
|
120
|
-
allowlist: readAllowlist(allowlistPath),
|
|
121
|
-
allowlistPath
|
|
122
|
-
});
|
|
123
|
-
if (options.json) write(JSON.stringify(report, null, 2));
|
|
124
|
-
else write(renderCoveragePlain(report));
|
|
125
|
-
return coverageExitCode(report);
|
|
126
|
-
} finally {
|
|
127
|
-
await runner.close();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
export {
|
|
131
|
-
runCoverage
|
|
132
|
-
};
|
|
133
|
-
//# sourceMappingURL=coverage-HCHLJTDD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/coverage.ts","../src/coverage.ts"],"sourcesContent":["import { dirname } from \"node:path\";\nimport { createSurfaceRunner } from \"../load.js\";\nimport { baselineDirFor } from \"../baseline.js\";\nimport { authoredIds, extractCapabilities, unresolved } from \"../extract.js\";\nimport {\n allowlistPathFor,\n buildCoverageReport,\n coverageExitCode,\n readAllowlist,\n} from \"../coverage.js\";\nimport { renderCoveragePlain } from \"../render/plain.js\";\nimport { write } from \"../output.js\";\n\nexport interface CoverageOptions {\n configPath: string;\n scenario?: string;\n scope?: string[];\n tsconfig?: string;\n baselineDir?: string;\n json?: boolean;\n plain?: boolean;\n}\n\n/**\n * Joins the two halves: the static inventory (what is authored) against the\n * union of every scenario's explanation (what is reached).\n *\n * The join key is `capabilityId`, which is instance-independent by\n * construction — `instanceId` is not part of it — so two mounted instances of\n * one component collapse onto the one authored entry, which is what a coverage\n * question means.\n */\nexport async function runCoverage(options: CoverageOptions): Promise<number> {\n const root = dirname(options.configPath);\n const inventory = extractCapabilities({\n root,\n ...(options.tsconfig ? { tsconfig: options.tsconfig } : {}),\n });\n\n const authored = authoredIds(inventory);\n const origins = new Map<string, { file: string; line: number }>();\n for (const capability of inventory.capabilities) {\n if (!origins.has(capability.capabilityId)) origins.set(capability.capabilityId, capability.origin);\n }\n\n const runner = await createSurfaceRunner(options.configPath);\n try {\n const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;\n const reachedIds = new Set<string>();\n\n for (const scenario of scenarios) {\n const result = await runner.collect({\n scenario,\n ...(options.scope ? { scope: options.scope } : {}),\n });\n // Reached means present in the *explanation*, not the snapshot — see\n // BuildCoverageInput.reachedIds for why hiding still counts as reaching.\n for (const capability of result.explanation.capabilities) {\n reachedIds.add(capability.capabilityId);\n }\n }\n\n const dir = baselineDirFor(\n options.configPath,\n options.baselineDir ?? runner.config.baselineDir,\n );\n const allowlistPath = allowlistPathFor(dir);\n const report = buildCoverageReport({\n authored,\n origins,\n reachedIds,\n scenarios,\n unresolved: unresolved(inventory),\n allowlist: readAllowlist(allowlistPath),\n allowlistPath,\n });\n\n if (options.json) write(JSON.stringify(report, null, 2));\n else write(renderCoveragePlain(report));\n\n // AS-CLI-002's contract: 0 clean, 1 a gap, 2 usage.\n return coverageExitCode(report);\n } finally {\n await runner.close();\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\";\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 readAllowlist(path: string): 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 { \"capabilityId\": \"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\nexport interface UnreachedCapability {\n capabilityId: string;\n origin: { file: string; line: number };\n}\n\nexport interface CoverageReport {\n /** Distinct capability ids the inventory resolved. */\n authored: number;\n /** How many of them at least one scenario surfaced. */\n reached: number;\n scenarios: string[];\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 /** Carried forward from the inventory. */\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}\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 unresolved: AuthoredCapability[];\n allowlist: CoverageAllowlist;\n allowlistPath: string;\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 const unaccounted = [...input.reachedIds].filter((id) => !input.authored.has(id)).sort();\n const domainReached = unaccounted.filter((id) => id.startsWith(\"domain:\"));\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 unreached,\n undeclared,\n domainReached,\n unresolved: input.unresolved,\n allowed,\n staleAllowlist,\n allowlistPath: input.allowlistPath,\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 */\nexport function coverageExitCode(report: CoverageReport): number {\n if (report.unreached.length > 0) return 1;\n if (report.unresolved.length > 0) return 1;\n if (report.staleAllowlist.length > 0) return 1;\n return 0;\n}\n"],"mappings":"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","names":[]}
|
package/dist/ink-HBPOQTRS.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/render/ink.tsx"],"sourcesContent":["import type { ReactElement } from \"react\";\nimport { Box, Static, Text } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport type { CapabilityRow, CapabilityGroup, SurfaceView } from \"./model.js\";\nimport type { DiffEntry } from \"../baseline.js\";\nimport { formatValue } from \"../baseline.js\";\n\nconst OUTCOME = {\n expose: { mark: \"●\", color: \"green\" as const },\n disable: { mark: \"◐\", color: \"yellow\" as const },\n hide: { mark: \"○\", color: \"red\" as const },\n};\n\nexport function Loading({ label }: { label: string }): ReactElement {\n return (\n <Text>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n {` ${label}`}\n </Text>\n );\n}\n\nfunction PolicyLine({\n policy,\n}: {\n policy: NonNullable<CapabilityRow[\"policies\"]>[number];\n}): ReactElement {\n const vote = policy.discovery?.decision;\n const color = vote === \"hide\" ? \"red\" : vote === \"disable\" ? \"yellow\" : \"green\";\n return (\n <Box paddingLeft={6}>\n <Text dimColor>policy </Text>\n <Text bold>{policy.name}</Text>\n <Text dimColor>{` (${policy.scope}${policy.phases.length ? `, ${policy.phases.join(\"/\")}` : \"\"}) `}</Text>\n {vote ? (\n <Text color={color}>\n {vote}\n {policy.discovery?.decision === \"disable\" ? ` — ${policy.discovery.reason}` : \"\"}\n </Text>\n ) : (\n <Text dimColor>no discovery hook</Text>\n )}\n {policy.threw ? <Text color=\"red\" bold>{\" THREW\"}</Text> : null}\n {policy.confirmationEscalation ? (\n <Text color=\"magenta\">{\" escalates-confirmation\"}</Text>\n ) : null}\n </Box>\n );\n}\n\nfunction Capability({ row }: { row: CapabilityRow }): ReactElement {\n const outcome = OUTCOME[row.outcome];\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text color={outcome.color}>{` ${outcome.mark} `}</Text>\n <Text bold>{row.name}</Text>\n {row.tags.length > 0 ? <Text dimColor>{` ${row.tags.join(\" · \")}`}</Text> : null}\n </Box>\n <Box paddingLeft={4}>\n <Text dimColor wrap=\"wrap\">\n {row.description}\n </Text>\n </Box>\n {row.reason ? (\n <Box paddingLeft={4}>\n <Text color=\"yellow\" wrap=\"wrap\">{`⤷ ${row.reason}`}</Text>\n </Box>\n ) : null}\n {row.policies\n ? row.policies.length > 0\n ? row.policies.map((policy, index) => (\n <PolicyLine key={`${policy.name}-${index}`} policy={policy} />\n ))\n : [\n <Box key=\"none\" paddingLeft={6}>\n <Text dimColor>policies: none</Text>\n </Box>,\n ]\n : null}\n {row.policies && row.availability && !row.availability.available ? (\n <Box paddingLeft={6}>\n <Text dimColor>{`availability: unavailable${\n row.availability.reason ? ` — ${row.availability.reason}` : \"\"\n }`}</Text>\n </Box>\n ) : null}\n {row.schemas?.input !== undefined ? (\n <Box paddingLeft={6}>\n <Text dimColor wrap=\"wrap\">{`input: ${JSON.stringify(row.schemas.input)}`}</Text>\n </Box>\n ) : null}\n {row.schemas?.output !== undefined ? (\n <Box paddingLeft={6}>\n <Text dimColor wrap=\"wrap\">{`output: ${JSON.stringify(row.schemas.output)}`}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction Group({ group }: { group: CapabilityGroup }): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text backgroundColor=\"blueBright\" color=\"black\" bold>{` ${group.heading} `}</Text>\n <Text dimColor>{` ${group.rows.length}`}</Text>\n </Box>\n {group.rows.map((row) => (\n <Capability key={`${row.capabilityId}-${row.name}`} row={row} />\n ))}\n </Box>\n );\n}\n\n/**\n * The header states everything the counts are relative to (`AS-CLI-007`): the\n * scenario, the route, and the scope when one is active — a scope filters both\n * projections, so an unqualified count reads as a claim about the whole surface.\n * `hidden` is unconditional here for the same reason it is in plain text.\n */\nfunction Header({ view }: { view: SurfaceView }): ReactElement {\n return (\n <Box>\n <Text bold>{view.scenario}</Text>\n {view.route ? <Text dimColor>{` ${view.route}`}</Text> : null}\n {view.scope && view.scope.length > 0 ? (\n <Text color=\"cyan\">{` scope ${view.scope.join(\" \")}`}</Text>\n ) : null}\n <Text dimColor>{\" · \"}</Text>\n <Text color=\"green\">{`${view.counts.callable} callable`}</Text>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"yellow\">{`${view.counts.disabled} visible-disabled`}</Text>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"red\">{`${view.counts.hidden} hidden`}</Text>\n {view.rejections.length > 0 ? (\n <>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"magenta\">\n {`${view.rejections.length} registration${\n view.rejections.length === 1 ? \"\" : \"s\"\n } rejected`}\n </Text>\n </>\n ) : null}\n </Box>\n );\n}\n\n/**\n * Rejected registrations (`AS-CLI-006`). A dead handle leaves no trace in either\n * projection, so without this block a copy-pasted component `type` removes a\n * capability and prints nothing anywhere.\n */\nfunction Rejections({ view }: { view: SurfaceView }): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text backgroundColor=\"magenta\" color=\"black\" bold>\n {\" rejected during mount \"}\n </Text>\n <Text dimColor>{` ${view.rejections.length}`}</Text>\n </Box>\n {view.rejections.map((rejection) => (\n <Box key={`${rejection.componentType}@${rejection.instanceId}-${rejection.reason}`}>\n <Text color=\"magenta\">{\" ! \"}</Text>\n <Text bold>{`${rejection.componentType} (${rejection.instanceId})`}</Text>\n <Text dimColor>\n {rejection.reason === \"duplicate\"\n ? \" duplicate — an earlier registration holds this key\"\n : \" guard — onRegister rejected this registration\"}\n </Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction Empty({ view }: { view: SurfaceView }): ReactElement {\n if (view.counts.hidden > 0) {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor wrap=\"wrap\">\n {`Nothing is callable here — all ${view.counts.hidden} registered capabilities were hidden by policy. `}\n The surface is empty by decision, not because nothing was annotated.\n </Text>\n {view.explained ? null : (\n <Text dimColor>Re-run with --explain to see which policy hid them.</Text>\n )}\n </Box>\n );\n }\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor wrap=\"wrap\">\n Nothing is registered for this scenario — the agent has no surface here. That is the\n default: capabilities exist only where they were explicitly annotated.\n </Text>\n {view.explained ? null : (\n <Text dimColor>Re-run with --explain to see whether a policy hid it.</Text>\n )}\n </Box>\n );\n}\n\ntype Block = { key: string; group?: CapabilityGroup };\n\nexport function Surface({ view }: { view: SurfaceView }): ReactElement {\n const populated = view.groups.filter((group) => group.rows.length > 0);\n\n // Everything goes through <Static>, header included. Ink paints static output\n // once, permanently, above the live frame — and erases the live frame on\n // unmount. A one-shot render that leaves anything outside <Static> therefore\n // prints it and then wipes it, which is exactly what happened to this header.\n const blocks: Block[] = [\n { key: \"__header\" },\n ...populated.map((group) => ({ key: group.heading, group })),\n ];\n\n return (\n <Static items={blocks}>\n {(block) =>\n block.group ? (\n <Group key={block.key} group={block.group} />\n ) : (\n <Box key={block.key} flexDirection=\"column\">\n <Header view={view} />\n {view.rejections.length > 0 ? <Rejections view={view} /> : null}\n {populated.length === 0 ? <Empty view={view} /> : null}\n </Box>\n )\n }\n </Static>\n );\n}\n\nexport function Drift({\n scenario,\n entries,\n}: {\n scenario: string;\n entries: DiffEntry[];\n}): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text backgroundColor=\"yellow\" color=\"black\" bold>{` ${scenario} `}</Text>\n <Text dimColor>{` ${entries.length} change${entries.length === 1 ? \"\" : \"s\"}`}</Text>\n </Box>\n {entries.map((entry) => (\n <Box key={`${entry.kind}-${entry.path}`} flexDirection=\"column\" paddingLeft={2}>\n {entry.subject ? (\n <Text bold>\n {entry.subject}\n <Text dimColor>{` ${entry.path}`}</Text>\n </Text>\n ) : null}\n {entry.kind === \"added\" ? (\n <Text color=\"green\" wrap=\"wrap\">{`+ ${entry.path} ${formatValue(entry.after)}`}</Text>\n ) : entry.kind === \"removed\" ? (\n <Text color=\"red\" wrap=\"wrap\">{`- ${entry.path} ${formatValue(entry.before)}`}</Text>\n ) : (\n <>\n <Text color=\"yellow\">{`~ ${entry.path}`}</Text>\n <Text color=\"red\" wrap=\"wrap\">{` before: ${formatValue(entry.before)}`}</Text>\n <Text color=\"green\" wrap=\"wrap\">{` after: ${formatValue(entry.after)}`}</Text>\n </>\n )}\n </Box>\n ))}\n </Box>\n );\n}\n"],"mappings":"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","names":[]}
|