@agent-surface/cli 0.19.1 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -3,12 +3,12 @@ import {
3
3
  DEFAULT_SNAPSHOT,
4
4
  readBaseManifest,
5
5
  readManifest,
6
- renderReport,
7
6
  writeManifest
8
- } from "./chunk-X6RGGBQ4.js";
7
+ } from "./chunk-XCQACLOT.js";
9
8
  import {
10
- diffContracts
11
- } from "./chunk-IA7FUP4R.js";
9
+ diffContracts,
10
+ renderReport
11
+ } from "./chunk-KMAH2WU5.js";
12
12
 
13
13
  // src/bin.ts
14
14
  import { realpathSync } from "fs";
@@ -71,12 +71,13 @@ function shouldFail(policy, changes) {
71
71
  }
72
72
  async function present(report, options) {
73
73
  const interactive = options.format === "human" && !options.plain && process.stdout.isTTY === true && !process.env["CI"] && !process.env["NO_COLOR"];
74
+ const render = { root: options.root, ...options.detail ? { detail: true } : {} };
74
75
  if (interactive) {
75
- const { renderInk } = await import("./ink-E6DATMUQ.js");
76
- await renderInk(report);
76
+ const { renderInk } = await import("./ink-4CUU77KD.js");
77
+ await renderInk(report, render);
77
78
  return;
78
79
  }
79
- process.stdout.write(renderReport(report, options.format));
80
+ process.stdout.write(renderReport(report, options.format, render));
80
81
  }
81
82
  async function runInspect(options) {
82
83
  const manifest = await compile(options);
@@ -139,24 +140,33 @@ async function runCheck(options) {
139
140
  var USAGE = `agent-surface \u2014 compiled production capability contract
140
141
 
141
142
  Usage
142
- agent-surface inspect
143
- agent-surface snapshot
144
- agent-surface check --base origin/main --format github
143
+ agent-surface inspect [options] view the contract and any drift
144
+ agent-surface snapshot [options] write the committed contract
145
+ agent-surface check [options] fail on drift, for CI
145
146
 
146
- Options
147
+ Compilation
147
148
  --root <path> application root (default: cwd)
148
149
  --config <path> Vite config, relative to root
149
- --snapshot <path> committed contract (default: .agent-surface/contract.json)
150
150
  --target <name> production build target; repeatable
151
- --base <git-ref> render committed PR drift against Git base
152
151
  --allow <pkg>=<d> approve a dependency to contribute capabilities; repeatable
152
+
153
+ Contract
154
+ --snapshot <path> committed contract (default: .agent-surface/contract.json)
155
+ --base <git-ref> render committed PR drift against Git base
153
156
  --policy <mode> all | widening | narrowing | neutral | none (default: all)
157
+
158
+ Output
159
+ --detail add descriptions, confirmation, policies, and tags
154
160
  --format <format> human | json | github | markdown (default: human)
155
161
  --json alias for --format json
156
- --plain disable Ink terminal rendering
162
+ --plain plain text instead of the drawn terminal view
157
163
  -h, --help
158
164
  -v, --version
159
165
 
166
+ Examples
167
+ agent-surface inspect --detail
168
+ agent-surface check --base origin/main --format github
169
+
160
170
  Exit: 0 clean/viewed, 1 deterministic drift/policy finding, 2 completeness failure.
161
171
  `;
162
172
  var COMMANDS = /* @__PURE__ */ new Set(["inspect", "snapshot", "check"]);
@@ -166,11 +176,15 @@ function write(text, error = false) {
166
176
  (error ? process.stderr : process.stdout).write(text.endsWith("\n") ? text : `${text}
167
177
  `);
168
178
  }
179
+ function stripSeparator(argv) {
180
+ const index = argv.indexOf("--");
181
+ return index === -1 ? argv : [...argv.slice(0, index), ...argv.slice(index + 1)];
182
+ }
169
183
  async function main(argv = process.argv.slice(2)) {
170
184
  let parsed;
171
185
  try {
172
186
  parsed = parseArgs({
173
- args: argv,
187
+ args: stripSeparator(argv),
174
188
  allowPositionals: true,
175
189
  options: {
176
190
  root: { type: "string", default: process.cwd() },
@@ -183,6 +197,7 @@ async function main(argv = process.argv.slice(2)) {
183
197
  format: { type: "string", default: "human" },
184
198
  json: { type: "boolean", default: false },
185
199
  plain: { type: "boolean", default: false },
200
+ detail: { type: "boolean", default: false },
186
201
  help: { type: "boolean", short: "h", default: false },
187
202
  version: { type: "boolean", short: "v", default: false }
188
203
  }
@@ -236,7 +251,8 @@ async function main(argv = process.argv.slice(2)) {
236
251
  format,
237
252
  policy: values.policy,
238
253
  ...allow.length > 0 ? { externalContracts: { allow } } : {},
239
- ...values.plain ? { plain: true } : {}
254
+ ...values.plain ? { plain: true } : {},
255
+ ...values.detail ? { detail: true } : {}
240
256
  };
241
257
  try {
242
258
  if (command === "inspect") return await runInspect(options);
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/bin.ts","../src/commands.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { parseArgs } from \"node:util\";\nimport { runCheck, runInspect, runSnapshot, type CommandOptions } from \"./commands.js\";\nimport type { OutputFormat } from \"./report.js\";\n\nconst USAGE = `agent-surface — compiled production capability contract\n\nUsage\n agent-surface inspect\n agent-surface snapshot\n agent-surface check --base origin/main --format github\n\nOptions\n --root <path> application root (default: cwd)\n --config <path> Vite config, relative to root\n --snapshot <path> committed contract (default: .agent-surface/contract.json)\n --target <name> production build target; repeatable\n --base <git-ref> render committed PR drift against Git base\n --allow <pkg>=<d> approve a dependency to contribute capabilities; repeatable\n --policy <mode> all | widening | narrowing | neutral | none (default: all)\n --format <format> human | json | github | markdown (default: human)\n --json alias for --format json\n --plain disable Ink terminal rendering\n -h, --help\n -v, --version\n\nExit: 0 clean/viewed, 1 deterministic drift/policy finding, 2 completeness failure.\n`;\n\nconst COMMANDS = new Set([\"inspect\", \"snapshot\", \"check\"]);\nconst FORMATS = new Set<OutputFormat>([\"human\", \"json\", \"github\", \"markdown\"]);\nconst POLICIES = new Set([\"all\", \"widening\", \"narrowing\", \"neutral\", \"none\"]);\n\nfunction write(text: string, error = false): void {\n (error ? process.stderr : process.stdout).write(text.endsWith(\"\\n\") ? text : `${text}\\n`);\n}\n\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<number> {\n let parsed;\n try {\n parsed = parseArgs({\n args: argv,\n allowPositionals: true,\n options: {\n root: { type: \"string\", default: process.cwd() },\n config: { type: \"string\" },\n snapshot: { type: \"string\" },\n target: { type: \"string\", multiple: true },\n base: { type: \"string\" },\n allow: { type: \"string\", multiple: true },\n policy: { type: \"string\", default: \"all\" },\n format: { type: \"string\", default: \"human\" },\n json: { type: \"boolean\", default: false },\n plain: { type: \"boolean\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n version: { type: \"boolean\", short: \"v\", default: false },\n },\n });\n } catch (error) {\n write(error instanceof Error ? error.message : String(error), true);\n write(USAGE, true);\n return 2;\n }\n const { values, positionals } = parsed;\n if (values.help) {\n write(USAGE);\n return 0;\n }\n if (values.version) {\n write(await readVersion());\n return 0;\n }\n const [command, ...extra] = positionals;\n if (!command || !COMMANDS.has(command) || extra.length > 0) {\n write(command === \"init\" ? \"init removed: configure the Vite compiler plugin\" : `invalid command ${command ?? \"\"}`, true);\n write(USAGE, true);\n return 2;\n }\n const format = (values.json ? \"json\" : values.format) as OutputFormat;\n if (!FORMATS.has(format)) {\n write(`--format must be human, json, github, or markdown`, true);\n return 2;\n }\n if (!POLICIES.has(values.policy)) {\n write(`--policy must be all, widening, narrowing, neutral, or none`, true);\n return 2;\n }\n const allow = [];\n for (const entry of values.allow ?? []) {\n const separator = entry.lastIndexOf(\"=\");\n const name = separator === -1 ? \"\" : entry.slice(0, separator);\n const digest = separator === -1 ? \"\" : entry.slice(separator + 1);\n if (!name || !/^[0-9a-f]{64}$/.test(digest)) {\n write(`--allow must be <package>=<sha256>, got \"${entry}\"`, true);\n return 2;\n }\n allow.push({ package: name, digest });\n }\n const options: CommandOptions = {\n root: values.root,\n ...(values.config ? { configFile: values.config } : {}),\n ...(values.snapshot ? { snapshot: values.snapshot } : {}),\n targets: values.target ?? [],\n ...(values.base ? { base: values.base } : {}),\n format,\n policy: values.policy as CommandOptions[\"policy\"],\n ...(allow.length > 0 ? { externalContracts: { allow } } : {}),\n ...(values.plain ? { plain: true } : {}),\n };\n try {\n if (command === \"inspect\") return await runInspect(options);\n if (command === \"snapshot\") return await runSnapshot(options);\n return await runCheck(options);\n } catch (error) {\n write(error instanceof Error ? error.message : String(error), true);\n if (error instanceof Error && error.stack && process.env[\"AGENT_SURFACE_DEBUG\"]) {\n write(error.stack, true);\n }\n return 2;\n }\n}\n\nasync function readVersion(): Promise<string> {\n const { readFileSync } = await import(\"node:fs\");\n const path = fileURLToPath(new URL(\"../package.json\", import.meta.url));\n return (JSON.parse(readFileSync(path, \"utf8\")) as { version: string }).version;\n}\n\nfunction invokedAsBinary(): boolean {\n const entry = process.argv[1];\n if (!entry) return false;\n try {\n return fileURLToPath(import.meta.url) === realpathSync(entry);\n } catch {\n return false;\n }\n}\n\nif (invokedAsBinary()) {\n void main().then((code) => {\n process.exitCode = code;\n });\n}\n","import { resolve } from \"node:path\";\nimport type { CapabilityContractEntry, CapabilityContractManifest } from \"@agent-surface/core\";\nimport {\n compileCapabilityContract,\n computeManifestHash,\n type ExternalContractPolicy,\n} from \"@agent-surface/compiler\";\nimport { diffContracts, type ChangeClassification } from \"./diff.js\";\nimport { DEFAULT_SNAPSHOT, readBaseManifest, readManifest, writeManifest } from \"./files.js\";\nimport type { ContractReport, OutputFormat } from \"./report.js\";\nimport { renderReport } from \"./report.js\";\n\nexport interface CommandOptions {\n root: string;\n configFile?: string;\n snapshot?: string;\n targets: string[];\n base?: string;\n format: OutputFormat;\n policy: \"all\" | ChangeClassification | \"none\";\n /** Dependencies approved to contribute, as `--allow <package>=<sha256>`. */\n externalContracts?: ExternalContractPolicy;\n plain?: boolean;\n}\n\nfunction mergeManifests(manifests: CapabilityContractManifest[]): CapabilityContractManifest {\n const entries = new Map<string, CapabilityContractEntry>();\n for (const manifest of manifests) {\n for (const entry of manifest.capabilities) {\n const key = `${entry.declarationId}\\0${entry.capabilityId}`;\n const existing = entries.get(key);\n if (!existing) entries.set(key, { ...entry, targets: [...entry.targets] });\n else {\n if (existing.contractHash !== entry.contractHash) {\n throw new Error(`contract ${entry.declarationId} / ${entry.capabilityId} differs across targets`);\n }\n existing.targets = [...new Set([...existing.targets, ...entry.targets])].sort();\n }\n }\n }\n const payload = {\n formatVersion: 5 as const,\n compilerVersion: manifests[0]?.compilerVersion ?? \"unknown\",\n targets: [...new Set(manifests.flatMap((manifest) => manifest.targets))].sort(),\n capabilities: [...entries.values()].sort((a, b) =>\n `${a.declarationId}\\0${a.capabilityId}`.localeCompare(`${b.declarationId}\\0${b.capabilityId}`),\n ),\n // One attribution per package: the same dependency contributes identically\n // to every target, and a divergence would already have failed the compile.\n externalContracts: manifests\n .flatMap((manifest) => manifest.externalContracts)\n .filter((entry, index, all) => all.findIndex((candidate) => candidate.package === entry.package) === index)\n .sort((a, b) => a.package.localeCompare(b.package)),\n completeness: { status: \"proven\" as const },\n };\n return { ...payload, hash: computeManifestHash(payload) };\n}\n\nasync function compile(options: CommandOptions): Promise<CapabilityContractManifest> {\n const targets = options.targets.length > 0 ? options.targets : [\"web-production\"];\n const manifests = await Promise.all(\n targets.map((target) =>\n compileCapabilityContract({\n root: options.root,\n ...(options.configFile ? { configFile: options.configFile } : {}),\n ...(options.externalContracts ? { externalContracts: options.externalContracts } : {}),\n target,\n }),\n ),\n );\n return mergeManifests(manifests);\n}\n\nfunction shouldFail(\n policy: CommandOptions[\"policy\"],\n changes: ReturnType<typeof diffContracts>,\n): boolean {\n if (policy === \"none\") return false;\n if (policy === \"all\") return changes.length > 0;\n return changes.some((change) => change.classification === policy);\n}\n\nasync function present(report: ContractReport, options: CommandOptions): Promise<void> {\n const interactive =\n options.format === \"human\" &&\n !options.plain &&\n process.stdout.isTTY === true &&\n !process.env[\"CI\"] &&\n !process.env[\"NO_COLOR\"];\n if (interactive) {\n const { renderInk } = await import(\"./ink.js\");\n await renderInk(report);\n return;\n }\n process.stdout.write(renderReport(report, options.format));\n}\n\nexport async function runInspect(options: CommandOptions): Promise<number> {\n const manifest = await compile(options);\n const snapshotPath = resolve(options.root, options.snapshot ?? DEFAULT_SNAPSHOT);\n const snapshot = readManifest(snapshotPath);\n const integrityChanges = diffContracts(snapshot, manifest);\n const report: ContractReport = {\n command: \"inspect\",\n status: \"view\",\n manifest,\n snapshotPath,\n integrity: {\n status: !snapshot ? \"missing\" : snapshot.hash === manifest.hash ? \"current\" : \"stale\",\n changes: integrityChanges,\n },\n ...(options.base\n ? {\n pullRequest: {\n base: options.base,\n changes: diffContracts(readBaseManifest(options.base, snapshotPath, options.root), manifest),\n },\n }\n : {}),\n };\n await present(report, options);\n return 0;\n}\n\nexport async function runSnapshot(options: CommandOptions): Promise<number> {\n const manifest = await compile(options);\n const snapshotPath = resolve(options.root, options.snapshot ?? DEFAULT_SNAPSHOT);\n writeManifest(snapshotPath, manifest);\n await present({ command: \"snapshot\", status: \"written\", manifest, snapshotPath }, options);\n return 0;\n}\n\nexport async function runCheck(options: CommandOptions): Promise<number> {\n const manifest = await compile(options);\n const snapshotPath = resolve(options.root, options.snapshot ?? DEFAULT_SNAPSHOT);\n const snapshot = readManifest(snapshotPath);\n const integrityChanges = diffContracts(snapshot, manifest);\n const integrityCurrent = snapshot?.hash === manifest.hash;\n const pr = options.base\n ? {\n base: options.base,\n changes: diffContracts(readBaseManifest(options.base, snapshotPath, options.root), snapshot ?? manifest),\n }\n : undefined;\n const failed = !integrityCurrent || (pr ? shouldFail(options.policy, pr.changes) : false);\n const report: ContractReport = {\n command: \"check\",\n status: failed ? \"fail\" : \"pass\",\n manifest,\n snapshotPath,\n integrity: {\n status: !snapshot ? \"missing\" : integrityCurrent ? \"current\" : \"stale\",\n changes: integrityChanges,\n },\n ...(pr ? { pullRequest: pr } : {}),\n };\n await present(report, options);\n return failed ? 1 : 0;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/bin.ts","../src/commands.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { realpathSync } from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport { parseArgs } from \"node:util\";\nimport { runCheck, runInspect, runSnapshot, type CommandOptions } from \"./commands.js\";\nimport type { OutputFormat } from \"./report.js\";\n\nconst USAGE = `agent-surface — compiled production capability contract\n\nUsage\n agent-surface inspect [options] view the contract and any drift\n agent-surface snapshot [options] write the committed contract\n agent-surface check [options] fail on drift, for CI\n\nCompilation\n --root <path> application root (default: cwd)\n --config <path> Vite config, relative to root\n --target <name> production build target; repeatable\n --allow <pkg>=<d> approve a dependency to contribute capabilities; repeatable\n\nContract\n --snapshot <path> committed contract (default: .agent-surface/contract.json)\n --base <git-ref> render committed PR drift against Git base\n --policy <mode> all | widening | narrowing | neutral | none (default: all)\n\nOutput\n --detail add descriptions, confirmation, policies, and tags\n --format <format> human | json | github | markdown (default: human)\n --json alias for --format json\n --plain plain text instead of the drawn terminal view\n -h, --help\n -v, --version\n\nExamples\n agent-surface inspect --detail\n agent-surface check --base origin/main --format github\n\nExit: 0 clean/viewed, 1 deterministic drift/policy finding, 2 completeness failure.\n`;\n\nconst COMMANDS = new Set([\"inspect\", \"snapshot\", \"check\"]);\nconst FORMATS = new Set<OutputFormat>([\"human\", \"json\", \"github\", \"markdown\"]);\nconst POLICIES = new Set([\"all\", \"widening\", \"narrowing\", \"neutral\", \"none\"]);\n\nfunction write(text: string, error = false): void {\n (error ? process.stderr : process.stdout).write(text.endsWith(\"\\n\") ? text : `${text}\\n`);\n}\n\n/**\n * `pnpm run <script> -- --plain` forwards the separator itself, and `parseArgs`\n * turns everything after it into positionals — so the flag arrived as a second\n * command and the run failed as `invalid command inspect`. This CLI takes no\n * literal operands, so the first separator is dropped and the pass-through\n * idiom every package manager documents works.\n */\nfunction stripSeparator(argv: string[]): string[] {\n const index = argv.indexOf(\"--\");\n return index === -1 ? argv : [...argv.slice(0, index), ...argv.slice(index + 1)];\n}\n\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<number> {\n let parsed;\n try {\n parsed = parseArgs({\n args: stripSeparator(argv),\n allowPositionals: true,\n options: {\n root: { type: \"string\", default: process.cwd() },\n config: { type: \"string\" },\n snapshot: { type: \"string\" },\n target: { type: \"string\", multiple: true },\n base: { type: \"string\" },\n allow: { type: \"string\", multiple: true },\n policy: { type: \"string\", default: \"all\" },\n format: { type: \"string\", default: \"human\" },\n json: { type: \"boolean\", default: false },\n plain: { type: \"boolean\", default: false },\n detail: { type: \"boolean\", default: false },\n help: { type: \"boolean\", short: \"h\", default: false },\n version: { type: \"boolean\", short: \"v\", default: false },\n },\n });\n } catch (error) {\n write(error instanceof Error ? error.message : String(error), true);\n write(USAGE, true);\n return 2;\n }\n const { values, positionals } = parsed;\n if (values.help) {\n write(USAGE);\n return 0;\n }\n if (values.version) {\n write(await readVersion());\n return 0;\n }\n const [command, ...extra] = positionals;\n if (!command || !COMMANDS.has(command) || extra.length > 0) {\n write(command === \"init\" ? \"init removed: configure the Vite compiler plugin\" : `invalid command ${command ?? \"\"}`, true);\n write(USAGE, true);\n return 2;\n }\n const format = (values.json ? \"json\" : values.format) as OutputFormat;\n if (!FORMATS.has(format)) {\n write(`--format must be human, json, github, or markdown`, true);\n return 2;\n }\n if (!POLICIES.has(values.policy)) {\n write(`--policy must be all, widening, narrowing, neutral, or none`, true);\n return 2;\n }\n const allow = [];\n for (const entry of values.allow ?? []) {\n const separator = entry.lastIndexOf(\"=\");\n const name = separator === -1 ? \"\" : entry.slice(0, separator);\n const digest = separator === -1 ? \"\" : entry.slice(separator + 1);\n if (!name || !/^[0-9a-f]{64}$/.test(digest)) {\n write(`--allow must be <package>=<sha256>, got \"${entry}\"`, true);\n return 2;\n }\n allow.push({ package: name, digest });\n }\n const options: CommandOptions = {\n root: values.root,\n ...(values.config ? { configFile: values.config } : {}),\n ...(values.snapshot ? { snapshot: values.snapshot } : {}),\n targets: values.target ?? [],\n ...(values.base ? { base: values.base } : {}),\n format,\n policy: values.policy as CommandOptions[\"policy\"],\n ...(allow.length > 0 ? { externalContracts: { allow } } : {}),\n ...(values.plain ? { plain: true } : {}),\n ...(values.detail ? { detail: true } : {}),\n };\n try {\n if (command === \"inspect\") return await runInspect(options);\n if (command === \"snapshot\") return await runSnapshot(options);\n return await runCheck(options);\n } catch (error) {\n write(error instanceof Error ? error.message : String(error), true);\n if (error instanceof Error && error.stack && process.env[\"AGENT_SURFACE_DEBUG\"]) {\n write(error.stack, true);\n }\n return 2;\n }\n}\n\nasync function readVersion(): Promise<string> {\n const { readFileSync } = await import(\"node:fs\");\n const path = fileURLToPath(new URL(\"../package.json\", import.meta.url));\n return (JSON.parse(readFileSync(path, \"utf8\")) as { version: string }).version;\n}\n\nfunction invokedAsBinary(): boolean {\n const entry = process.argv[1];\n if (!entry) return false;\n try {\n return fileURLToPath(import.meta.url) === realpathSync(entry);\n } catch {\n return false;\n }\n}\n\nif (invokedAsBinary()) {\n void main().then((code) => {\n process.exitCode = code;\n });\n}\n","import { resolve } from \"node:path\";\nimport type { CapabilityContractEntry, CapabilityContractManifest } from \"@agent-surface/core\";\nimport {\n compileCapabilityContract,\n computeManifestHash,\n type ExternalContractPolicy,\n} from \"@agent-surface/compiler\";\nimport { diffContracts, type ChangeClassification } from \"./diff.js\";\nimport { DEFAULT_SNAPSHOT, readBaseManifest, readManifest, writeManifest } from \"./files.js\";\nimport type { ContractReport, OutputFormat, RenderOptions } from \"./report.js\";\nimport { renderReport } from \"./report.js\";\n\nexport interface CommandOptions {\n root: string;\n configFile?: string;\n snapshot?: string;\n targets: string[];\n base?: string;\n format: OutputFormat;\n policy: \"all\" | ChangeClassification | \"none\";\n /** Dependencies approved to contribute, as `--allow <package>=<sha256>`. */\n externalContracts?: ExternalContractPolicy;\n plain?: boolean;\n detail?: boolean;\n}\n\nfunction mergeManifests(manifests: CapabilityContractManifest[]): CapabilityContractManifest {\n const entries = new Map<string, CapabilityContractEntry>();\n for (const manifest of manifests) {\n for (const entry of manifest.capabilities) {\n const key = `${entry.declarationId}\\0${entry.capabilityId}`;\n const existing = entries.get(key);\n if (!existing) entries.set(key, { ...entry, targets: [...entry.targets] });\n else {\n if (existing.contractHash !== entry.contractHash) {\n throw new Error(`contract ${entry.declarationId} / ${entry.capabilityId} differs across targets`);\n }\n existing.targets = [...new Set([...existing.targets, ...entry.targets])].sort();\n }\n }\n }\n const payload = {\n formatVersion: 5 as const,\n compilerVersion: manifests[0]?.compilerVersion ?? \"unknown\",\n targets: [...new Set(manifests.flatMap((manifest) => manifest.targets))].sort(),\n capabilities: [...entries.values()].sort((a, b) =>\n `${a.declarationId}\\0${a.capabilityId}`.localeCompare(`${b.declarationId}\\0${b.capabilityId}`),\n ),\n // One attribution per package: the same dependency contributes identically\n // to every target, and a divergence would already have failed the compile.\n externalContracts: manifests\n .flatMap((manifest) => manifest.externalContracts)\n .filter((entry, index, all) => all.findIndex((candidate) => candidate.package === entry.package) === index)\n .sort((a, b) => a.package.localeCompare(b.package)),\n completeness: { status: \"proven\" as const },\n };\n return { ...payload, hash: computeManifestHash(payload) };\n}\n\nasync function compile(options: CommandOptions): Promise<CapabilityContractManifest> {\n const targets = options.targets.length > 0 ? options.targets : [\"web-production\"];\n const manifests = await Promise.all(\n targets.map((target) =>\n compileCapabilityContract({\n root: options.root,\n ...(options.configFile ? { configFile: options.configFile } : {}),\n ...(options.externalContracts ? { externalContracts: options.externalContracts } : {}),\n target,\n }),\n ),\n );\n return mergeManifests(manifests);\n}\n\nfunction shouldFail(\n policy: CommandOptions[\"policy\"],\n changes: ReturnType<typeof diffContracts>,\n): boolean {\n if (policy === \"none\") return false;\n if (policy === \"all\") return changes.length > 0;\n return changes.some((change) => change.classification === policy);\n}\n\nasync function present(report: ContractReport, options: CommandOptions): Promise<void> {\n const interactive =\n options.format === \"human\" &&\n !options.plain &&\n process.stdout.isTTY === true &&\n !process.env[\"CI\"] &&\n !process.env[\"NO_COLOR\"];\n const render: RenderOptions = { root: options.root, ...(options.detail ? { detail: true } : {}) };\n if (interactive) {\n const { renderInk } = await import(\"./ink.js\");\n await renderInk(report, render);\n return;\n }\n process.stdout.write(renderReport(report, options.format, render));\n}\n\nexport async function runInspect(options: CommandOptions): Promise<number> {\n const manifest = await compile(options);\n const snapshotPath = resolve(options.root, options.snapshot ?? DEFAULT_SNAPSHOT);\n const snapshot = readManifest(snapshotPath);\n const integrityChanges = diffContracts(snapshot, manifest);\n const report: ContractReport = {\n command: \"inspect\",\n status: \"view\",\n manifest,\n snapshotPath,\n integrity: {\n status: !snapshot ? \"missing\" : snapshot.hash === manifest.hash ? \"current\" : \"stale\",\n changes: integrityChanges,\n },\n ...(options.base\n ? {\n pullRequest: {\n base: options.base,\n changes: diffContracts(readBaseManifest(options.base, snapshotPath, options.root), manifest),\n },\n }\n : {}),\n };\n await present(report, options);\n return 0;\n}\n\nexport async function runSnapshot(options: CommandOptions): Promise<number> {\n const manifest = await compile(options);\n const snapshotPath = resolve(options.root, options.snapshot ?? DEFAULT_SNAPSHOT);\n writeManifest(snapshotPath, manifest);\n await present({ command: \"snapshot\", status: \"written\", manifest, snapshotPath }, options);\n return 0;\n}\n\nexport async function runCheck(options: CommandOptions): Promise<number> {\n const manifest = await compile(options);\n const snapshotPath = resolve(options.root, options.snapshot ?? DEFAULT_SNAPSHOT);\n const snapshot = readManifest(snapshotPath);\n const integrityChanges = diffContracts(snapshot, manifest);\n const integrityCurrent = snapshot?.hash === manifest.hash;\n const pr = options.base\n ? {\n base: options.base,\n changes: diffContracts(readBaseManifest(options.base, snapshotPath, options.root), snapshot ?? manifest),\n }\n : undefined;\n const failed = !integrityCurrent || (pr ? shouldFail(options.policy, pr.changes) : false);\n const report: ContractReport = {\n command: \"check\",\n status: failed ? \"fail\" : \"pass\",\n manifest,\n snapshotPath,\n integrity: {\n status: !snapshot ? \"missing\" : integrityCurrent ? \"current\" : \"stale\",\n changes: integrityChanges,\n },\n ...(pr ? { pullRequest: pr } : {}),\n };\n await present(report, options);\n return failed ? 1 : 0;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,333 @@
1
+ // src/diff.ts
2
+ var RISK = {
3
+ read: 0,
4
+ "local-state": 1,
5
+ navigation: 2,
6
+ "server-query": 3,
7
+ "server-mutation": 4,
8
+ "external-side-effect": 5,
9
+ destructive: 6
10
+ };
11
+ var CONFIRMATION = { never: 0, optional: 1, required: 2 };
12
+ function key(entry) {
13
+ return `${entry.declarationId}\0${entry.capabilityId}`;
14
+ }
15
+ function equal(a, b) {
16
+ return JSON.stringify(a) === JSON.stringify(b);
17
+ }
18
+ function stringSet(value2) {
19
+ return new Set(Array.isArray(value2) ? value2.map((item) => JSON.stringify(item)) : []);
20
+ }
21
+ function setDirection(before, after) {
22
+ const a = stringSet(before);
23
+ const b = stringSet(after);
24
+ const added = [...b].some((item) => !a.has(item));
25
+ const removed = [...a].some((item) => !b.has(item));
26
+ if (added && removed) return "mixed";
27
+ if (added) return "added";
28
+ if (removed) return "removed";
29
+ return "same";
30
+ }
31
+ function classify(field, before, after) {
32
+ if (field === "targets") {
33
+ const direction = setDirection(before, after);
34
+ return direction === "added" ? "widening" : direction === "removed" ? "narrowing" : "neutral";
35
+ }
36
+ if (field === "policies") {
37
+ const direction = setDirection(before, after);
38
+ return direction === "removed" || direction === "mixed" ? "widening" : direction === "added" ? "narrowing" : "neutral";
39
+ }
40
+ if (field === "confirmation") {
41
+ const a = CONFIRMATION[String(before ?? "never")] ?? 0;
42
+ const b = CONFIRMATION[String(after ?? "never")] ?? 0;
43
+ return b < a ? "widening" : b > a ? "narrowing" : "neutral";
44
+ }
45
+ if (field === "effect") {
46
+ const a = RISK[String(before)] ?? 0;
47
+ const b = RISK[String(after)] ?? 0;
48
+ return b < a ? "widening" : b > a ? "narrowing" : "neutral";
49
+ }
50
+ return "neutral";
51
+ }
52
+ function diffContracts(before, after) {
53
+ if (!before) {
54
+ return after.capabilities.map((entry) => ({
55
+ declarationId: entry.declarationId,
56
+ capabilityId: entry.capabilityId,
57
+ kind: "added",
58
+ field: "capability",
59
+ classification: "widening",
60
+ after: entry
61
+ }));
62
+ }
63
+ const left = new Map(before.capabilities.map((entry) => [key(entry), entry]));
64
+ const right = new Map(after.capabilities.map((entry) => [key(entry), entry]));
65
+ const changes = [];
66
+ for (const entryKey of [.../* @__PURE__ */ new Set([...left.keys(), ...right.keys()])].sort()) {
67
+ const a = left.get(entryKey);
68
+ const b = right.get(entryKey);
69
+ if (!a && b) {
70
+ changes.push({
71
+ declarationId: b.declarationId,
72
+ capabilityId: b.capabilityId,
73
+ kind: "added",
74
+ field: "capability",
75
+ classification: "widening",
76
+ after: b
77
+ });
78
+ continue;
79
+ }
80
+ if (a && !b) {
81
+ changes.push({
82
+ declarationId: a.declarationId,
83
+ capabilityId: a.capabilityId,
84
+ kind: "removed",
85
+ field: "capability",
86
+ classification: "narrowing",
87
+ before: a
88
+ });
89
+ continue;
90
+ }
91
+ if (!a || !b) continue;
92
+ const fields = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
93
+ fields.delete("declarationId");
94
+ fields.delete("capabilityId");
95
+ fields.delete("contractHash");
96
+ for (const field of [...fields].sort()) {
97
+ const beforeValue = a[field];
98
+ const afterValue = b[field];
99
+ if (equal(beforeValue, afterValue)) continue;
100
+ changes.push({
101
+ declarationId: b.declarationId,
102
+ capabilityId: b.capabilityId,
103
+ kind: "changed",
104
+ field,
105
+ classification: classify(field, beforeValue, afterValue),
106
+ before: beforeValue,
107
+ after: afterValue
108
+ });
109
+ }
110
+ }
111
+ return changes;
112
+ }
113
+ function changeCounts(changes) {
114
+ return {
115
+ widening: changes.filter((change) => change.classification === "widening").length,
116
+ narrowing: changes.filter((change) => change.classification === "narrowing").length,
117
+ neutral: changes.filter((change) => change.classification === "neutral").length
118
+ };
119
+ }
120
+
121
+ // src/report.ts
122
+ import { relative } from "path";
123
+ import { canonicalJson } from "@agent-surface/compiler";
124
+ function value(value2) {
125
+ if (value2 === void 0) return "\u2014";
126
+ const text = typeof value2 === "string" ? value2 : JSON.stringify(value2);
127
+ return text.length > 100 ? `${text.slice(0, 97)}\u2026` : text;
128
+ }
129
+ function changeLine(change) {
130
+ const mark = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
131
+ return `${mark} ${change.classification.padEnd(9)} ${change.capabilityId} \xB7 ${change.declarationId} \xB7 ${change.field}${change.kind === "changed" ? `: ${value(change.before)} \u2192 ${value(change.after)}` : ""}`;
132
+ }
133
+ function sectionTitle(title, changes) {
134
+ const counts = changeCounts(changes);
135
+ return `${title} (${changes.length}) \xB7 widening ${counts.widening} \xB7 narrowing ${counts.narrowing} \xB7 neutral ${counts.neutral}`;
136
+ }
137
+ function section(title, changes) {
138
+ return [sectionTitle(title, changes), ...changes.length > 0 ? changes.map(changeLine) : ["no changes"]];
139
+ }
140
+ var EFFECT_ORDER = [
141
+ "read",
142
+ "local-state",
143
+ "navigation",
144
+ "server-query",
145
+ "server-mutation",
146
+ "external-side-effect",
147
+ "destructive"
148
+ ];
149
+ function effectRank(effect) {
150
+ const index = EFFECT_ORDER.indexOf(effect);
151
+ return index === -1 ? EFFECT_ORDER.length : index;
152
+ }
153
+ function groupByDeclaration(capabilities) {
154
+ const groups = /* @__PURE__ */ new Map();
155
+ for (const entry of capabilities) {
156
+ const existing = groups.get(entry.declarationId);
157
+ if (existing) existing.push(entry);
158
+ else groups.set(entry.declarationId, [entry]);
159
+ }
160
+ return [...groups].map(([declarationId, entries]) => ({ declarationId, entries }));
161
+ }
162
+ function reachOf(effect) {
163
+ const rank = effectRank(effect);
164
+ if (rank <= 1) return "low";
165
+ if (rank <= 3) return "medium";
166
+ return "high";
167
+ }
168
+ var NONE = "\u2014";
169
+ var CONTRACT_HEADERS = ["CAPABILITY", "KIND", "EFFECT", "REACH", "CONFIRM", "POLICIES"];
170
+ function contractRow(entry) {
171
+ const policies = (entry.policies ?? []).map((policy) => `${policy.name}${policy.phase ? `@${policy.phase}` : ""}`);
172
+ const tags = (entry.tags ?? []).map((tag) => `#${tag}`).join(" ");
173
+ return {
174
+ capabilityId: entry.capabilityId,
175
+ kind: entry.kind,
176
+ effect: entry.effect,
177
+ reach: reachOf(entry.effect),
178
+ confirm: entry.confirmation ?? NONE,
179
+ policies: policies.length > 0 ? policies.join(", ") : NONE,
180
+ note: [entry.description, tags].filter(Boolean).join(" ")
181
+ };
182
+ }
183
+ function contractCells(row) {
184
+ return [row.capabilityId, row.kind, row.effect, row.reach, row.confirm, row.policies];
185
+ }
186
+ function tally(values) {
187
+ const counts = /* @__PURE__ */ new Map();
188
+ for (const value2 of values) counts.set(value2, (counts.get(value2) ?? 0) + 1);
189
+ return [...counts].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([name, count]) => `${count} ${name}`).join(" \xB7 ");
190
+ }
191
+ function plural(count, noun, suffix = "s") {
192
+ return `${count} ${noun}${count === 1 ? "" : suffix}`;
193
+ }
194
+ function contractHeading(manifest) {
195
+ const count = manifest.capabilities.length;
196
+ const groups = groupByDeclaration(manifest.capabilities).length;
197
+ return `REPOSITORY CONTRACT \xB7 ${count} ${count === 1 ? "capability" : "capabilities"} \xB7 ${plural(groups, "declaration")}`;
198
+ }
199
+ function headline(manifest) {
200
+ const capabilities = manifest.capabilities;
201
+ const count = capabilities.length;
202
+ const gated = capabilities.filter((entry) => entry.confirmation && entry.confirmation !== "never").length;
203
+ const policed = capabilities.filter((entry) => (entry.policies ?? []).length > 0).length;
204
+ const reach = ["low", "medium", "high"].map((grade) => [grade, capabilities.filter((entry) => reachOf(entry.effect) === grade).length]).filter(([, count2]) => count2 > 0).map(([grade, count2]) => `${count2} ${grade}`).join(" \xB7 ");
205
+ return [
206
+ `${count} ${count === 1 ? "capability" : "capabilities"} \xB7 ${plural(
207
+ groupByDeclaration(capabilities).length,
208
+ "declaration"
209
+ )}${count > 0 ? ` \xB7 ${tally(capabilities.map((entry) => entry.kind))}` : ""}`,
210
+ `reach ${reach || NONE} \xB7 declared gates: ${gated} confirmation \xB7 ${policed} policy`
211
+ ];
212
+ }
213
+ var CONTRACT_CAVEAT = [
214
+ "Declarations, compiled from the production graph \u2014 what this code can expose,",
215
+ "not what a mount exposed at runtime. CONFIRM and POLICIES are declared per",
216
+ "capability; whether a policy admits, denies or hides one depends on the actor,",
217
+ "input and context of a real invocation, which this command never performs."
218
+ ];
219
+ function contractColumns(capabilities) {
220
+ const rows = capabilities.map((entry) => contractCells(contractRow(entry)));
221
+ return CONTRACT_HEADERS.map(
222
+ (header, index) => Math.max(header.length, ...rows.map((cells) => cells[index]?.length ?? 0))
223
+ );
224
+ }
225
+ function contractLine(cells, widths) {
226
+ return cells.map((cell, index) => index === cells.length - 1 ? cell : cell.padEnd(widths[index] ?? 0)).join(" ").trimEnd();
227
+ }
228
+ function displayPath(path, root) {
229
+ if (!root) return path;
230
+ const relativePath = relative(root, path);
231
+ return !relativePath || relativePath.startsWith("..") ? path : relativePath;
232
+ }
233
+ function showsContract(report, options = {}) {
234
+ return report.command === "inspect" || options.detail === true;
235
+ }
236
+ function summaryFields(report, options = {}) {
237
+ const { manifest } = report;
238
+ const fields = [
239
+ ["Contract", manifest.hash],
240
+ ["Compiler", manifest.compilerVersion],
241
+ ["Completeness", manifest.completeness.status],
242
+ ["Targets", manifest.targets.join(", ") || NONE],
243
+ ["Snapshot", displayPath(report.snapshotPath, options.root)]
244
+ ];
245
+ if (report.integrity) fields.push(["Integrity", report.integrity.status]);
246
+ return fields;
247
+ }
248
+ function humanReport(report, options = {}) {
249
+ const fields = summaryFields(report, options);
250
+ const label = Math.max(...fields.map(([name]) => name.length));
251
+ const lines = [
252
+ `AGENT SURFACE ${report.command.toUpperCase()} \xB7 ${report.status.toUpperCase()}`,
253
+ "",
254
+ ...headline(report.manifest),
255
+ "",
256
+ ...fields.map(([name, value2]) => `${name.padEnd(label)} ${value2}`)
257
+ ];
258
+ if (report.integrity) lines.push("", ...section("SOURCE \u2194 SNAPSHOT", report.integrity.changes));
259
+ if (report.pullRequest) {
260
+ lines.push("", ...section(`PR DRIFT vs ${report.pullRequest.base}`, report.pullRequest.changes));
261
+ }
262
+ if (showsContract(report, options)) {
263
+ const widths = contractColumns(report.manifest.capabilities);
264
+ lines.push("", contractHeading(report.manifest), "", ` ${contractLine(CONTRACT_HEADERS, widths)}`);
265
+ for (const group of groupByDeclaration(report.manifest.capabilities)) {
266
+ lines.push("", `${group.declarationId} (${group.entries.length})`);
267
+ for (const entry of group.entries) {
268
+ const row = contractRow(entry);
269
+ lines.push(` ${contractLine(contractCells(row), widths)}`);
270
+ if (options.detail && row.note) lines.push(` ${row.note}`);
271
+ }
272
+ }
273
+ lines.push("", ...CONTRACT_CAVEAT);
274
+ }
275
+ return `${lines.join("\n")}
276
+ `;
277
+ }
278
+ function markdownChanges(changes) {
279
+ if (changes.length === 0) return "No changes.\n";
280
+ return [
281
+ "| Class | Change | Capability | Declaration | Field |",
282
+ "|---|---|---|---|---|",
283
+ ...changes.map(
284
+ (change) => `| ${change.classification} | ${change.kind} | \`${change.capabilityId}\` | \`${change.declarationId}\` | \`${change.field}\` |`
285
+ )
286
+ ].join("\n");
287
+ }
288
+ function markdownReport(report, github = false) {
289
+ const lines = [
290
+ `## Agent Surface ${report.command} \u2014 ${report.status}`,
291
+ "",
292
+ `- Contract: \`${report.manifest.hash}\``,
293
+ `- Completeness: **${report.manifest.completeness.status}**`,
294
+ `- Capabilities: **${report.manifest.capabilities.length}**`,
295
+ `- Targets: ${report.manifest.targets.map((target) => `\`${target}\``).join(", ") || "\u2014"}`
296
+ ];
297
+ if (report.integrity) {
298
+ lines.push("", `### Source \u2194 snapshot \u2014 ${report.integrity.status}`, "", markdownChanges(report.integrity.changes));
299
+ }
300
+ if (report.pullRequest) {
301
+ lines.push("", `### PR drift vs \`${report.pullRequest.base}\``, "", markdownChanges(report.pullRequest.changes));
302
+ }
303
+ if (github && report.status === "fail") {
304
+ lines.unshift(`::error title=Agent Surface contract drift::${report.integrity?.changes.length ?? 0} integrity change(s)`);
305
+ }
306
+ return `${lines.join("\n")}
307
+ `;
308
+ }
309
+ function renderReport(report, format, options = {}) {
310
+ if (format === "json") return canonicalJson(report, true);
311
+ if (format === "markdown") return markdownReport(report);
312
+ if (format === "github") return markdownReport(report, true);
313
+ return humanReport(report, options);
314
+ }
315
+
316
+ export {
317
+ diffContracts,
318
+ changeCounts,
319
+ sectionTitle,
320
+ groupByDeclaration,
321
+ CONTRACT_HEADERS,
322
+ contractRow,
323
+ contractCells,
324
+ contractHeading,
325
+ headline,
326
+ CONTRACT_CAVEAT,
327
+ contractColumns,
328
+ contractLine,
329
+ showsContract,
330
+ summaryFields,
331
+ renderReport
332
+ };
333
+ //# sourceMappingURL=chunk-KMAH2WU5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/diff.ts","../src/report.ts"],"sourcesContent":["import type { CapabilityContractEntry, CapabilityContractManifest } from \"@agent-surface/core\";\n\nexport type ChangeClassification = \"widening\" | \"narrowing\" | \"neutral\";\nexport type ChangeKind = \"added\" | \"removed\" | \"changed\";\n\nexport interface ContractChange {\n declarationId: string;\n capabilityId: string;\n kind: ChangeKind;\n field: string;\n classification: ChangeClassification;\n before?: unknown;\n after?: unknown;\n}\n\nconst RISK: Record<string, number> = {\n read: 0,\n \"local-state\": 1,\n navigation: 2,\n \"server-query\": 3,\n \"server-mutation\": 4,\n \"external-side-effect\": 5,\n destructive: 6,\n};\n\nconst CONFIRMATION: Record<string, number> = { never: 0, optional: 1, required: 2 };\n\nfunction key(entry: CapabilityContractEntry): string {\n return `${entry.declarationId}\\0${entry.capabilityId}`;\n}\n\nfunction equal(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\nfunction stringSet(value: unknown): Set<string> {\n return new Set(Array.isArray(value) ? value.map((item) => JSON.stringify(item)) : []);\n}\n\nfunction setDirection(before: unknown, after: unknown): \"added\" | \"removed\" | \"mixed\" | \"same\" {\n const a = stringSet(before);\n const b = stringSet(after);\n const added = [...b].some((item) => !a.has(item));\n const removed = [...a].some((item) => !b.has(item));\n if (added && removed) return \"mixed\";\n if (added) return \"added\";\n if (removed) return \"removed\";\n return \"same\";\n}\n\nfunction classify(field: string, before: unknown, after: unknown): ChangeClassification {\n if (field === \"targets\") {\n const direction = setDirection(before, after);\n return direction === \"added\" ? \"widening\" : direction === \"removed\" ? \"narrowing\" : \"neutral\";\n }\n if (field === \"policies\") {\n const direction = setDirection(before, after);\n return direction === \"removed\" || direction === \"mixed\"\n ? \"widening\"\n : direction === \"added\"\n ? \"narrowing\"\n : \"neutral\";\n }\n if (field === \"confirmation\") {\n const a = CONFIRMATION[String(before ?? \"never\")] ?? 0;\n const b = CONFIRMATION[String(after ?? \"never\")] ?? 0;\n return b < a ? \"widening\" : b > a ? \"narrowing\" : \"neutral\";\n }\n if (field === \"effect\") {\n const a = RISK[String(before)] ?? 0;\n const b = RISK[String(after)] ?? 0;\n // Lowering declared risk weakens review/approval posture.\n return b < a ? \"widening\" : b > a ? \"narrowing\" : \"neutral\";\n }\n return \"neutral\";\n}\n\nexport function diffContracts(\n before: CapabilityContractManifest | undefined,\n after: CapabilityContractManifest,\n): ContractChange[] {\n if (!before) {\n return after.capabilities.map((entry) => ({\n declarationId: entry.declarationId,\n capabilityId: entry.capabilityId,\n kind: \"added\",\n field: \"capability\",\n classification: \"widening\",\n after: entry,\n }));\n }\n const left = new Map(before.capabilities.map((entry) => [key(entry), entry]));\n const right = new Map(after.capabilities.map((entry) => [key(entry), entry]));\n const changes: ContractChange[] = [];\n for (const entryKey of [...new Set([...left.keys(), ...right.keys()])].sort()) {\n const a = left.get(entryKey);\n const b = right.get(entryKey);\n if (!a && b) {\n changes.push({\n declarationId: b.declarationId,\n capabilityId: b.capabilityId,\n kind: \"added\",\n field: \"capability\",\n classification: \"widening\",\n after: b,\n });\n continue;\n }\n if (a && !b) {\n changes.push({\n declarationId: a.declarationId,\n capabilityId: a.capabilityId,\n kind: \"removed\",\n field: \"capability\",\n classification: \"narrowing\",\n before: a,\n });\n continue;\n }\n if (!a || !b) continue;\n const fields = new Set([...Object.keys(a), ...Object.keys(b)]);\n fields.delete(\"declarationId\");\n fields.delete(\"capabilityId\");\n fields.delete(\"contractHash\");\n for (const field of [...fields].sort()) {\n const beforeValue = (a as unknown as Record<string, unknown>)[field];\n const afterValue = (b as unknown as Record<string, unknown>)[field];\n if (equal(beforeValue, afterValue)) continue;\n changes.push({\n declarationId: b.declarationId,\n capabilityId: b.capabilityId,\n kind: \"changed\",\n field,\n classification: classify(field, beforeValue, afterValue),\n before: beforeValue,\n after: afterValue,\n });\n }\n }\n return changes;\n}\n\nexport function changeCounts(changes: readonly ContractChange[]): Record<ChangeClassification, number> {\n return {\n widening: changes.filter((change) => change.classification === \"widening\").length,\n narrowing: changes.filter((change) => change.classification === \"narrowing\").length,\n neutral: changes.filter((change) => change.classification === \"neutral\").length,\n };\n}\n","import { relative } from \"node:path\";\nimport type { CapabilityContractEntry, CapabilityContractManifest } from \"@agent-surface/core\";\nimport { canonicalJson } from \"@agent-surface/compiler\";\nimport type { ContractChange } from \"./diff.js\";\nimport { changeCounts } from \"./diff.js\";\n\nexport type OutputFormat = \"human\" | \"json\" | \"github\" | \"markdown\";\n\n/**\n * Presentation-only inputs. They never reach the report model, because `--json`\n * is canonical machine output and a checkout path would make it differ between\n * two machines that compiled the same source.\n */\nexport interface RenderOptions {\n /** Application root, used to show the snapshot path as the user typed it. */\n root?: string;\n /** Include descriptions, confirmation, policies, and tags. */\n detail?: boolean;\n}\n\nexport interface ContractReport {\n command: \"inspect\" | \"check\" | \"snapshot\";\n status: \"pass\" | \"fail\" | \"written\" | \"view\";\n manifest: CapabilityContractManifest;\n snapshotPath: string;\n integrity?: {\n status: \"current\" | \"missing\" | \"stale\";\n changes: ContractChange[];\n };\n pullRequest?: {\n base: string;\n changes: ContractChange[];\n };\n}\n\nfunction value(value: unknown): string {\n if (value === undefined) return \"—\";\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n return text.length > 100 ? `${text.slice(0, 97)}…` : text;\n}\n\nfunction changeLine(change: ContractChange): string {\n const mark = change.kind === \"added\" ? \"+\" : change.kind === \"removed\" ? \"-\" : \"~\";\n return `${mark} ${change.classification.padEnd(9)} ${change.capabilityId} · ${change.declarationId} · ${change.field}${\n change.kind === \"changed\" ? `: ${value(change.before)} → ${value(change.after)}` : \"\"\n }`;\n}\n\nexport function sectionTitle(title: string, changes: readonly ContractChange[]): string {\n const counts = changeCounts(changes);\n return `${title} (${changes.length}) · widening ${counts.widening} · narrowing ${counts.narrowing} · neutral ${counts.neutral}`;\n}\n\nfunction section(title: string, changes: readonly ContractChange[]): string[] {\n return [sectionTitle(title, changes), ...(changes.length > 0 ? changes.map(changeLine) : [\"no changes\"])];\n}\n\n/**\n * Effects in escalating order of reach, so a renderer can grade one without\n * restating the vocabulary. Anything unknown sorts last: a capability whose\n * effect this CLI does not recognize is the one worth looking at.\n */\nconst EFFECT_ORDER = [\n \"read\",\n \"local-state\",\n \"navigation\",\n \"server-query\",\n \"server-mutation\",\n \"external-side-effect\",\n \"destructive\",\n] as const;\n\nexport function effectRank(effect: string): number {\n const index = EFFECT_ORDER.indexOf(effect as (typeof EFFECT_ORDER)[number]);\n return index === -1 ? EFFECT_ORDER.length : index;\n}\n\nexport interface ContractGroup {\n declarationId: string;\n entries: CapabilityContractEntry[];\n}\n\n/** Capabilities grouped by the declaration that owns them, input order kept. */\nexport function groupByDeclaration(capabilities: readonly CapabilityContractEntry[]): ContractGroup[] {\n const groups = new Map<string, CapabilityContractEntry[]>();\n for (const entry of capabilities) {\n const existing = groups.get(entry.declarationId);\n if (existing) existing.push(entry);\n else groups.set(entry.declarationId, [entry]);\n }\n return [...groups].map(([declarationId, entries]) => ({ declarationId, entries }));\n}\n\nexport type Reach = \"low\" | \"medium\" | \"high\";\n\n/**\n * The effect ladder, graded. The effect is the fact; the grade is how it reads\n * to someone who has not memorised the ladder. It is printed as a word rather\n * than left to colour alone, because the streams that matter most — a pipe, a\n * CI log, `--plain` — have no colour to read.\n */\nexport function reachOf(effect: string): Reach {\n const rank = effectRank(effect);\n if (rank <= 1) return \"low\";\n if (rank <= 3) return \"medium\";\n return \"high\";\n}\n\n/** Nothing declared. A column still has to read as a column. */\nconst NONE = \"—\";\n\nexport const CONTRACT_HEADERS = [\"CAPABILITY\", \"KIND\", \"EFFECT\", \"REACH\", \"CONFIRM\", \"POLICIES\"] as const;\n\nexport interface ContractRow {\n capabilityId: string;\n kind: string;\n effect: string;\n reach: Reach;\n confirm: string;\n policies: string;\n /** Description and tags, shown under --detail. */\n note: string;\n}\n\nexport function contractRow(entry: CapabilityContractEntry): ContractRow {\n const policies = (entry.policies ?? []).map((policy) => `${policy.name}${policy.phase ? `@${policy.phase}` : \"\"}`);\n const tags = (entry.tags ?? []).map((tag) => `#${tag}`).join(\" \");\n return {\n capabilityId: entry.capabilityId,\n kind: entry.kind,\n effect: entry.effect,\n reach: reachOf(entry.effect),\n confirm: entry.confirmation ?? NONE,\n policies: policies.length > 0 ? policies.join(\", \") : NONE,\n note: [entry.description, tags].filter(Boolean).join(\" \"),\n };\n}\n\nexport function contractCells(row: ContractRow): string[] {\n return [row.capabilityId, row.kind, row.effect, row.reach, row.confirm, row.policies];\n}\n\nfunction tally(values: readonly string[]): string {\n const counts = new Map<string, number>();\n for (const value of values) counts.set(value, (counts.get(value) ?? 0) + 1);\n return [...counts]\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n .map(([name, count]) => `${count} ${name}`)\n .join(\" · \");\n}\n\nfunction plural(count: number, noun: string, suffix = \"s\"): string {\n return `${count} ${noun}${count === 1 ? \"\" : suffix}`;\n}\n\nexport function contractHeading(manifest: CapabilityContractManifest): string {\n const count = manifest.capabilities.length;\n const groups = groupByDeclaration(manifest.capabilities).length;\n return `REPOSITORY CONTRACT · ${count} ${count === 1 ? \"capability\" : \"capabilities\"} · ${plural(groups, \"declaration\")}`;\n}\n\n/**\n * The answer, before the evidence for it. A reader who stops after this line\n * should still know how large the surface is and how much of it is gated.\n */\nexport function headline(manifest: CapabilityContractManifest): string[] {\n const capabilities = manifest.capabilities;\n const count = capabilities.length;\n const gated = capabilities.filter((entry) => entry.confirmation && entry.confirmation !== \"never\").length;\n const policed = capabilities.filter((entry) => (entry.policies ?? []).length > 0).length;\n // Along the ladder, not by frequency: \"34 low · 1 high · 1 medium\" reads as\n // a miscount rather than a distribution.\n const reach = ([\"low\", \"medium\", \"high\"] as const)\n .map((grade) => [grade, capabilities.filter((entry) => reachOf(entry.effect) === grade).length] as const)\n .filter(([, count]) => count > 0)\n .map(([grade, count]) => `${count} ${grade}`)\n .join(\" · \");\n return [\n `${count} ${count === 1 ? \"capability\" : \"capabilities\"} · ${plural(\n groupByDeclaration(capabilities).length,\n \"declaration\",\n )}${count > 0 ? ` · ${tally(capabilities.map((entry) => entry.kind))}` : \"\"}`,\n `reach ${reach || NONE} · declared gates: ${gated} confirmation · ${policed} policy`,\n ];\n}\n\n/**\n * What a compiled contract cannot tell you. The columns above are declarations\n * the graph proves are reachable — not a transcript of a run, and a policy's\n * verdict is not knowable until there is an actor and an input to judge.\n */\nexport const CONTRACT_CAVEAT = [\n \"Declarations, compiled from the production graph — what this code can expose,\",\n \"not what a mount exposed at runtime. CONFIRM and POLICIES are declared per\",\n \"capability; whether a policy admits, denies or hides one depends on the actor,\",\n \"input and context of a real invocation, which this command never performs.\",\n];\n\n/** Column widths shared by the plain and drawn renderers, so both align alike. */\nexport function contractColumns(capabilities: readonly CapabilityContractEntry[]): number[] {\n const rows = capabilities.map((entry) => contractCells(contractRow(entry)));\n return CONTRACT_HEADERS.map((header, index) =>\n Math.max(header.length, ...rows.map((cells) => cells[index]?.length ?? 0)),\n );\n}\n\n/** One table line, padded to the shared widths. The last cell is never padded. */\nexport function contractLine(cells: readonly string[], widths: readonly number[]): string {\n return cells\n .map((cell, index) => (index === cells.length - 1 ? cell : cell.padEnd(widths[index] ?? 0)))\n .join(\" \")\n .trimEnd();\n}\n\n/** Show the snapshot where the user would type it, not as an absolute path. */\nexport function displayPath(path: string, root?: string): string {\n if (!root) return path;\n const relativePath = relative(root, path);\n return !relativePath || relativePath.startsWith(\"..\") ? path : relativePath;\n}\n\n/**\n * `inspect` exists to show the inventory. The other commands lead with a\n * verdict and keep it out of the way — until `--detail` is asked for, because\n * the reason to ask a gate for detail is to read what it gated.\n */\nexport function showsContract(report: ContractReport, options: RenderOptions = {}): boolean {\n return report.command === \"inspect\" || options.detail === true;\n}\n\n/** Provenance. True, needed, and not what the reader came for — so it sits below. */\nexport function summaryFields(report: ContractReport, options: RenderOptions = {}): [string, string][] {\n const { manifest } = report;\n const fields: [string, string][] = [\n [\"Contract\", manifest.hash],\n [\"Compiler\", manifest.compilerVersion],\n [\"Completeness\", manifest.completeness.status],\n [\"Targets\", manifest.targets.join(\", \") || NONE],\n [\"Snapshot\", displayPath(report.snapshotPath, options.root)],\n ];\n if (report.integrity) fields.push([\"Integrity\", report.integrity.status]);\n return fields;\n}\n\nexport function humanReport(report: ContractReport, options: RenderOptions = {}): string {\n const fields = summaryFields(report, options);\n const label = Math.max(...fields.map(([name]) => name.length));\n const lines = [\n `AGENT SURFACE ${report.command.toUpperCase()} · ${report.status.toUpperCase()}`,\n \"\",\n ...headline(report.manifest),\n \"\",\n ...fields.map(([name, value]) => `${name.padEnd(label)} ${value}`),\n ];\n if (report.integrity) lines.push(\"\", ...section(\"SOURCE ↔ SNAPSHOT\", report.integrity.changes));\n if (report.pullRequest) {\n lines.push(\"\", ...section(`PR DRIFT vs ${report.pullRequest.base}`, report.pullRequest.changes));\n }\n if (showsContract(report, options)) {\n const widths = contractColumns(report.manifest.capabilities);\n lines.push(\"\", contractHeading(report.manifest), \"\", ` ${contractLine(CONTRACT_HEADERS, widths)}`);\n for (const group of groupByDeclaration(report.manifest.capabilities)) {\n lines.push(\"\", `${group.declarationId} (${group.entries.length})`);\n for (const entry of group.entries) {\n const row = contractRow(entry);\n lines.push(` ${contractLine(contractCells(row), widths)}`);\n if (options.detail && row.note) lines.push(` ${row.note}`);\n }\n }\n lines.push(\"\", ...CONTRACT_CAVEAT);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction markdownChanges(changes: readonly ContractChange[]): string {\n if (changes.length === 0) return \"No changes.\\n\";\n return [\n \"| Class | Change | Capability | Declaration | Field |\",\n \"|---|---|---|---|---|\",\n ...changes.map(\n (change) =>\n `| ${change.classification} | ${change.kind} | \\`${change.capabilityId}\\` | \\`${change.declarationId}\\` | \\`${change.field}\\` |`,\n ),\n ].join(\"\\n\");\n}\n\nexport function markdownReport(report: ContractReport, github = false): string {\n const lines = [\n `## Agent Surface ${report.command} — ${report.status}`,\n \"\",\n `- Contract: \\`${report.manifest.hash}\\``,\n `- Completeness: **${report.manifest.completeness.status}**`,\n `- Capabilities: **${report.manifest.capabilities.length}**`,\n `- Targets: ${report.manifest.targets.map((target) => `\\`${target}\\``).join(\", \") || \"—\"}`,\n ];\n if (report.integrity) {\n lines.push(\"\", `### Source ↔ snapshot — ${report.integrity.status}`, \"\", markdownChanges(report.integrity.changes));\n }\n if (report.pullRequest) {\n lines.push(\"\", `### PR drift vs \\`${report.pullRequest.base}\\``, \"\", markdownChanges(report.pullRequest.changes));\n }\n if (github && report.status === \"fail\") {\n lines.unshift(`::error title=Agent Surface contract drift::${report.integrity?.changes.length ?? 0} integrity change(s)`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function renderReport(report: ContractReport, format: OutputFormat, options: RenderOptions = {}): string {\n if (format === \"json\") return canonicalJson(report, true);\n if (format === \"markdown\") return markdownReport(report);\n if (format === \"github\") return markdownReport(report, true);\n return humanReport(report, options);\n}\n"],"mappings":"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","names":["value","value","count"]}
@@ -0,0 +1,51 @@
1
+ // src/files.ts
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
3
+ import { dirname, isAbsolute, relative, resolve, sep } from "path";
4
+ import { spawnSync } from "child_process";
5
+ import { canonicalManifestJson, verifyManifest } from "@agent-surface/compiler";
6
+ var DEFAULT_SNAPSHOT = ".agent-surface/contract.json";
7
+ function readManifest(path) {
8
+ if (!existsSync(path)) return void 0;
9
+ try {
10
+ const manifest = JSON.parse(readFileSync(path, "utf8"));
11
+ verifyManifest(manifest);
12
+ return manifest;
13
+ } catch (error) {
14
+ throw new Error(`could not read contract ${path}: ${error instanceof Error ? error.message : String(error)}`);
15
+ }
16
+ }
17
+ function writeManifest(path, manifest) {
18
+ mkdirSync(dirname(path), { recursive: true });
19
+ writeFileSync(path, canonicalManifestJson(manifest), "utf8");
20
+ }
21
+ function git(args, cwd) {
22
+ const result = spawnSync("git", args, { cwd, encoding: "utf8" });
23
+ if (result.status !== 0) {
24
+ throw new Error(result.stderr.trim() || `git ${args.join(" ")} failed`);
25
+ }
26
+ return result.stdout.trim();
27
+ }
28
+ function readBaseManifest(base, snapshotPath, cwd) {
29
+ const root = git(["rev-parse", "--show-toplevel"], cwd);
30
+ const absolute = isAbsolute(snapshotPath) ? snapshotPath : resolve(cwd, snapshotPath);
31
+ const path = relative(root, absolute);
32
+ if (path === ".." || path.startsWith(`..${sep}`) || isAbsolute(path)) {
33
+ throw new Error(`snapshot ${snapshotPath} is outside the Git worktree`);
34
+ }
35
+ const text = git(["show", `${base}:${path.split(sep).join("/")}`], root);
36
+ try {
37
+ const manifest = JSON.parse(text);
38
+ verifyManifest(manifest);
39
+ return manifest;
40
+ } catch (error) {
41
+ throw new Error(`could not read ${snapshotPath} at ${base}: ${error instanceof Error ? error.message : String(error)}`);
42
+ }
43
+ }
44
+
45
+ export {
46
+ DEFAULT_SNAPSHOT,
47
+ readManifest,
48
+ writeManifest,
49
+ readBaseManifest
50
+ };
51
+ //# sourceMappingURL=chunk-XCQACLOT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/files.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport type { CapabilityContractManifest } from \"@agent-surface/core\";\nimport { canonicalManifestJson, verifyManifest } from \"@agent-surface/compiler\";\n\nexport const DEFAULT_SNAPSHOT = \".agent-surface/contract.json\";\n\nexport function readManifest(path: string): CapabilityContractManifest | undefined {\n if (!existsSync(path)) return undefined;\n try {\n const manifest = JSON.parse(readFileSync(path, \"utf8\")) as CapabilityContractManifest;\n verifyManifest(manifest);\n return manifest;\n } catch (error) {\n throw new Error(`could not read contract ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\nexport function writeManifest(path: string, manifest: CapabilityContractManifest): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, canonicalManifestJson(manifest), \"utf8\");\n}\n\nfunction git(args: string[], cwd: string): string {\n const result = spawnSync(\"git\", args, { cwd, encoding: \"utf8\" });\n if (result.status !== 0) {\n throw new Error(result.stderr.trim() || `git ${args.join(\" \")} failed`);\n }\n return result.stdout.trim();\n}\n\nexport function readBaseManifest(\n base: string,\n snapshotPath: string,\n cwd: string,\n): CapabilityContractManifest {\n const root = git([\"rev-parse\", \"--show-toplevel\"], cwd);\n const absolute = isAbsolute(snapshotPath) ? snapshotPath : resolve(cwd, snapshotPath);\n const path = relative(root, absolute);\n if (path === \"..\" || path.startsWith(`..${sep}`) || isAbsolute(path)) {\n throw new Error(`snapshot ${snapshotPath} is outside the Git worktree`);\n }\n const text = git([\"show\", `${base}:${path.split(sep).join(\"/\")}`], root);\n try {\n const manifest = JSON.parse(text) as CapabilityContractManifest;\n verifyManifest(manifest);\n return manifest;\n } catch (error) {\n throw new Error(`could not read ${snapshotPath} at ${base}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n"],"mappings":";AAAA,SAAS,YAAY,WAAW,cAAc,qBAAqB;AACnE,SAAS,SAAS,YAAY,UAAU,SAAS,WAAW;AAC5D,SAAS,iBAAiB;AAE1B,SAAS,uBAAuB,sBAAsB;AAE/C,IAAM,mBAAmB;AAEzB,SAAS,aAAa,MAAsD;AACjF,MAAI,CAAC,WAAW,IAAI,EAAG,QAAO;AAC9B,MAAI;AACF,UAAM,WAAW,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AACtD,mBAAe,QAAQ;AACvB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,2BAA2B,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,EAC9G;AACF;AAEO,SAAS,cAAc,MAAc,UAA4C;AACtF,YAAU,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC5C,gBAAc,MAAM,sBAAsB,QAAQ,GAAG,MAAM;AAC7D;AAEA,SAAS,IAAI,MAAgB,KAAqB;AAChD,QAAM,SAAS,UAAU,OAAO,MAAM,EAAE,KAAK,UAAU,OAAO,CAAC;AAC/D,MAAI,OAAO,WAAW,GAAG;AACvB,UAAM,IAAI,MAAM,OAAO,OAAO,KAAK,KAAK,OAAO,KAAK,KAAK,GAAG,CAAC,SAAS;AAAA,EACxE;AACA,SAAO,OAAO,OAAO,KAAK;AAC5B;AAEO,SAAS,iBACd,MACA,cACA,KAC4B;AAC5B,QAAM,OAAO,IAAI,CAAC,aAAa,iBAAiB,GAAG,GAAG;AACtD,QAAM,WAAW,WAAW,YAAY,IAAI,eAAe,QAAQ,KAAK,YAAY;AACpF,QAAM,OAAO,SAAS,MAAM,QAAQ;AACpC,MAAI,SAAS,QAAQ,KAAK,WAAW,KAAK,GAAG,EAAE,KAAK,WAAW,IAAI,GAAG;AACpE,UAAM,IAAI,MAAM,YAAY,YAAY,8BAA8B;AAAA,EACxE;AACA,QAAM,OAAO,IAAI,CAAC,QAAQ,GAAG,IAAI,IAAI,KAAK,MAAM,GAAG,EAAE,KAAK,GAAG,CAAC,EAAE,GAAG,IAAI;AACvE,MAAI;AACF,UAAM,WAAW,KAAK,MAAM,IAAI;AAChC,mBAAe,QAAQ;AACvB,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,kBAAkB,YAAY,OAAO,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,EACxH;AACF;","names":[]}
package/dist/index.d.ts CHANGED
@@ -19,6 +19,17 @@ declare function readManifest(path: string): CapabilityContractManifest | undefi
19
19
  declare function writeManifest(path: string, manifest: CapabilityContractManifest): void;
20
20
 
21
21
  type OutputFormat = "human" | "json" | "github" | "markdown";
22
+ /**
23
+ * Presentation-only inputs. They never reach the report model, because `--json`
24
+ * is canonical machine output and a checkout path would make it differ between
25
+ * two machines that compiled the same source.
26
+ */
27
+ interface RenderOptions {
28
+ /** Application root, used to show the snapshot path as the user typed it. */
29
+ root?: string;
30
+ /** Include descriptions, confirmation, policies, and tags. */
31
+ detail?: boolean;
32
+ }
22
33
  interface ContractReport {
23
34
  command: "inspect" | "check" | "snapshot";
24
35
  status: "pass" | "fail" | "written" | "view";
@@ -33,6 +44,6 @@ interface ContractReport {
33
44
  changes: ContractChange[];
34
45
  };
35
46
  }
36
- declare function renderReport(report: ContractReport, format: OutputFormat): string;
47
+ declare function renderReport(report: ContractReport, format: OutputFormat, options?: RenderOptions): string;
37
48
 
38
49
  export { type ChangeClassification, type ChangeKind, type ContractChange, type ContractReport, DEFAULT_SNAPSHOT, type OutputFormat, changeCounts, diffContracts, readManifest, renderReport, writeManifest };
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  DEFAULT_SNAPSHOT,
3
3
  readManifest,
4
- renderReport,
5
4
  writeManifest
6
- } from "./chunk-X6RGGBQ4.js";
5
+ } from "./chunk-XCQACLOT.js";
7
6
  import {
8
7
  changeCounts,
9
- diffContracts
10
- } from "./chunk-IA7FUP4R.js";
8
+ diffContracts,
9
+ renderReport
10
+ } from "./chunk-KMAH2WU5.js";
11
11
  export {
12
12
  DEFAULT_SNAPSHOT,
13
13
  changeCounts,
@@ -0,0 +1,146 @@
1
+ import {
2
+ CONTRACT_CAVEAT,
3
+ CONTRACT_HEADERS,
4
+ contractCells,
5
+ contractColumns,
6
+ contractHeading,
7
+ contractLine,
8
+ contractRow,
9
+ groupByDeclaration,
10
+ headline,
11
+ sectionTitle,
12
+ showsContract,
13
+ summaryFields
14
+ } from "./chunk-KMAH2WU5.js";
15
+
16
+ // src/ink.tsx
17
+ import { Box, Text, render } from "ink";
18
+ import { jsx, jsxs } from "react/jsx-runtime";
19
+ var REACH_COLOR = { low: "green", medium: "yellow", high: "red" };
20
+ function changeColor(change) {
21
+ if (change.classification === "widening") return "yellow";
22
+ if (change.classification === "narrowing") return "cyan";
23
+ return "gray";
24
+ }
25
+ function pad(text, width) {
26
+ return text.padEnd(width);
27
+ }
28
+ function Summary({ report, options }) {
29
+ const fields = summaryFields(report, options);
30
+ const label = Math.max(...fields.map(([name]) => name.length));
31
+ return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: fields.map(([name, value]) => /* @__PURE__ */ jsxs(Text, { children: [
32
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: pad(name, label) }),
33
+ " ",
34
+ /* @__PURE__ */ jsx(Text, { color: name === "Integrity" && value !== "current" ? "yellow" : void 0, children: value })
35
+ ] }, name)) });
36
+ }
37
+ function Changes({ title, changes }) {
38
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
39
+ /* @__PURE__ */ jsx(Text, { bold: true, children: sectionTitle(title, changes) }),
40
+ changes.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "no changes" }) : changes.map((change, index) => /* @__PURE__ */ jsxs(
41
+ Text,
42
+ {
43
+ color: changeColor(change),
44
+ children: [
45
+ change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~",
46
+ " ",
47
+ change.classification.padEnd(9),
48
+ " ",
49
+ change.capabilityId,
50
+ " \xB7 ",
51
+ change.field
52
+ ]
53
+ },
54
+ `${change.capabilityId}\0${change.declarationId}\0${change.field}\0${index}`
55
+ ))
56
+ ] });
57
+ }
58
+ function Capability({
59
+ entry,
60
+ widths,
61
+ detail
62
+ }) {
63
+ const row = contractRow(entry);
64
+ const [id, kind, effect, reach, confirm, policies] = contractCells(row);
65
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
66
+ /* @__PURE__ */ jsxs(Text, { children: [
67
+ " ",
68
+ pad(id ?? "", widths[0] ?? 0),
69
+ " ",
70
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: pad(kind ?? "", widths[1] ?? 0) }),
71
+ " ",
72
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: pad(effect ?? "", widths[2] ?? 0) }),
73
+ " ",
74
+ /* @__PURE__ */ jsx(Text, { color: REACH_COLOR[row.reach], children: pad(reach ?? "", widths[3] ?? 0) }),
75
+ " ",
76
+ /* @__PURE__ */ jsx(Text, { color: row.confirm === "required" ? "magenta" : void 0, dimColor: row.confirm === "\u2014", children: pad(confirm ?? "", widths[4] ?? 0) }),
77
+ " ",
78
+ /* @__PURE__ */ jsx(Text, { color: "magenta", dimColor: row.policies === "\u2014", children: policies })
79
+ ] }),
80
+ detail && row.note ? /* @__PURE__ */ jsx(Box, { paddingLeft: 6, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.note }) }) : null
81
+ ] });
82
+ }
83
+ function App({ report, options }) {
84
+ const ok = report.status === "pass" || report.status === "written" || report.status === "view";
85
+ const widths = contractColumns(report.manifest.capabilities);
86
+ const [size, gates] = headline(report.manifest);
87
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
88
+ /* @__PURE__ */ jsxs(Text, { bold: true, color: ok ? "green" : "red", children: [
89
+ "AGENT SURFACE ",
90
+ report.command.toUpperCase(),
91
+ " \xB7 ",
92
+ report.status.toUpperCase()
93
+ ] }),
94
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
95
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: size }),
96
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: gates })
97
+ ] }),
98
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Summary, { report, options }) }),
99
+ report.integrity ? /* @__PURE__ */ jsx(Changes, { title: "SOURCE \u2194 SNAPSHOT", changes: report.integrity.changes }) : null,
100
+ report.pullRequest ? /* @__PURE__ */ jsx(Changes, { title: `PR DRIFT vs ${report.pullRequest.base}`, changes: report.pullRequest.changes }) : null,
101
+ showsContract(report, options) ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
102
+ /* @__PURE__ */ jsx(Text, { bold: true, children: contractHeading(report.manifest) }),
103
+ /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${contractLine(CONTRACT_HEADERS, widths)}` }) }),
104
+ groupByDeclaration(report.manifest.capabilities).map((group) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
105
+ /* @__PURE__ */ jsxs(Text, { children: [
106
+ /* @__PURE__ */ jsx(Text, { color: "blue", children: group.declarationId }),
107
+ " ",
108
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
109
+ "(",
110
+ group.entries.length,
111
+ ")"
112
+ ] })
113
+ ] }),
114
+ group.entries.map((entry) => /* @__PURE__ */ jsx(
115
+ Capability,
116
+ {
117
+ entry,
118
+ widths,
119
+ detail: options.detail === true
120
+ },
121
+ entry.capabilityId
122
+ ))
123
+ ] }, group.declarationId)),
124
+ /* @__PURE__ */ jsx(
125
+ Box,
126
+ {
127
+ flexDirection: "column",
128
+ alignSelf: "flex-start",
129
+ marginTop: 1,
130
+ borderStyle: "round",
131
+ borderColor: "gray",
132
+ paddingX: 1,
133
+ children: CONTRACT_CAVEAT.map((line) => /* @__PURE__ */ jsx(Text, { dimColor: true, children: line }, line))
134
+ }
135
+ )
136
+ ] }) : null
137
+ ] });
138
+ }
139
+ async function renderInk(report, options = {}) {
140
+ const instance = render(/* @__PURE__ */ jsx(App, { report, options }));
141
+ await instance.waitUntilExit();
142
+ }
143
+ export {
144
+ renderInk
145
+ };
146
+ //# sourceMappingURL=ink-4CUU77KD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ink.tsx"],"sourcesContent":["import { Box, Text, render } from \"ink\";\nimport type { CapabilityContractEntry } from \"@agent-surface/core\";\nimport type { ContractChange } from \"./diff.js\";\nimport type { ContractReport, Reach, RenderOptions } from \"./report.js\";\nimport {\n CONTRACT_CAVEAT,\n CONTRACT_HEADERS,\n contractCells,\n contractColumns,\n contractHeading,\n contractLine,\n contractRow,\n groupByDeclaration,\n headline,\n sectionTitle,\n showsContract,\n summaryFields,\n} from \"./report.js\";\n\n/** Colour repeats the REACH word; it never carries the grade on its own. */\nconst REACH_COLOR: Record<Reach, string> = { low: \"green\", medium: \"yellow\", high: \"red\" };\n\nfunction changeColor(change: ContractChange): string {\n if (change.classification === \"widening\") return \"yellow\";\n if (change.classification === \"narrowing\") return \"cyan\";\n return \"gray\";\n}\n\nfunction pad(text: string, width: number): string {\n return text.padEnd(width);\n}\n\nfunction Summary({ report, options }: { report: ContractReport; options: RenderOptions }) {\n const fields = summaryFields(report, options);\n const label = Math.max(...fields.map(([name]) => name.length));\n return (\n <Box flexDirection=\"column\">\n {fields.map(([name, value]) => (\n <Text key={name}>\n <Text dimColor>{pad(name, label)}</Text>\n {\" \"}\n <Text color={name === \"Integrity\" && value !== \"current\" ? \"yellow\" : undefined}>{value}</Text>\n </Text>\n ))}\n </Box>\n );\n}\n\nfunction Changes({ title, changes }: { title: string; changes: ContractChange[] }) {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text bold>{sectionTitle(title, changes)}</Text>\n {changes.length === 0 ? (\n <Text dimColor>no changes</Text>\n ) : (\n changes.map((change, index) => (\n <Text\n key={`${change.capabilityId}\\0${change.declarationId}\\0${change.field}\\0${index}`}\n color={changeColor(change)}\n >\n {change.kind === \"added\" ? \"+\" : change.kind === \"removed\" ? \"-\" : \"~\"} {change.classification.padEnd(9)}{\" \"}\n {change.capabilityId} · {change.field}\n </Text>\n ))\n )}\n </Box>\n );\n}\n\nfunction Capability({\n entry,\n widths,\n detail,\n}: {\n entry: CapabilityContractEntry;\n widths: number[];\n detail: boolean;\n}) {\n const row = contractRow(entry);\n const [id, kind, effect, reach, confirm, policies] = contractCells(row);\n return (\n <Box flexDirection=\"column\">\n <Text>\n {\" \"}\n {pad(id ?? \"\", widths[0] ?? 0)}\n {\" \"}\n <Text dimColor>{pad(kind ?? \"\", widths[1] ?? 0)}</Text>\n {\" \"}\n <Text dimColor>{pad(effect ?? \"\", widths[2] ?? 0)}</Text>\n {\" \"}\n <Text color={REACH_COLOR[row.reach]}>{pad(reach ?? \"\", widths[3] ?? 0)}</Text>\n {\" \"}\n <Text color={row.confirm === \"required\" ? \"magenta\" : undefined} dimColor={row.confirm === \"—\"}>\n {pad(confirm ?? \"\", widths[4] ?? 0)}\n </Text>\n {\" \"}\n <Text color=\"magenta\" dimColor={row.policies === \"—\"}>\n {policies}\n </Text>\n </Text>\n {/* Padding rather than a prefix, so a wrapped description stays in its column. */}\n {detail && row.note ? (\n <Box paddingLeft={6}>\n <Text dimColor>{row.note}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction App({ report, options }: { report: ContractReport; options: RenderOptions }) {\n const ok = report.status === \"pass\" || report.status === \"written\" || report.status === \"view\";\n const widths = contractColumns(report.manifest.capabilities);\n const [size, gates] = headline(report.manifest);\n return (\n <Box flexDirection=\"column\">\n <Text bold color={ok ? \"green\" : \"red\"}>\n AGENT SURFACE {report.command.toUpperCase()} · {report.status.toUpperCase()}\n </Text>\n <Box flexDirection=\"column\" marginTop={1}>\n <Text bold color=\"cyan\">\n {size}\n </Text>\n <Text dimColor>{gates}</Text>\n </Box>\n <Box marginTop={1}>\n <Summary report={report} options={options} />\n </Box>\n {report.integrity ? <Changes title=\"SOURCE ↔ SNAPSHOT\" changes={report.integrity.changes} /> : null}\n {report.pullRequest ? (\n <Changes title={`PR DRIFT vs ${report.pullRequest.base}`} changes={report.pullRequest.changes} />\n ) : null}\n {showsContract(report, options) ? (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text bold>{contractHeading(report.manifest)}</Text>\n <Box marginTop={1}>\n <Text dimColor>{` ${contractLine(CONTRACT_HEADERS, widths)}`}</Text>\n </Box>\n {groupByDeclaration(report.manifest.capabilities).map((group) => (\n <Box key={group.declarationId} flexDirection=\"column\" marginTop={1}>\n <Text>\n <Text color=\"blue\">{group.declarationId}</Text> <Text dimColor>({group.entries.length})</Text>\n </Text>\n {group.entries.map((entry) => (\n <Capability\n key={entry.capabilityId}\n entry={entry}\n widths={widths}\n detail={options.detail === true}\n />\n ))}\n </Box>\n ))}\n {/* flex-start, or the border stretches to the width of the terminal. */}\n <Box\n flexDirection=\"column\"\n alignSelf=\"flex-start\"\n marginTop={1}\n borderStyle=\"round\"\n borderColor=\"gray\"\n paddingX={1}\n >\n {CONTRACT_CAVEAT.map((line) => (\n <Text key={line} dimColor>\n {line}\n </Text>\n ))}\n </Box>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nexport async function renderInk(report: ContractReport, options: RenderOptions = {}): Promise<void> {\n const instance = render(<App report={report} options={options} />);\n await instance.waitUntilExit();\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-surface/cli",
3
- "version": "0.19.1",
3
+ "version": "0.20.1",
4
4
  "description": "Inspect and check the agent surface your app exposes — in the terminal and in CI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,8 +23,8 @@
23
23
  "ink": "^6.3.1",
24
24
  "react": "^19.1.1",
25
25
  "vite": "^7.1.3",
26
- "@agent-surface/compiler": "^0.19.1",
27
- "@agent-surface/core": "^0.19.1"
26
+ "@agent-surface/compiler": "^0.20.1",
27
+ "@agent-surface/core": "^0.20.1"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^20.19.0",
@@ -39,7 +39,7 @@
39
39
  "url": "git+https://github.com/Wiseair-srl/agent-surface.git",
40
40
  "directory": "packages/cli"
41
41
  },
42
- "homepage": "https://agent-surface-docs.vercel.app",
42
+ "homepage": "https://agent-surface.dev",
43
43
  "bugs": {
44
44
  "url": "https://github.com/Wiseair-srl/agent-surface/issues"
45
45
  },
@@ -1,125 +0,0 @@
1
- // src/diff.ts
2
- var RISK = {
3
- read: 0,
4
- "local-state": 1,
5
- navigation: 2,
6
- "server-query": 3,
7
- "server-mutation": 4,
8
- "external-side-effect": 5,
9
- destructive: 6
10
- };
11
- var CONFIRMATION = { never: 0, optional: 1, required: 2 };
12
- function key(entry) {
13
- return `${entry.declarationId}\0${entry.capabilityId}`;
14
- }
15
- function equal(a, b) {
16
- return JSON.stringify(a) === JSON.stringify(b);
17
- }
18
- function stringSet(value) {
19
- return new Set(Array.isArray(value) ? value.map((item) => JSON.stringify(item)) : []);
20
- }
21
- function setDirection(before, after) {
22
- const a = stringSet(before);
23
- const b = stringSet(after);
24
- const added = [...b].some((item) => !a.has(item));
25
- const removed = [...a].some((item) => !b.has(item));
26
- if (added && removed) return "mixed";
27
- if (added) return "added";
28
- if (removed) return "removed";
29
- return "same";
30
- }
31
- function classify(field, before, after) {
32
- if (field === "targets") {
33
- const direction = setDirection(before, after);
34
- return direction === "added" ? "widening" : direction === "removed" ? "narrowing" : "neutral";
35
- }
36
- if (field === "policies") {
37
- const direction = setDirection(before, after);
38
- return direction === "removed" || direction === "mixed" ? "widening" : direction === "added" ? "narrowing" : "neutral";
39
- }
40
- if (field === "confirmation") {
41
- const a = CONFIRMATION[String(before ?? "never")] ?? 0;
42
- const b = CONFIRMATION[String(after ?? "never")] ?? 0;
43
- return b < a ? "widening" : b > a ? "narrowing" : "neutral";
44
- }
45
- if (field === "effect") {
46
- const a = RISK[String(before)] ?? 0;
47
- const b = RISK[String(after)] ?? 0;
48
- return b < a ? "widening" : b > a ? "narrowing" : "neutral";
49
- }
50
- return "neutral";
51
- }
52
- function diffContracts(before, after) {
53
- if (!before) {
54
- return after.capabilities.map((entry) => ({
55
- declarationId: entry.declarationId,
56
- capabilityId: entry.capabilityId,
57
- kind: "added",
58
- field: "capability",
59
- classification: "widening",
60
- after: entry
61
- }));
62
- }
63
- const left = new Map(before.capabilities.map((entry) => [key(entry), entry]));
64
- const right = new Map(after.capabilities.map((entry) => [key(entry), entry]));
65
- const changes = [];
66
- for (const entryKey of [.../* @__PURE__ */ new Set([...left.keys(), ...right.keys()])].sort()) {
67
- const a = left.get(entryKey);
68
- const b = right.get(entryKey);
69
- if (!a && b) {
70
- changes.push({
71
- declarationId: b.declarationId,
72
- capabilityId: b.capabilityId,
73
- kind: "added",
74
- field: "capability",
75
- classification: "widening",
76
- after: b
77
- });
78
- continue;
79
- }
80
- if (a && !b) {
81
- changes.push({
82
- declarationId: a.declarationId,
83
- capabilityId: a.capabilityId,
84
- kind: "removed",
85
- field: "capability",
86
- classification: "narrowing",
87
- before: a
88
- });
89
- continue;
90
- }
91
- if (!a || !b) continue;
92
- const fields = /* @__PURE__ */ new Set([...Object.keys(a), ...Object.keys(b)]);
93
- fields.delete("declarationId");
94
- fields.delete("capabilityId");
95
- fields.delete("contractHash");
96
- for (const field of [...fields].sort()) {
97
- const beforeValue = a[field];
98
- const afterValue = b[field];
99
- if (equal(beforeValue, afterValue)) continue;
100
- changes.push({
101
- declarationId: b.declarationId,
102
- capabilityId: b.capabilityId,
103
- kind: "changed",
104
- field,
105
- classification: classify(field, beforeValue, afterValue),
106
- before: beforeValue,
107
- after: afterValue
108
- });
109
- }
110
- }
111
- return changes;
112
- }
113
- function changeCounts(changes) {
114
- return {
115
- widening: changes.filter((change) => change.classification === "widening").length,
116
- narrowing: changes.filter((change) => change.classification === "narrowing").length,
117
- neutral: changes.filter((change) => change.classification === "neutral").length
118
- };
119
- }
120
-
121
- export {
122
- diffContracts,
123
- changeCounts
124
- };
125
- //# sourceMappingURL=chunk-IA7FUP4R.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/diff.ts"],"sourcesContent":["import type { CapabilityContractEntry, CapabilityContractManifest } from \"@agent-surface/core\";\n\nexport type ChangeClassification = \"widening\" | \"narrowing\" | \"neutral\";\nexport type ChangeKind = \"added\" | \"removed\" | \"changed\";\n\nexport interface ContractChange {\n declarationId: string;\n capabilityId: string;\n kind: ChangeKind;\n field: string;\n classification: ChangeClassification;\n before?: unknown;\n after?: unknown;\n}\n\nconst RISK: Record<string, number> = {\n read: 0,\n \"local-state\": 1,\n navigation: 2,\n \"server-query\": 3,\n \"server-mutation\": 4,\n \"external-side-effect\": 5,\n destructive: 6,\n};\n\nconst CONFIRMATION: Record<string, number> = { never: 0, optional: 1, required: 2 };\n\nfunction key(entry: CapabilityContractEntry): string {\n return `${entry.declarationId}\\0${entry.capabilityId}`;\n}\n\nfunction equal(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\nfunction stringSet(value: unknown): Set<string> {\n return new Set(Array.isArray(value) ? value.map((item) => JSON.stringify(item)) : []);\n}\n\nfunction setDirection(before: unknown, after: unknown): \"added\" | \"removed\" | \"mixed\" | \"same\" {\n const a = stringSet(before);\n const b = stringSet(after);\n const added = [...b].some((item) => !a.has(item));\n const removed = [...a].some((item) => !b.has(item));\n if (added && removed) return \"mixed\";\n if (added) return \"added\";\n if (removed) return \"removed\";\n return \"same\";\n}\n\nfunction classify(field: string, before: unknown, after: unknown): ChangeClassification {\n if (field === \"targets\") {\n const direction = setDirection(before, after);\n return direction === \"added\" ? \"widening\" : direction === \"removed\" ? \"narrowing\" : \"neutral\";\n }\n if (field === \"policies\") {\n const direction = setDirection(before, after);\n return direction === \"removed\" || direction === \"mixed\"\n ? \"widening\"\n : direction === \"added\"\n ? \"narrowing\"\n : \"neutral\";\n }\n if (field === \"confirmation\") {\n const a = CONFIRMATION[String(before ?? \"never\")] ?? 0;\n const b = CONFIRMATION[String(after ?? \"never\")] ?? 0;\n return b < a ? \"widening\" : b > a ? \"narrowing\" : \"neutral\";\n }\n if (field === \"effect\") {\n const a = RISK[String(before)] ?? 0;\n const b = RISK[String(after)] ?? 0;\n // Lowering declared risk weakens review/approval posture.\n return b < a ? \"widening\" : b > a ? \"narrowing\" : \"neutral\";\n }\n return \"neutral\";\n}\n\nexport function diffContracts(\n before: CapabilityContractManifest | undefined,\n after: CapabilityContractManifest,\n): ContractChange[] {\n if (!before) {\n return after.capabilities.map((entry) => ({\n declarationId: entry.declarationId,\n capabilityId: entry.capabilityId,\n kind: \"added\",\n field: \"capability\",\n classification: \"widening\",\n after: entry,\n }));\n }\n const left = new Map(before.capabilities.map((entry) => [key(entry), entry]));\n const right = new Map(after.capabilities.map((entry) => [key(entry), entry]));\n const changes: ContractChange[] = [];\n for (const entryKey of [...new Set([...left.keys(), ...right.keys()])].sort()) {\n const a = left.get(entryKey);\n const b = right.get(entryKey);\n if (!a && b) {\n changes.push({\n declarationId: b.declarationId,\n capabilityId: b.capabilityId,\n kind: \"added\",\n field: \"capability\",\n classification: \"widening\",\n after: b,\n });\n continue;\n }\n if (a && !b) {\n changes.push({\n declarationId: a.declarationId,\n capabilityId: a.capabilityId,\n kind: \"removed\",\n field: \"capability\",\n classification: \"narrowing\",\n before: a,\n });\n continue;\n }\n if (!a || !b) continue;\n const fields = new Set([...Object.keys(a), ...Object.keys(b)]);\n fields.delete(\"declarationId\");\n fields.delete(\"capabilityId\");\n fields.delete(\"contractHash\");\n for (const field of [...fields].sort()) {\n const beforeValue = (a as unknown as Record<string, unknown>)[field];\n const afterValue = (b as unknown as Record<string, unknown>)[field];\n if (equal(beforeValue, afterValue)) continue;\n changes.push({\n declarationId: b.declarationId,\n capabilityId: b.capabilityId,\n kind: \"changed\",\n field,\n classification: classify(field, beforeValue, afterValue),\n before: beforeValue,\n after: afterValue,\n });\n }\n }\n return changes;\n}\n\nexport function changeCounts(changes: readonly ContractChange[]): Record<ChangeClassification, number> {\n return {\n widening: changes.filter((change) => change.classification === \"widening\").length,\n narrowing: changes.filter((change) => change.classification === \"narrowing\").length,\n neutral: changes.filter((change) => change.classification === \"neutral\").length,\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,139 +0,0 @@
1
- import {
2
- changeCounts
3
- } from "./chunk-IA7FUP4R.js";
4
-
5
- // src/files.ts
6
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
7
- import { dirname, isAbsolute, relative, resolve, sep } from "path";
8
- import { spawnSync } from "child_process";
9
- import { canonicalManifestJson, verifyManifest } from "@agent-surface/compiler";
10
- var DEFAULT_SNAPSHOT = ".agent-surface/contract.json";
11
- function readManifest(path) {
12
- if (!existsSync(path)) return void 0;
13
- try {
14
- const manifest = JSON.parse(readFileSync(path, "utf8"));
15
- verifyManifest(manifest);
16
- return manifest;
17
- } catch (error) {
18
- throw new Error(`could not read contract ${path}: ${error instanceof Error ? error.message : String(error)}`);
19
- }
20
- }
21
- function writeManifest(path, manifest) {
22
- mkdirSync(dirname(path), { recursive: true });
23
- writeFileSync(path, canonicalManifestJson(manifest), "utf8");
24
- }
25
- function git(args, cwd) {
26
- const result = spawnSync("git", args, { cwd, encoding: "utf8" });
27
- if (result.status !== 0) {
28
- throw new Error(result.stderr.trim() || `git ${args.join(" ")} failed`);
29
- }
30
- return result.stdout.trim();
31
- }
32
- function readBaseManifest(base, snapshotPath, cwd) {
33
- const root = git(["rev-parse", "--show-toplevel"], cwd);
34
- const absolute = isAbsolute(snapshotPath) ? snapshotPath : resolve(cwd, snapshotPath);
35
- const path = relative(root, absolute);
36
- if (path === ".." || path.startsWith(`..${sep}`) || isAbsolute(path)) {
37
- throw new Error(`snapshot ${snapshotPath} is outside the Git worktree`);
38
- }
39
- const text = git(["show", `${base}:${path.split(sep).join("/")}`], root);
40
- try {
41
- const manifest = JSON.parse(text);
42
- verifyManifest(manifest);
43
- return manifest;
44
- } catch (error) {
45
- throw new Error(`could not read ${snapshotPath} at ${base}: ${error instanceof Error ? error.message : String(error)}`);
46
- }
47
- }
48
-
49
- // src/report.ts
50
- import { canonicalJson } from "@agent-surface/compiler";
51
- function value(value2) {
52
- if (value2 === void 0) return "\u2014";
53
- const text = typeof value2 === "string" ? value2 : JSON.stringify(value2);
54
- return text.length > 100 ? `${text.slice(0, 97)}\u2026` : text;
55
- }
56
- function changeLine(change) {
57
- const mark = change.kind === "added" ? "+" : change.kind === "removed" ? "-" : "~";
58
- return `${mark} ${change.classification.padEnd(9)} ${change.capabilityId} \xB7 ${change.declarationId} \xB7 ${change.field}${change.kind === "changed" ? `: ${value(change.before)} \u2192 ${value(change.after)}` : ""}`;
59
- }
60
- function section(title, changes) {
61
- const counts = changeCounts(changes);
62
- return [
63
- `${title} (${changes.length}) \xB7 widening ${counts.widening} \xB7 narrowing ${counts.narrowing} \xB7 neutral ${counts.neutral}`,
64
- ...changes.length > 0 ? changes.map(changeLine) : ["no changes"]
65
- ];
66
- }
67
- function humanReport(report) {
68
- const lines = [
69
- `AGENT SURFACE ${report.command.toUpperCase()} \xB7 ${report.status.toUpperCase()}`,
70
- `Contract ${report.manifest.hash}`,
71
- `Completeness ${report.manifest.completeness.status}`,
72
- `Targets ${report.manifest.targets.join(", ") || "\u2014"}`,
73
- `Capabilities ${report.manifest.capabilities.length}`,
74
- `Snapshot ${report.snapshotPath}`
75
- ];
76
- if (report.integrity) {
77
- lines.push(`Integrity ${report.integrity.status}`);
78
- lines.push("", ...section("SOURCE \u2194 SNAPSHOT", report.integrity.changes));
79
- }
80
- if (report.pullRequest) {
81
- lines.push("", ...section(`PR DRIFT vs ${report.pullRequest.base}`, report.pullRequest.changes));
82
- }
83
- if (report.command === "inspect") {
84
- lines.push("", "REPOSITORY CONTRACT");
85
- for (const entry of report.manifest.capabilities) {
86
- lines.push(
87
- `${entry.capabilityId} \xB7 ${entry.kind} \xB7 ${entry.effect} \xB7 ${entry.declarationId} \xB7 ${entry.targets.join(",")}`
88
- );
89
- }
90
- }
91
- return `${lines.join("\n")}
92
- `;
93
- }
94
- function markdownChanges(changes) {
95
- if (changes.length === 0) return "No changes.\n";
96
- return [
97
- "| Class | Change | Capability | Declaration | Field |",
98
- "|---|---|---|---|---|",
99
- ...changes.map(
100
- (change) => `| ${change.classification} | ${change.kind} | \`${change.capabilityId}\` | \`${change.declarationId}\` | \`${change.field}\` |`
101
- )
102
- ].join("\n");
103
- }
104
- function markdownReport(report, github = false) {
105
- const lines = [
106
- `## Agent Surface ${report.command} \u2014 ${report.status}`,
107
- "",
108
- `- Contract: \`${report.manifest.hash}\``,
109
- `- Completeness: **${report.manifest.completeness.status}**`,
110
- `- Capabilities: **${report.manifest.capabilities.length}**`,
111
- `- Targets: ${report.manifest.targets.map((target) => `\`${target}\``).join(", ") || "\u2014"}`
112
- ];
113
- if (report.integrity) {
114
- lines.push("", `### Source \u2194 snapshot \u2014 ${report.integrity.status}`, "", markdownChanges(report.integrity.changes));
115
- }
116
- if (report.pullRequest) {
117
- lines.push("", `### PR drift vs \`${report.pullRequest.base}\``, "", markdownChanges(report.pullRequest.changes));
118
- }
119
- if (github && report.status === "fail") {
120
- lines.unshift(`::error title=Agent Surface contract drift::${report.integrity?.changes.length ?? 0} integrity change(s)`);
121
- }
122
- return `${lines.join("\n")}
123
- `;
124
- }
125
- function renderReport(report, format) {
126
- if (format === "json") return canonicalJson(report, true);
127
- if (format === "markdown") return markdownReport(report);
128
- if (format === "github") return markdownReport(report, true);
129
- return humanReport(report);
130
- }
131
-
132
- export {
133
- DEFAULT_SNAPSHOT,
134
- readManifest,
135
- writeManifest,
136
- readBaseManifest,
137
- renderReport
138
- };
139
- //# sourceMappingURL=chunk-X6RGGBQ4.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/files.ts","../src/report.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport type { CapabilityContractManifest } from \"@agent-surface/core\";\nimport { canonicalManifestJson, verifyManifest } from \"@agent-surface/compiler\";\n\nexport const DEFAULT_SNAPSHOT = \".agent-surface/contract.json\";\n\nexport function readManifest(path: string): CapabilityContractManifest | undefined {\n if (!existsSync(path)) return undefined;\n try {\n const manifest = JSON.parse(readFileSync(path, \"utf8\")) as CapabilityContractManifest;\n verifyManifest(manifest);\n return manifest;\n } catch (error) {\n throw new Error(`could not read contract ${path}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\nexport function writeManifest(path: string, manifest: CapabilityContractManifest): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, canonicalManifestJson(manifest), \"utf8\");\n}\n\nfunction git(args: string[], cwd: string): string {\n const result = spawnSync(\"git\", args, { cwd, encoding: \"utf8\" });\n if (result.status !== 0) {\n throw new Error(result.stderr.trim() || `git ${args.join(\" \")} failed`);\n }\n return result.stdout.trim();\n}\n\nexport function readBaseManifest(\n base: string,\n snapshotPath: string,\n cwd: string,\n): CapabilityContractManifest {\n const root = git([\"rev-parse\", \"--show-toplevel\"], cwd);\n const absolute = isAbsolute(snapshotPath) ? snapshotPath : resolve(cwd, snapshotPath);\n const path = relative(root, absolute);\n if (path === \"..\" || path.startsWith(`..${sep}`) || isAbsolute(path)) {\n throw new Error(`snapshot ${snapshotPath} is outside the Git worktree`);\n }\n const text = git([\"show\", `${base}:${path.split(sep).join(\"/\")}`], root);\n try {\n const manifest = JSON.parse(text) as CapabilityContractManifest;\n verifyManifest(manifest);\n return manifest;\n } catch (error) {\n throw new Error(`could not read ${snapshotPath} at ${base}: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n","import type { CapabilityContractManifest } from \"@agent-surface/core\";\nimport { canonicalJson } from \"@agent-surface/compiler\";\nimport type { ContractChange } from \"./diff.js\";\nimport { changeCounts } from \"./diff.js\";\n\nexport type OutputFormat = \"human\" | \"json\" | \"github\" | \"markdown\";\n\nexport interface ContractReport {\n command: \"inspect\" | \"check\" | \"snapshot\";\n status: \"pass\" | \"fail\" | \"written\" | \"view\";\n manifest: CapabilityContractManifest;\n snapshotPath: string;\n integrity?: {\n status: \"current\" | \"missing\" | \"stale\";\n changes: ContractChange[];\n };\n pullRequest?: {\n base: string;\n changes: ContractChange[];\n };\n}\n\nfunction value(value: unknown): string {\n if (value === undefined) return \"—\";\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n return text.length > 100 ? `${text.slice(0, 97)}…` : text;\n}\n\nfunction changeLine(change: ContractChange): string {\n const mark = change.kind === \"added\" ? \"+\" : change.kind === \"removed\" ? \"-\" : \"~\";\n return `${mark} ${change.classification.padEnd(9)} ${change.capabilityId} · ${change.declarationId} · ${change.field}${\n change.kind === \"changed\" ? `: ${value(change.before)} → ${value(change.after)}` : \"\"\n }`;\n}\n\nfunction section(title: string, changes: readonly ContractChange[]): string[] {\n const counts = changeCounts(changes);\n return [\n `${title} (${changes.length}) · widening ${counts.widening} · narrowing ${counts.narrowing} · neutral ${counts.neutral}`,\n ...(changes.length > 0 ? changes.map(changeLine) : [\"no changes\"]),\n ];\n}\n\nexport function humanReport(report: ContractReport): string {\n const lines = [\n `AGENT SURFACE ${report.command.toUpperCase()} · ${report.status.toUpperCase()}`,\n `Contract ${report.manifest.hash}`,\n `Completeness ${report.manifest.completeness.status}`,\n `Targets ${report.manifest.targets.join(\", \") || \"—\"}`,\n `Capabilities ${report.manifest.capabilities.length}`,\n `Snapshot ${report.snapshotPath}`,\n ];\n if (report.integrity) {\n lines.push(`Integrity ${report.integrity.status}`);\n lines.push(\"\", ...section(\"SOURCE ↔ SNAPSHOT\", report.integrity.changes));\n }\n if (report.pullRequest) {\n lines.push(\"\", ...section(`PR DRIFT vs ${report.pullRequest.base}`, report.pullRequest.changes));\n }\n if (report.command === \"inspect\") {\n lines.push(\"\", \"REPOSITORY CONTRACT\");\n for (const entry of report.manifest.capabilities) {\n lines.push(\n `${entry.capabilityId} · ${entry.kind} · ${entry.effect} · ${entry.declarationId} · ${entry.targets.join(\",\")}`,\n );\n }\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nfunction markdownChanges(changes: readonly ContractChange[]): string {\n if (changes.length === 0) return \"No changes.\\n\";\n return [\n \"| Class | Change | Capability | Declaration | Field |\",\n \"|---|---|---|---|---|\",\n ...changes.map(\n (change) =>\n `| ${change.classification} | ${change.kind} | \\`${change.capabilityId}\\` | \\`${change.declarationId}\\` | \\`${change.field}\\` |`,\n ),\n ].join(\"\\n\");\n}\n\nexport function markdownReport(report: ContractReport, github = false): string {\n const lines = [\n `## Agent Surface ${report.command} — ${report.status}`,\n \"\",\n `- Contract: \\`${report.manifest.hash}\\``,\n `- Completeness: **${report.manifest.completeness.status}**`,\n `- Capabilities: **${report.manifest.capabilities.length}**`,\n `- Targets: ${report.manifest.targets.map((target) => `\\`${target}\\``).join(\", \") || \"—\"}`,\n ];\n if (report.integrity) {\n lines.push(\"\", `### Source ↔ snapshot — ${report.integrity.status}`, \"\", markdownChanges(report.integrity.changes));\n }\n if (report.pullRequest) {\n lines.push(\"\", `### PR drift vs \\`${report.pullRequest.base}\\``, \"\", markdownChanges(report.pullRequest.changes));\n }\n if (github && report.status === \"fail\") {\n lines.unshift(`::error title=Agent Surface contract drift::${report.integrity?.changes.length ?? 0} integrity change(s)`);\n }\n return `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function renderReport(report: ContractReport, format: OutputFormat): string {\n if (format === \"json\") return canonicalJson(report, true);\n if (format === \"markdown\") return markdownReport(report);\n if (format === \"github\") return markdownReport(report, true);\n return humanReport(report);\n}\n"],"mappings":"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","names":["value"]}
@@ -1,51 +0,0 @@
1
- import {
2
- changeCounts
3
- } from "./chunk-IA7FUP4R.js";
4
-
5
- // src/ink.tsx
6
- import { Box, Text, render } from "ink";
7
- import { jsx, jsxs } from "react/jsx-runtime";
8
- function App({ report }) {
9
- const ok = report.status === "pass" || report.status === "written" || report.status === "view";
10
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
11
- /* @__PURE__ */ jsxs(Text, { bold: true, color: ok ? "green" : "red", children: [
12
- "AGENT SURFACE ",
13
- report.command.toUpperCase(),
14
- " \xB7 ",
15
- report.status.toUpperCase()
16
- ] }),
17
- /* @__PURE__ */ jsxs(Text, { children: [
18
- "Contract ",
19
- report.manifest.hash
20
- ] }),
21
- /* @__PURE__ */ jsxs(Text, { children: [
22
- "Completeness ",
23
- report.manifest.completeness.status
24
- ] }),
25
- /* @__PURE__ */ jsxs(Text, { children: [
26
- "Capabilities ",
27
- report.manifest.capabilities.length
28
- ] }),
29
- report.integrity ? /* @__PURE__ */ jsxs(Text, { children: [
30
- "Integrity ",
31
- report.integrity.status
32
- ] }) : null,
33
- report.pullRequest ? /* @__PURE__ */ jsxs(Text, { children: [
34
- "PR drift ",
35
- report.pullRequest.changes.length,
36
- " vs ",
37
- report.pullRequest.base,
38
- " (",
39
- changeCounts(report.pullRequest.changes).widening,
40
- " widening)"
41
- ] }) : null
42
- ] });
43
- }
44
- async function renderInk(report) {
45
- const instance = render(/* @__PURE__ */ jsx(App, { report }));
46
- await instance.waitUntilExit();
47
- }
48
- export {
49
- renderInk
50
- };
51
- //# sourceMappingURL=ink-E6DATMUQ.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/ink.tsx"],"sourcesContent":["import { Box, Text, render } from \"ink\";\nimport type { ContractReport } from \"./report.js\";\nimport { changeCounts } from \"./diff.js\";\n\nfunction App({ report }: { report: ContractReport }) {\n const ok = report.status === \"pass\" || report.status === \"written\" || report.status === \"view\";\n return (\n <Box flexDirection=\"column\">\n <Text bold color={ok ? \"green\" : \"red\"}>\n AGENT SURFACE {report.command.toUpperCase()} · {report.status.toUpperCase()}\n </Text>\n <Text>Contract {report.manifest.hash}</Text>\n <Text>Completeness {report.manifest.completeness.status}</Text>\n <Text>Capabilities {report.manifest.capabilities.length}</Text>\n {report.integrity ? <Text>Integrity {report.integrity.status}</Text> : null}\n {report.pullRequest ? (\n <Text>\n PR drift {report.pullRequest.changes.length} vs {report.pullRequest.base} ({\n changeCounts(report.pullRequest.changes).widening\n } widening)\n </Text>\n ) : null}\n </Box>\n );\n}\n\nexport async function renderInk(report: ContractReport): Promise<void> {\n const instance = render(<App report={report} />);\n await instance.waitUntilExit();\n}\n"],"mappings":";;;;;AAAA,SAAS,KAAK,MAAM,cAAc;AAQ5B,SAmBoB,KAnBpB;AAJN,SAAS,IAAI,EAAE,OAAO,GAA+B;AACnD,QAAM,KAAK,OAAO,WAAW,UAAU,OAAO,WAAW,aAAa,OAAO,WAAW;AACxF,SACE,qBAAC,OAAI,eAAc,UACjB;AAAA,yBAAC,QAAK,MAAI,MAAC,OAAO,KAAK,UAAU,OAAO;AAAA;AAAA,MACvB,OAAO,QAAQ,YAAY;AAAA,MAAE;AAAA,MAAI,OAAO,OAAO,YAAY;AAAA,OAC5E;AAAA,IACA,qBAAC,QAAK;AAAA;AAAA,MAAe,OAAO,SAAS;AAAA,OAAK;AAAA,IAC1C,qBAAC,QAAK;AAAA;AAAA,MAAe,OAAO,SAAS,aAAa;AAAA,OAAO;AAAA,IACzD,qBAAC,QAAK;AAAA;AAAA,MAAe,OAAO,SAAS,aAAa;AAAA,OAAO;AAAA,IACxD,OAAO,YAAY,qBAAC,QAAK;AAAA;AAAA,MAAe,OAAO,UAAU;AAAA,OAAO,IAAU;AAAA,IAC1E,OAAO,cACN,qBAAC,QAAK;AAAA;AAAA,MACW,OAAO,YAAY,QAAQ;AAAA,MAAO;AAAA,MAAK,OAAO,YAAY;AAAA,MAAK;AAAA,MAC5E,aAAa,OAAO,YAAY,OAAO,EAAE;AAAA,MAC1C;AAAA,OACH,IACE;AAAA,KACN;AAEJ;AAEA,eAAsB,UAAU,QAAuC;AACrE,QAAM,WAAW,OAAO,oBAAC,OAAI,QAAgB,CAAE;AAC/C,QAAM,SAAS,cAAc;AAC/B;","names":[]}