@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 +32 -16
- package/dist/bin.js.map +1 -1
- package/dist/chunk-KMAH2WU5.js +333 -0
- package/dist/chunk-KMAH2WU5.js.map +1 -0
- package/dist/chunk-XCQACLOT.js +51 -0
- package/dist/chunk-XCQACLOT.js.map +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.js +4 -4
- package/dist/ink-4CUU77KD.js +146 -0
- package/dist/ink-4CUU77KD.js.map +1 -0
- package/package.json +4 -4
- package/dist/chunk-IA7FUP4R.js +0 -125
- package/dist/chunk-IA7FUP4R.js.map +0 -1
- package/dist/chunk-X6RGGBQ4.js +0 -139
- package/dist/chunk-X6RGGBQ4.js.map +0 -1
- package/dist/ink-E6DATMUQ.js +0 -51
- package/dist/ink-E6DATMUQ.js.map +0 -1
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-
|
|
7
|
+
} from "./chunk-XCQACLOT.js";
|
|
9
8
|
import {
|
|
10
|
-
diffContracts
|
|
11
|
-
|
|
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-
|
|
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
|
|
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
|
-
|
|
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
|
|
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":";;;;;;;;;;;;;AACA,SAAS,oBAAoB;AAC7B,SAAS,qBAAqB;AAC9B,SAAS,iBAAiB;;;ACH1B,SAAS,eAAe;AAExB;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAmBP,SAAS,eAAe,WAAqE;AAC3F,QAAM,UAAU,oBAAI,IAAqC;AACzD,aAAW,YAAY,WAAW;AAChC,eAAW,SAAS,SAAS,cAAc;AACzC,YAAM,MAAM,GAAG,MAAM,aAAa,KAAK,MAAM,YAAY;AACzD,YAAM,WAAW,QAAQ,IAAI,GAAG;AAChC,UAAI,CAAC,SAAU,SAAQ,IAAI,KAAK,EAAE,GAAG,OAAO,SAAS,CAAC,GAAG,MAAM,OAAO,EAAE,CAAC;AAAA,WACpE;AACH,YAAI,SAAS,iBAAiB,MAAM,cAAc;AAChD,gBAAM,IAAI,MAAM,YAAY,MAAM,aAAa,MAAM,MAAM,YAAY,yBAAyB;AAAA,QAClG;AACA,iBAAS,UAAU,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,SAAS,SAAS,GAAG,MAAM,OAAO,CAAC,CAAC,EAAE,KAAK;AAAA,MAChF;AAAA,IACF;AAAA,EACF;AACA,QAAM,UAAU;AAAA,IACd,eAAe;AAAA,IACf,iBAAiB,UAAU,CAAC,GAAG,mBAAmB;AAAA,IAClD,SAAS,CAAC,GAAG,IAAI,IAAI,UAAU,QAAQ,CAAC,aAAa,SAAS,OAAO,CAAC,CAAC,EAAE,KAAK;AAAA,IAC9E,cAAc,CAAC,GAAG,QAAQ,OAAO,CAAC,EAAE;AAAA,MAAK,CAAC,GAAG,MAC3C,GAAG,EAAE,aAAa,KAAK,EAAE,YAAY,GAAG,cAAc,GAAG,EAAE,aAAa,KAAK,EAAE,YAAY,EAAE;AAAA,IAC/F;AAAA;AAAA;AAAA,IAGA,mBAAmB,UAChB,QAAQ,CAAC,aAAa,SAAS,iBAAiB,EAChD,OAAO,CAAC,OAAO,OAAO,QAAQ,IAAI,UAAU,CAAC,cAAc,UAAU,YAAY,MAAM,OAAO,MAAM,KAAK,EACzG,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,cAAc,EAAE,OAAO,CAAC;AAAA,IACpD,cAAc,EAAE,QAAQ,SAAkB;AAAA,EAC5C;AACA,SAAO,EAAE,GAAG,SAAS,MAAM,oBAAoB,OAAO,EAAE;AAC1D;AAEA,eAAe,QAAQ,SAA8D;AACnF,QAAM,UAAU,QAAQ,QAAQ,SAAS,IAAI,QAAQ,UAAU,CAAC,gBAAgB;AAChF,QAAM,YAAY,MAAM,QAAQ;AAAA,IAC9B,QAAQ;AAAA,MAAI,CAAC,WACX,0BAA0B;AAAA,QACxB,MAAM,QAAQ;AAAA,QACd,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,WAAW,IAAI,CAAC;AAAA,QAC/D,GAAI,QAAQ,oBAAoB,EAAE,mBAAmB,QAAQ,kBAAkB,IAAI,CAAC;AAAA,QACpF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,eAAe,SAAS;AACjC;AAEA,SAAS,WACP,QACA,SACS;AACT,MAAI,WAAW,OAAQ,QAAO;AAC9B,MAAI,WAAW,MAAO,QAAO,QAAQ,SAAS;AAC9C,SAAO,QAAQ,KAAK,CAAC,WAAW,OAAO,mBAAmB,MAAM;AAClE;AAEA,eAAe,QAAQ,QAAwB,SAAwC;AACrF,QAAM,cACJ,QAAQ,WAAW,WACnB,CAAC,QAAQ,SACT,QAAQ,OAAO,UAAU,QACzB,CAAC,QAAQ,IAAI,IAAI,KACjB,CAAC,QAAQ,IAAI,UAAU;AACzB,MAAI,aAAa;AACf,UAAM,EAAE,UAAU,IAAI,MAAM,OAAO,mBAAU;AAC7C,UAAM,UAAU,MAAM;AACtB;AAAA,EACF;AACA,UAAQ,OAAO,MAAM,aAAa,QAAQ,QAAQ,MAAM,CAAC;AAC3D;AAEA,eAAsB,WAAW,SAA0C;AACzE,QAAM,WAAW,MAAM,QAAQ,OAAO;AACtC,QAAM,eAAe,QAAQ,QAAQ,MAAM,QAAQ,YAAY,gBAAgB;AAC/E,QAAM,WAAW,aAAa,YAAY;AAC1C,QAAM,mBAAmB,cAAc,UAAU,QAAQ;AACzD,QAAM,SAAyB;AAAA,IAC7B,SAAS;AAAA,IACT,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA,WAAW;AAAA,MACT,QAAQ,CAAC,WAAW,YAAY,SAAS,SAAS,SAAS,OAAO,YAAY;AAAA,MAC9E,SAAS;AAAA,IACX;AAAA,IACA,GAAI,QAAQ,OACR;AAAA,MACE,aAAa;AAAA,QACX,MAAM,QAAQ;AAAA,QACd,SAAS,cAAc,iBAAiB,QAAQ,MAAM,cAAc,QAAQ,IAAI,GAAG,QAAQ;AAAA,MAC7F;AAAA,IACF,IACA,CAAC;AAAA,EACP;AACA,QAAM,QAAQ,QAAQ,OAAO;AAC7B,SAAO;AACT;AAEA,eAAsB,YAAY,SAA0C;AAC1E,QAAM,WAAW,MAAM,QAAQ,OAAO;AACtC,QAAM,eAAe,QAAQ,QAAQ,MAAM,QAAQ,YAAY,gBAAgB;AAC/E,gBAAc,cAAc,QAAQ;AACpC,QAAM,QAAQ,EAAE,SAAS,YAAY,QAAQ,WAAW,UAAU,aAAa,GAAG,OAAO;AACzF,SAAO;AACT;AAEA,eAAsB,SAAS,SAA0C;AACvE,QAAM,WAAW,MAAM,QAAQ,OAAO;AACtC,QAAM,eAAe,QAAQ,QAAQ,MAAM,QAAQ,YAAY,gBAAgB;AAC/E,QAAM,WAAW,aAAa,YAAY;AAC1C,QAAM,mBAAmB,cAAc,UAAU,QAAQ;AACzD,QAAM,mBAAmB,UAAU,SAAS,SAAS;AACrD,QAAM,KAAK,QAAQ,OACf;AAAA,IACE,MAAM,QAAQ;AAAA,IACd,SAAS,cAAc,iBAAiB,QAAQ,MAAM,cAAc,QAAQ,IAAI,GAAG,YAAY,QAAQ;AAAA,EACzG,IACA;AACJ,QAAM,SAAS,CAAC,qBAAqB,KAAK,WAAW,QAAQ,QAAQ,GAAG,OAAO,IAAI;AACnF,QAAM,SAAyB;AAAA,IAC7B,SAAS;AAAA,IACT,QAAQ,SAAS,SAAS;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,WAAW;AAAA,MACT,QAAQ,CAAC,WAAW,YAAY,mBAAmB,YAAY;AAAA,MAC/D,SAAS;AAAA,IACX;AAAA,IACA,GAAI,KAAK,EAAE,aAAa,GAAG,IAAI,CAAC;AAAA,EAClC;AACA,QAAM,QAAQ,QAAQ,OAAO;AAC7B,SAAO,SAAS,IAAI;AACtB;;;ADvJA,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBd,IAAM,WAAW,oBAAI,IAAI,CAAC,WAAW,YAAY,OAAO,CAAC;AACzD,IAAM,UAAU,oBAAI,IAAkB,CAAC,SAAS,QAAQ,UAAU,UAAU,CAAC;AAC7E,IAAM,WAAW,oBAAI,IAAI,CAAC,OAAO,YAAY,aAAa,WAAW,MAAM,CAAC;AAE5E,SAAS,MAAM,MAAc,QAAQ,OAAa;AAChD,GAAC,QAAQ,QAAQ,SAAS,QAAQ,QAAQ,MAAM,KAAK,SAAS,IAAI,IAAI,OAAO,GAAG,IAAI;AAAA,CAAI;AAC1F;AAEA,eAAsB,KAAK,OAAiB,QAAQ,KAAK,MAAM,CAAC,GAAoB;AAClF,MAAI;AACJ,MAAI;AACF,aAAS,UAAU;AAAA,MACjB,MAAM;AAAA,MACN,kBAAkB;AAAA,MAClB,SAAS;AAAA,QACP,MAAM,EAAE,MAAM,UAAU,SAAS,QAAQ,IAAI,EAAE;AAAA,QAC/C,QAAQ,EAAE,MAAM,SAAS;AAAA,QACzB,UAAU,EAAE,MAAM,SAAS;AAAA,QAC3B,QAAQ,EAAE,MAAM,UAAU,UAAU,KAAK;AAAA,QACzC,MAAM,EAAE,MAAM,SAAS;AAAA,QACvB,OAAO,EAAE,MAAM,UAAU,UAAU,KAAK;AAAA,QACxC,QAAQ,EAAE,MAAM,UAAU,SAAS,MAAM;AAAA,QACzC,QAAQ,EAAE,MAAM,UAAU,SAAS,QAAQ;AAAA,QAC3C,MAAM,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,QACxC,OAAO,EAAE,MAAM,WAAW,SAAS,MAAM;AAAA,QACzC,MAAM,EAAE,MAAM,WAAW,OAAO,KAAK,SAAS,MAAM;AAAA,QACpD,SAAS,EAAE,MAAM,WAAW,OAAO,KAAK,SAAS,MAAM;AAAA,MACzD;AAAA,IACF,CAAC;AAAA,EACH,SAAS,OAAO;AACd,UAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,IAAI;AAClE,UAAM,OAAO,IAAI;AACjB,WAAO;AAAA,EACT;AACA,QAAM,EAAE,QAAQ,YAAY,IAAI;AAChC,MAAI,OAAO,MAAM;AACf,UAAM,KAAK;AACX,WAAO;AAAA,EACT;AACA,MAAI,OAAO,SAAS;AAClB,UAAM,MAAM,YAAY,CAAC;AACzB,WAAO;AAAA,EACT;AACA,QAAM,CAAC,SAAS,GAAG,KAAK,IAAI;AAC5B,MAAI,CAAC,WAAW,CAAC,SAAS,IAAI,OAAO,KAAK,MAAM,SAAS,GAAG;AAC1D,UAAM,YAAY,SAAS,qDAAqD,mBAAmB,WAAW,EAAE,IAAI,IAAI;AACxH,UAAM,OAAO,IAAI;AACjB,WAAO;AAAA,EACT;AACA,QAAM,SAAU,OAAO,OAAO,SAAS,OAAO;AAC9C,MAAI,CAAC,QAAQ,IAAI,MAAM,GAAG;AACxB,UAAM,qDAAqD,IAAI;AAC/D,WAAO;AAAA,EACT;AACA,MAAI,CAAC,SAAS,IAAI,OAAO,MAAM,GAAG;AAChC,UAAM,+DAA+D,IAAI;AACzE,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,CAAC;AACf,aAAW,SAAS,OAAO,SAAS,CAAC,GAAG;AACtC,UAAM,YAAY,MAAM,YAAY,GAAG;AACvC,UAAM,OAAO,cAAc,KAAK,KAAK,MAAM,MAAM,GAAG,SAAS;AAC7D,UAAM,SAAS,cAAc,KAAK,KAAK,MAAM,MAAM,YAAY,CAAC;AAChE,QAAI,CAAC,QAAQ,CAAC,iBAAiB,KAAK,MAAM,GAAG;AAC3C,YAAM,4CAA4C,KAAK,KAAK,IAAI;AAChE,aAAO;AAAA,IACT;AACA,UAAM,KAAK,EAAE,SAAS,MAAM,OAAO,CAAC;AAAA,EACtC;AACA,QAAM,UAA0B;AAAA,IAC9B,MAAM,OAAO;AAAA,IACb,GAAI,OAAO,SAAS,EAAE,YAAY,OAAO,OAAO,IAAI,CAAC;AAAA,IACrD,GAAI,OAAO,WAAW,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA,IACvD,SAAS,OAAO,UAAU,CAAC;AAAA,IAC3B,GAAI,OAAO,OAAO,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,IAC3C;AAAA,IACA,QAAQ,OAAO;AAAA,IACf,GAAI,MAAM,SAAS,IAAI,EAAE,mBAAmB,EAAE,MAAM,EAAE,IAAI,CAAC;AAAA,IAC3D,GAAI,OAAO,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;AAAA,EACxC;AACA,MAAI;AACF,QAAI,YAAY,UAAW,QAAO,MAAM,WAAW,OAAO;AAC1D,QAAI,YAAY,WAAY,QAAO,MAAM,YAAY,OAAO;AAC5D,WAAO,MAAM,SAAS,OAAO;AAAA,EAC/B,SAAS,OAAO;AACd,UAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,IAAI;AAClE,QAAI,iBAAiB,SAAS,MAAM,SAAS,QAAQ,IAAI,qBAAqB,GAAG;AAC/E,YAAM,MAAM,OAAO,IAAI;AAAA,IACzB;AACA,WAAO;AAAA,EACT;AACF;AAEA,eAAe,cAA+B;AAC5C,QAAM,EAAE,aAAa,IAAI,MAAM,OAAO,IAAS;AAC/C,QAAM,OAAO,cAAc,IAAI,IAAI,mBAAmB,YAAY,GAAG,CAAC;AACtE,SAAQ,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC,EAA0B;AACzE;AAEA,SAAS,kBAA2B;AAClC,QAAM,QAAQ,QAAQ,KAAK,CAAC;AAC5B,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI;AACF,WAAO,cAAc,YAAY,GAAG,MAAM,aAAa,KAAK;AAAA,EAC9D,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,IAAI,gBAAgB,GAAG;AACrB,OAAK,KAAK,EAAE,KAAK,CAAC,SAAS;AACzB,YAAQ,WAAW;AAAA,EACrB,CAAC;AACH;","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-
|
|
5
|
+
} from "./chunk-XCQACLOT.js";
|
|
7
6
|
import {
|
|
8
7
|
changeCounts,
|
|
9
|
-
diffContracts
|
|
10
|
-
|
|
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.
|
|
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.
|
|
27
|
-
"@agent-surface/core": "^0.
|
|
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
|
|
42
|
+
"homepage": "https://agent-surface.dev",
|
|
43
43
|
"bugs": {
|
|
44
44
|
"url": "https://github.com/Wiseair-srl/agent-surface/issues"
|
|
45
45
|
},
|
package/dist/chunk-IA7FUP4R.js
DELETED
|
@@ -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":";AAeA,IAAM,OAA+B;AAAA,EACnC,MAAM;AAAA,EACN,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,mBAAmB;AAAA,EACnB,wBAAwB;AAAA,EACxB,aAAa;AACf;AAEA,IAAM,eAAuC,EAAE,OAAO,GAAG,UAAU,GAAG,UAAU,EAAE;AAElF,SAAS,IAAI,OAAwC;AACnD,SAAO,GAAG,MAAM,aAAa,KAAK,MAAM,YAAY;AACtD;AAEA,SAAS,MAAM,GAAY,GAAqB;AAC9C,SAAO,KAAK,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC;AAC/C;AAEA,SAAS,UAAU,OAA6B;AAC9C,SAAO,IAAI,IAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,IAAI,CAAC,SAAS,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,CAAC;AACtF;AAEA,SAAS,aAAa,QAAiB,OAAwD;AAC7F,QAAM,IAAI,UAAU,MAAM;AAC1B,QAAM,IAAI,UAAU,KAAK;AACzB,QAAM,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,IAAI,CAAC;AAChD,QAAM,UAAU,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,IAAI,CAAC;AAClD,MAAI,SAAS,QAAS,QAAO;AAC7B,MAAI,MAAO,QAAO;AAClB,MAAI,QAAS,QAAO;AACpB,SAAO;AACT;AAEA,SAAS,SAAS,OAAe,QAAiB,OAAsC;AACtF,MAAI,UAAU,WAAW;AACvB,UAAM,YAAY,aAAa,QAAQ,KAAK;AAC5C,WAAO,cAAc,UAAU,aAAa,cAAc,YAAY,cAAc;AAAA,EACtF;AACA,MAAI,UAAU,YAAY;AACxB,UAAM,YAAY,aAAa,QAAQ,KAAK;AAC5C,WAAO,cAAc,aAAa,cAAc,UAC5C,aACA,cAAc,UACZ,cACA;AAAA,EACR;AACA,MAAI,UAAU,gBAAgB;AAC5B,UAAM,IAAI,aAAa,OAAO,UAAU,OAAO,CAAC,KAAK;AACrD,UAAM,IAAI,aAAa,OAAO,SAAS,OAAO,CAAC,KAAK;AACpD,WAAO,IAAI,IAAI,aAAa,IAAI,IAAI,cAAc;AAAA,EACpD;AACA,MAAI,UAAU,UAAU;AACtB,UAAM,IAAI,KAAK,OAAO,MAAM,CAAC,KAAK;AAClC,UAAM,IAAI,KAAK,OAAO,KAAK,CAAC,KAAK;AAEjC,WAAO,IAAI,IAAI,aAAa,IAAI,IAAI,cAAc;AAAA,EACpD;AACA,SAAO;AACT;AAEO,SAAS,cACd,QACA,OACkB;AAClB,MAAI,CAAC,QAAQ;AACX,WAAO,MAAM,aAAa,IAAI,CAAC,WAAW;AAAA,MACxC,eAAe,MAAM;AAAA,MACrB,cAAc,MAAM;AAAA,MACpB,MAAM;AAAA,MACN,OAAO;AAAA,MACP,gBAAgB;AAAA,MAChB,OAAO;AAAA,IACT,EAAE;AAAA,EACJ;AACA,QAAM,OAAO,IAAI,IAAI,OAAO,aAAa,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,GAAG,KAAK,CAAC,CAAC;AAC5E,QAAM,QAAQ,IAAI,IAAI,MAAM,aAAa,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,GAAG,KAAK,CAAC,CAAC;AAC5E,QAAM,UAA4B,CAAC;AACnC,aAAW,YAAY,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,KAAK,KAAK,GAAG,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG;AAC7E,UAAM,IAAI,KAAK,IAAI,QAAQ;AAC3B,UAAM,IAAI,MAAM,IAAI,QAAQ;AAC5B,QAAI,CAAC,KAAK,GAAG;AACX,cAAQ,KAAK;AAAA,QACX,eAAe,EAAE;AAAA,QACjB,cAAc,EAAE;AAAA,QAChB,MAAM;AAAA,QACN,OAAO;AAAA,QACP,gBAAgB;AAAA,QAChB,OAAO;AAAA,MACT,CAAC;AACD;AAAA,IACF;AACA,QAAI,KAAK,CAAC,GAAG;AACX,cAAQ,KAAK;AAAA,QACX,eAAe,EAAE;AAAA,QACjB,cAAc,EAAE;AAAA,QAChB,MAAM;AAAA,QACN,OAAO;AAAA,QACP,gBAAgB;AAAA,QAChB,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AACA,QAAI,CAAC,KAAK,CAAC,EAAG;AACd,UAAM,SAAS,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,GAAG,OAAO,KAAK,CAAC,CAAC,CAAC;AAC7D,WAAO,OAAO,eAAe;AAC7B,WAAO,OAAO,cAAc;AAC5B,WAAO,OAAO,cAAc;AAC5B,eAAW,SAAS,CAAC,GAAG,MAAM,EAAE,KAAK,GAAG;AACtC,YAAM,cAAe,EAAyC,KAAK;AACnE,YAAM,aAAc,EAAyC,KAAK;AAClE,UAAI,MAAM,aAAa,UAAU,EAAG;AACpC,cAAQ,KAAK;AAAA,QACX,eAAe,EAAE;AAAA,QACjB,cAAc,EAAE;AAAA,QAChB,MAAM;AAAA,QACN;AAAA,QACA,gBAAgB,SAAS,OAAO,aAAa,UAAU;AAAA,QACvD,QAAQ;AAAA,QACR,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,aAAa,SAA0E;AACrG,SAAO;AAAA,IACL,UAAU,QAAQ,OAAO,CAAC,WAAW,OAAO,mBAAmB,UAAU,EAAE;AAAA,IAC3E,WAAW,QAAQ,OAAO,CAAC,WAAW,OAAO,mBAAmB,WAAW,EAAE;AAAA,IAC7E,SAAS,QAAQ,OAAO,CAAC,WAAW,OAAO,mBAAmB,SAAS,EAAE;AAAA,EAC3E;AACF;","names":[]}
|
package/dist/chunk-X6RGGBQ4.js
DELETED
|
@@ -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"]}
|
package/dist/ink-E6DATMUQ.js
DELETED
|
@@ -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
|
package/dist/ink-E6DATMUQ.js.map
DELETED
|
@@ -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":[]}
|