@agent-surface/cli 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -182
- package/dist/bin.js +181 -207
- package/dist/bin.js.map +1 -1
- package/dist/chunk-IA7FUP4R.js +125 -0
- package/dist/chunk-IA7FUP4R.js.map +1 -0
- package/dist/chunk-X6RGGBQ4.js +139 -0
- package/dist/chunk-X6RGGBQ4.js.map +1 -0
- package/dist/index.d.ts +31 -173
- package/dist/index.js +16 -5
- package/dist/index.js.map +1 -1
- package/dist/ink-E6DATMUQ.js +51 -0
- package/dist/ink-E6DATMUQ.js.map +1 -0
- package/package.json +4 -20
- package/dist/check-VWRYNYLN.js +0 -259
- package/dist/check-VWRYNYLN.js.map +0 -1
- package/dist/chunk-A2G4QLX5.js +0 -29
- package/dist/chunk-A2G4QLX5.js.map +0 -1
- package/dist/chunk-A5UCBF7D.js +0 -246
- package/dist/chunk-A5UCBF7D.js.map +0 -1
- package/dist/chunk-GYYWHZPM.js +0 -532
- package/dist/chunk-GYYWHZPM.js.map +0 -1
- package/dist/chunk-NFK3XWWH.js +0 -1475
- package/dist/chunk-NFK3XWWH.js.map +0 -1
- package/dist/chunk-Y2LSPEVK.js +0 -61
- package/dist/chunk-Y2LSPEVK.js.map +0 -1
- package/dist/collect.js +0 -52
- package/dist/collect.js.map +0 -1
- package/dist/config-rHD2wEws.d.ts +0 -60
- package/dist/init-BVZR6CRS.js +0 -164
- package/dist/init-BVZR6CRS.js.map +0 -1
- package/dist/ink-ZCQ26EY4.js +0 -345
- package/dist/ink-ZCQ26EY4.js.map +0 -1
- package/dist/inspect-3YFPCYQJ.js +0 -226
- package/dist/inspect-3YFPCYQJ.js.map +0 -1
- package/dist/snapshot-5QKLZKZI.js +0 -130
- package/dist/snapshot-5QKLZKZI.js.map +0 -1
- package/dist/vitest.d.ts +0 -30
- package/dist/vitest.js +0 -9
- package/dist/vitest.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-surface/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
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",
|
|
@@ -13,10 +13,6 @@
|
|
|
13
13
|
".": {
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"import": "./dist/index.js"
|
|
16
|
-
},
|
|
17
|
-
"./vitest": {
|
|
18
|
-
"types": "./dist/vitest.d.ts",
|
|
19
|
-
"import": "./dist/vitest.js"
|
|
20
16
|
}
|
|
21
17
|
},
|
|
22
18
|
"files": [
|
|
@@ -25,26 +21,14 @@
|
|
|
25
21
|
],
|
|
26
22
|
"dependencies": {
|
|
27
23
|
"ink": "^6.3.1",
|
|
28
|
-
"ink-spinner": "^5.0.0",
|
|
29
|
-
"jsdom": "^26.1.0",
|
|
30
24
|
"react": "^19.1.1",
|
|
31
|
-
"typescript": "^5.9.2",
|
|
32
25
|
"vite": "^7.1.3",
|
|
33
|
-
"
|
|
34
|
-
"@agent-surface/core": "^0.
|
|
35
|
-
"@agent-surface/react": "^0.15.0",
|
|
36
|
-
"@agent-surface/testing": "^0.15.0"
|
|
37
|
-
},
|
|
38
|
-
"peerDependencies": {
|
|
39
|
-
"@testing-library/react": ">=14",
|
|
40
|
-
"react-dom": ">=18.2"
|
|
26
|
+
"@agent-surface/compiler": "^0.16.0",
|
|
27
|
+
"@agent-surface/core": "^0.16.0"
|
|
41
28
|
},
|
|
42
29
|
"devDependencies": {
|
|
43
|
-
"@testing-library/react": "^16.3.0",
|
|
44
|
-
"@types/jsdom": "^21.1.7",
|
|
45
30
|
"@types/node": "^20.19.0",
|
|
46
|
-
"@types/react": "^19.1.9"
|
|
47
|
-
"react-dom": "^19.1.1"
|
|
31
|
+
"@types/react": "^19.1.9"
|
|
48
32
|
},
|
|
49
33
|
"author": "Paolo Barbato",
|
|
50
34
|
"engines": {
|
package/dist/check-VWRYNYLN.js
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
coverageReport,
|
|
3
|
-
scenarioBaseline
|
|
4
|
-
} from "./chunk-Y2LSPEVK.js";
|
|
5
|
-
import {
|
|
6
|
-
createPresenter,
|
|
7
|
-
joinCoverage,
|
|
8
|
-
mountScenarios,
|
|
9
|
-
readInventory
|
|
10
|
-
} from "./chunk-A5UCBF7D.js";
|
|
11
|
-
import {
|
|
12
|
-
SCENARIO_MANIFEST_FILE,
|
|
13
|
-
UsageError,
|
|
14
|
-
annotate,
|
|
15
|
-
baselinePath,
|
|
16
|
-
diff,
|
|
17
|
-
readBaseline,
|
|
18
|
-
readScenarioManifest,
|
|
19
|
-
renderDriftPlain,
|
|
20
|
-
write
|
|
21
|
-
} from "./chunk-GYYWHZPM.js";
|
|
22
|
-
import {
|
|
23
|
-
ALLOWLIST_FILE,
|
|
24
|
-
READING_SOURCE,
|
|
25
|
-
UNREAD_ALLOWLIST_FILE,
|
|
26
|
-
checkOverviewParts,
|
|
27
|
-
coverageExitCode,
|
|
28
|
-
coverageSections,
|
|
29
|
-
displayPath,
|
|
30
|
-
failureSection,
|
|
31
|
-
mountingLabel,
|
|
32
|
-
noVerdictSection,
|
|
33
|
-
scenarioStats
|
|
34
|
-
} from "./chunk-NFK3XWWH.js";
|
|
35
|
-
|
|
36
|
-
// src/commands/check.ts
|
|
37
|
-
import { existsSync, readdirSync } from "fs";
|
|
38
|
-
async function runCheck(options) {
|
|
39
|
-
if (options.depth === "static") {
|
|
40
|
-
throw new UsageError(
|
|
41
|
-
"check --depth static has nothing to compare \u2014 a baseline is a projection, and at this depth nothing is mounted. Use --depth runtime for drift alone, or full for both."
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
const analysis = {
|
|
45
|
-
configPath: options.configPath,
|
|
46
|
-
depth: options.depth,
|
|
47
|
-
...options.scenario ? { scenario: options.scenario } : {},
|
|
48
|
-
...options.scope ? { scope: options.scope } : {},
|
|
49
|
-
...options.tsconfig ? { tsconfig: options.tsconfig } : {},
|
|
50
|
-
...options.baselineDir ? { baselineDir: options.baselineDir } : {}
|
|
51
|
-
};
|
|
52
|
-
const present = await createPresenter(options);
|
|
53
|
-
if (!options.json) await present.wait(READING_SOURCE);
|
|
54
|
-
const inventory = readInventory(analysis);
|
|
55
|
-
let scenarios = [];
|
|
56
|
-
let mountedCount = 0;
|
|
57
|
-
const runtime = await mountScenarios(analysis, {
|
|
58
|
-
onPlan: async (plan) => {
|
|
59
|
-
scenarios = plan.scenarios;
|
|
60
|
-
if (scenarios.length > 0) await present.wait(mountingLabel(scenarios, 0));
|
|
61
|
-
},
|
|
62
|
-
onEach: async () => {
|
|
63
|
-
mountedCount += 1;
|
|
64
|
-
if (mountedCount < scenarios.length) {
|
|
65
|
-
await present.wait(mountingLabel(scenarios, mountedCount));
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
present.settle();
|
|
70
|
-
if (!runtime) throw new UsageError("check needs a mount, and this depth performs none");
|
|
71
|
-
const drifted = [];
|
|
72
|
-
for (const result of runtime.results) {
|
|
73
|
-
const path = baselinePath(runtime.baselineDir, result.scenario);
|
|
74
|
-
const expected = readBaseline(path);
|
|
75
|
-
if (expected === void 0) {
|
|
76
|
-
drifted.push({ scenario: result.scenario, missingBaseline: true, entries: [] });
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
const actual = scenarioBaseline(result);
|
|
80
|
-
const entries = annotate(diff(expected, actual), actual, expected);
|
|
81
|
-
if (entries.length > 0) drifted.push({ scenario: result.scenario, entries });
|
|
82
|
-
}
|
|
83
|
-
const committedScenarios = readScenarioManifest(runtime.baselineDir);
|
|
84
|
-
const declared = [...runtime.declaredScenarios].sort();
|
|
85
|
-
const scenarioManifestMismatch = committedScenarios === void 0 || JSON.stringify(committedScenarios) !== JSON.stringify(declared);
|
|
86
|
-
const reserved = /* @__PURE__ */ new Set([SCENARIO_MANIFEST_FILE, ALLOWLIST_FILE, UNREAD_ALLOWLIST_FILE]);
|
|
87
|
-
const staleBaselineFiles = (existsSync(runtime.baselineDir) ? readdirSync(runtime.baselineDir, { withFileTypes: true }) : []).filter((entry) => entry.isFile() && entry.name.endsWith(".json") && !reserved.has(entry.name)).map((entry) => entry.name.slice(0, -5)).filter((scenario) => !runtime.declaredScenarios.includes(scenario)).sort();
|
|
88
|
-
const rejected = runtime.results.filter((result) => result.rejections.length > 0).map((result) => ({ scenario: result.scenario, rejections: result.rejections }));
|
|
89
|
-
const coverage = joinCoverage(inventory, runtime, analysis);
|
|
90
|
-
const coverageFailed = coverage !== void 0 && coverageExitCode(coverage, { allowUnresolved: options.allowUnresolved === true }) !== 0;
|
|
91
|
-
const couldNotRun = runtime.failures.length > 0;
|
|
92
|
-
const ok = drifted.length === 0 && !coverageFailed && !couldNotRun && rejected.length === 0 && !scenarioManifestMismatch && staleBaselineFiles.length === 0;
|
|
93
|
-
if (options.json) {
|
|
94
|
-
write(
|
|
95
|
-
JSON.stringify(
|
|
96
|
-
{
|
|
97
|
-
ok,
|
|
98
|
-
drifted,
|
|
99
|
-
failures: runtime.failures,
|
|
100
|
-
rejected,
|
|
101
|
-
scenarioManifest: {
|
|
102
|
-
expected: declared,
|
|
103
|
-
committed: committedScenarios ?? null,
|
|
104
|
-
staleBaselines: staleBaselineFiles
|
|
105
|
-
},
|
|
106
|
-
coverage: coverageReport(coverage)
|
|
107
|
-
},
|
|
108
|
-
null,
|
|
109
|
-
2
|
|
110
|
-
)
|
|
111
|
-
);
|
|
112
|
-
return couldNotRun ? 2 : ok ? 0 : 1;
|
|
113
|
-
}
|
|
114
|
-
const missing = drifted.filter((entry) => entry.missingBaseline);
|
|
115
|
-
const changed = drifted.filter((entry) => !entry.missingBaseline);
|
|
116
|
-
const baselineOf = (scenario) => {
|
|
117
|
-
const entry = drifted.find((candidate) => candidate.scenario === scenario);
|
|
118
|
-
if (!entry) return "current";
|
|
119
|
-
if (entry.missingBaseline) return "missing";
|
|
120
|
-
return `drift (${entry.entries.length})`;
|
|
121
|
-
};
|
|
122
|
-
const stats = runtime.scenarios.map((scenario) => {
|
|
123
|
-
const result = runtime.results.find((candidate) => candidate.scenario === scenario);
|
|
124
|
-
if (result) return { ...scenarioStats(result), baseline: baselineOf(scenario) };
|
|
125
|
-
const failure = runtime.failures.find((entry) => entry.scenario === scenario);
|
|
126
|
-
return {
|
|
127
|
-
scenario,
|
|
128
|
-
callable: 0,
|
|
129
|
-
disabled: 0,
|
|
130
|
-
hidden: 0,
|
|
131
|
-
rejected: 0,
|
|
132
|
-
failed: true,
|
|
133
|
-
...failure?.message ? { failure: failure.message } : {}
|
|
134
|
-
};
|
|
135
|
-
});
|
|
136
|
-
const stream = ok ? void 0 : "err";
|
|
137
|
-
const parts = checkOverviewParts({
|
|
138
|
-
status: couldNotRun ? "ERROR" : ok ? "PASS" : "FAIL",
|
|
139
|
-
...coverage ? { coverage } : {},
|
|
140
|
-
unresolvedAllowed: options.allowUnresolved === true,
|
|
141
|
-
baselineCurrent: Math.max(0, runtime.results.length - drifted.length),
|
|
142
|
-
baselineTotal: runtime.scenarios.length,
|
|
143
|
-
scenarioManifestOk: !scenarioManifestMismatch && staleBaselineFiles.length === 0,
|
|
144
|
-
rejected: rejected.reduce((sum, entry) => sum + entry.rejections.length, 0),
|
|
145
|
-
mountFailures: runtime.failures.length,
|
|
146
|
-
context: {
|
|
147
|
-
configPath: options.configPath,
|
|
148
|
-
depth: options.depth,
|
|
149
|
-
...runtime.scope ? { scope: runtime.scope } : {}
|
|
150
|
-
},
|
|
151
|
-
stats
|
|
152
|
-
}).map((part) => ({ ...part, ...stream ? { stream } : {} }));
|
|
153
|
-
if (coverage) {
|
|
154
|
-
const gaps = coverageSections(coverage, {
|
|
155
|
-
compact: true,
|
|
156
|
-
...options.detail ? { detail: true } : {}
|
|
157
|
-
});
|
|
158
|
-
if (gaps.length > 0) {
|
|
159
|
-
parts.push({
|
|
160
|
-
kind: "findings",
|
|
161
|
-
sections: gaps,
|
|
162
|
-
...coverageFailed ? { stream: "err" } : {}
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
const sections = [];
|
|
167
|
-
const steps = [];
|
|
168
|
-
if (coverage && coverage.unreached.length > 0) {
|
|
169
|
-
steps.push(
|
|
170
|
-
`mount the ${coverage.unreached.length} unreached capabilit${coverage.unreached.length === 1 ? "y" : "ies"} from a scenario, or record the decision in ${displayPath(coverage.allowlistPath)}`
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
if (coverage && coverage.unresolved.length > 0 && options.allowUnresolved !== true) {
|
|
174
|
-
steps.push(
|
|
175
|
-
`make the ${coverage.unresolved.length} unread call site${coverage.unresolved.length === 1 ? "" : "s"} readable, or paste each printed key into ${displayPath(coverage.unreadAllowlistPath)}`
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
if (rejected.length > 0) {
|
|
179
|
-
const total = rejected.reduce((sum, entry) => sum + entry.rejections.length, 0);
|
|
180
|
-
sections.push({
|
|
181
|
-
title: "REJECTED REGISTRATIONS",
|
|
182
|
-
gloss: "the runtime refused authored surface during the mount",
|
|
183
|
-
count: total,
|
|
184
|
-
lines: rejected.flatMap(
|
|
185
|
-
(entry) => entry.rejections.map(
|
|
186
|
-
(rejection) => `${entry.scenario}: ${rejection.componentType}@${rejection.instanceId} (${rejection.reason})`
|
|
187
|
-
)
|
|
188
|
-
),
|
|
189
|
-
hint: "a dead handle registers nothing \u2014 give the second registration its own instanceId, or remove the duplicated component type"
|
|
190
|
-
});
|
|
191
|
-
steps.push(
|
|
192
|
-
`resolve ${total} rejected registration${total === 1 ? "" : "s"} \u2014 the capabilities behind them reach no agent`
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
if (scenarioManifestMismatch || staleBaselineFiles.length > 0) {
|
|
196
|
-
sections.push({
|
|
197
|
-
title: "SCENARIO DRIFT",
|
|
198
|
-
gloss: "the committed baselines do not match the config",
|
|
199
|
-
count: 0,
|
|
200
|
-
lines: [
|
|
201
|
-
`config: ${declared.join(", ")}`,
|
|
202
|
-
`manifest: ${committedScenarios?.join(", ") ?? "missing"}`,
|
|
203
|
-
...staleBaselineFiles.length > 0 ? [`stale: ${staleBaselineFiles.join(", ")}`] : []
|
|
204
|
-
],
|
|
205
|
-
hint: "run `agent-surface snapshot`, commit the manifest, and delete any baseline for a scenario the config no longer declares"
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
if (missing.length > 0) {
|
|
209
|
-
sections.push({
|
|
210
|
-
title: "NO BASELINE",
|
|
211
|
-
gloss: "nothing to compare against, which is not the same as a match",
|
|
212
|
-
count: missing.length,
|
|
213
|
-
lines: missing.map(
|
|
214
|
-
(entry) => `${entry.scenario}: ${displayPath(
|
|
215
|
-
baselinePath(runtime.baselineDir, entry.scenario)
|
|
216
|
-
)} does not exist`
|
|
217
|
-
),
|
|
218
|
-
hint: "run `agent-surface snapshot` and commit the files it writes"
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
if (changed.length > 0) {
|
|
222
|
-
sections.push({
|
|
223
|
-
title: "DRIFT",
|
|
224
|
-
gloss: "the surface changed against its baseline",
|
|
225
|
-
count: changed.length,
|
|
226
|
-
lines: changed.flatMap((entry) => renderDriftPlain(entry.scenario, entry.entries)),
|
|
227
|
-
hint: "review the change, then `agent-surface snapshot` to accept it" + (runtime.scope ? ". A scope filters the projection while baselines are written from whatever scope wrote them, so re-check without --scope before believing this one" : "")
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
const baselinesStale = missing.length > 0 || changed.length > 0 || scenarioManifestMismatch || staleBaselineFiles.length > 0;
|
|
231
|
-
if (baselinesStale) {
|
|
232
|
-
steps.push(
|
|
233
|
-
"`agent-surface snapshot`, then commit .agent-surface/ \u2014 this accepts the surface above as reviewed"
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
if (sections.length > 0) parts.push({ kind: "findings", stream: "err", sections });
|
|
237
|
-
if (couldNotRun) {
|
|
238
|
-
parts.push({
|
|
239
|
-
kind: "findings",
|
|
240
|
-
stream: "err",
|
|
241
|
-
sections: [
|
|
242
|
-
failureSection(runtime.failures),
|
|
243
|
-
...inventory ? [noVerdictSection(runtime.failures)] : []
|
|
244
|
-
]
|
|
245
|
-
});
|
|
246
|
-
steps.unshift(
|
|
247
|
-
`fix the mount for ${runtime.failures.map((failure) => failure.scenario).join(", ")} \u2014 every count above is missing whatever ${runtime.failures.length === 1 ? "it" : "they"} would have surfaced`
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
if (!ok && steps.length > 0) {
|
|
251
|
-
parts.push({ kind: "steps", stream: "err", title: "NEXT STEPS", steps });
|
|
252
|
-
}
|
|
253
|
-
await present.emit(...parts);
|
|
254
|
-
return couldNotRun ? 2 : ok ? 0 : 1;
|
|
255
|
-
}
|
|
256
|
-
export {
|
|
257
|
-
runCheck
|
|
258
|
-
};
|
|
259
|
-
//# sourceMappingURL=check-VWRYNYLN.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/check.ts"],"sourcesContent":["import { existsSync, readdirSync } from \"node:fs\";\nimport {\n UsageError,\n joinCoverage,\n mountScenarios,\n readInventory,\n type AnalysisOptions,\n type Depth,\n} from \"../analysis.js\";\nimport {\n annotate,\n baselinePath,\n diff,\n readBaseline,\n readScenarioManifest,\n SCENARIO_MANIFEST_FILE,\n type DiffEntry,\n} from \"../baseline.js\";\nimport { ALLOWLIST_FILE, coverageExitCode, UNREAD_ALLOWLIST_FILE } from \"../coverage.js\";\nimport { renderDriftPlain } from \"../render/plain.js\";\nimport { createPresenter } from \"../render/present.js\";\nimport {\n checkOverviewParts,\n coverageSections,\n displayPath,\n failureSection,\n mountingLabel,\n noVerdictSection,\n READING_SOURCE,\n scenarioStats,\n type FindingSection,\n type ReportPart,\n type ScenarioStats,\n} from \"../render/summary.js\";\nimport { write } from \"../output.js\";\nimport { coverageReport, scenarioBaseline } from \"../report.js\";\n\nexport interface CheckOptions {\n configPath: string;\n depth: Depth;\n scenario?: string;\n scope?: string[];\n tsconfig?: string;\n baselineDir?: string;\n allowUnresolved?: boolean;\n json?: boolean;\n plain?: boolean;\n detail?: boolean;\n}\n\ninterface ScenarioDrift {\n scenario: string;\n missingBaseline?: boolean;\n entries: DiffEntry[];\n}\n\ninterface RejectedScenario {\n scenario: string;\n rejections: Array<{ componentType: string; instanceId: string; reason: string }>;\n}\n\n/**\n * The gate. The only command in this package that fails on a finding, which is\n * why every finding has to reach it.\n *\n * It used to fail on exactly one class — the projection drifting from its\n * baseline — and print a line telling you that capabilities no scenario mounts\n * were a different command's question. A gate that names the check it is not\n * performing is a gate with a hole in it, and in CI the hole was silent: a\n * whole unreached route sat behind a green tick.\n *\n * So it now fails on every incomplete or changed report:\n *\n * - **drift** — the surface changed against its committed baseline;\n * - **a missing baseline** — nothing to compare, which is not the same as a match;\n * - **an unreached capability** — authored, and no scenario mounts it;\n * - **an unread call site** — the catalog is incomplete, so the third check\n * above is computed over a denominator that is only a floor;\n * - **a rejected registration** — authored surface was refused;\n * - **scenario drift** — config, manifest and baseline files disagree.\n *\n * `.agent-surface/coverage-allow.json` ratchets the third; `--allow-unresolved`\n * accepts the fourth. Both are deliberate, committed decisions rather than\n * flags that quietly widen the gate.\n *\n * The report is read top-down: the verdict, what it was computed over, one row\n * per class of finding whether or not it fired, one row per scenario, then the\n * findings themselves and the commands that clear them — the same shapes, in\n * the same grid, that `inspect` uses for the same things. Its output is a\n * report pasted into a pull request and read out of a CI log, and neither of\n * those is a terminal — so neither of those gets a terminal UI: piped, `CI` and\n * `NO_COLOR` all render plain text, and that is decided by the presenter, once,\n * rather than by this command declining to have a renderer at all.\n */\nexport async function runCheck(options: CheckOptions): Promise<number> {\n if (options.depth === \"static\") {\n throw new UsageError(\n \"check --depth static has nothing to compare — a baseline is a projection, and \" +\n \"at this depth nothing is mounted. Use --depth runtime for drift alone, or full for both.\",\n );\n }\n\n const analysis: AnalysisOptions = {\n configPath: options.configPath,\n depth: options.depth,\n ...(options.scenario ? { scenario: options.scenario } : {}),\n ...(options.scope ? { scope: options.scope } : {}),\n ...(options.tsconfig ? { tsconfig: options.tsconfig } : {}),\n ...(options.baselineDir ? { baselineDir: options.baselineDir } : {}),\n };\n\n const present = await createPresenter(options);\n if (!options.json) await present.wait(READING_SOURCE);\n\n const inventory = readInventory(analysis);\n // No streaming here, unlike `inspect`. A report is read top-down and has to\n // lead with its findings, which means every finding has to exist first — so\n // the terminal is told what it is waiting for instead, in the same words and\n // with the same spinner `inspect` uses for the same wait.\n let scenarios: string[] = [];\n let mountedCount = 0;\n const runtime = await mountScenarios(analysis, {\n onPlan: async (plan) => {\n scenarios = plan.scenarios;\n if (scenarios.length > 0) await present.wait(mountingLabel(scenarios, 0));\n },\n onEach: async () => {\n mountedCount += 1;\n if (mountedCount < scenarios.length) {\n await present.wait(mountingLabel(scenarios, mountedCount));\n }\n },\n });\n present.settle();\n if (!runtime) throw new UsageError(\"check needs a mount, and this depth performs none\");\n\n const drifted: ScenarioDrift[] = [];\n for (const result of runtime.results) {\n const path = baselinePath(runtime.baselineDir, result.scenario);\n const expected = readBaseline(path);\n if (expected === undefined) {\n drifted.push({ scenario: result.scenario, missingBaseline: true, entries: [] });\n continue;\n }\n const actual = scenarioBaseline(result);\n const entries = annotate(diff(expected, actual), actual, expected);\n if (entries.length > 0) drifted.push({ scenario: result.scenario, entries });\n }\n\n const committedScenarios = readScenarioManifest(runtime.baselineDir);\n const declared = [...runtime.declaredScenarios].sort();\n const scenarioManifestMismatch =\n committedScenarios === undefined ||\n JSON.stringify(committedScenarios) !== JSON.stringify(declared);\n const reserved = new Set([SCENARIO_MANIFEST_FILE, ALLOWLIST_FILE, UNREAD_ALLOWLIST_FILE]);\n const staleBaselineFiles = (\n existsSync(runtime.baselineDir) ? readdirSync(runtime.baselineDir, { withFileTypes: true }) : []\n )\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".json\") && !reserved.has(entry.name))\n .map((entry) => entry.name.slice(0, -5))\n .filter((scenario) => !runtime.declaredScenarios.includes(scenario))\n .sort();\n\n const rejected: RejectedScenario[] = runtime.results\n .filter((result) => result.rejections.length > 0)\n .map((result) => ({ scenario: result.scenario, rejections: result.rejections }));\n\n const coverage = joinCoverage(inventory, runtime, analysis);\n const coverageFailed =\n coverage !== undefined &&\n coverageExitCode(coverage, { allowUnresolved: options.allowUnresolved === true }) !== 0;\n const couldNotRun = runtime.failures.length > 0;\n const ok =\n drifted.length === 0 &&\n !coverageFailed &&\n !couldNotRun &&\n rejected.length === 0 &&\n !scenarioManifestMismatch &&\n staleBaselineFiles.length === 0;\n\n if (options.json) {\n write(\n JSON.stringify(\n {\n ok,\n drifted,\n failures: runtime.failures,\n rejected,\n scenarioManifest: {\n expected: declared,\n committed: committedScenarios ?? null,\n staleBaselines: staleBaselineFiles,\n },\n coverage: coverageReport(coverage),\n },\n null,\n 2,\n ),\n );\n return couldNotRun ? 2 : ok ? 0 : 1;\n }\n\n // \"No baseline\" is not drift, and filing it under a heading that says the\n // surface changed would be a claim about a comparison that never happened.\n const missing = drifted.filter((entry) => entry.missingBaseline);\n const changed = drifted.filter((entry) => !entry.missingBaseline);\n\n const baselineOf = (scenario: string): string => {\n const entry = drifted.find((candidate) => candidate.scenario === scenario);\n if (!entry) return \"current\";\n if (entry.missingBaseline) return \"missing\";\n return `drift (${entry.entries.length})`;\n };\n // One row per scenario the run attempted, in config order — including the\n // ones that threw, which otherwise appear only at the bottom of the report.\n const stats: ScenarioStats[] = runtime.scenarios.map((scenario) => {\n const result = runtime.results.find((candidate) => candidate.scenario === scenario);\n if (result) return { ...scenarioStats(result), baseline: baselineOf(scenario) };\n const failure = runtime.failures.find((entry) => entry.scenario === scenario);\n return {\n scenario,\n callable: 0,\n disabled: 0,\n hidden: 0,\n rejected: 0,\n failed: true,\n ...(failure?.message ? { failure: failure.message } : {}),\n };\n });\n\n // A failing report goes to stderr in full. The verdict is the part a reader\n // has to see, and a gate whose red is on the stream nobody captured is a gate\n // that reads as green.\n const stream = ok ? undefined : (\"err\" as const);\n const parts: ReportPart[] = checkOverviewParts({\n status: couldNotRun ? \"ERROR\" : ok ? \"PASS\" : \"FAIL\",\n ...(coverage ? { coverage } : {}),\n unresolvedAllowed: options.allowUnresolved === true,\n baselineCurrent: Math.max(0, runtime.results.length - drifted.length),\n baselineTotal: runtime.scenarios.length,\n scenarioManifestOk: !scenarioManifestMismatch && staleBaselineFiles.length === 0,\n rejected: rejected.reduce((sum, entry) => sum + entry.rejections.length, 0),\n mountFailures: runtime.failures.length,\n context: {\n configPath: options.configPath,\n depth: options.depth,\n ...(runtime.scope ? { scope: runtime.scope } : {}),\n },\n stats,\n }).map((part) => ({ ...part, ...(stream ? { stream } : {}) }));\n\n // The gap leads, because it is the finding this command could not previously\n // make at all. Drift follows, because it is the one it always could.\n if (coverage) {\n const gaps = coverageSections(coverage, {\n compact: true,\n ...(options.detail ? { detail: true } : {}),\n });\n if (gaps.length > 0) {\n parts.push({\n kind: \"findings\",\n sections: gaps,\n ...(coverageFailed ? { stream: \"err\" as const } : {}),\n });\n }\n }\n\n const sections: FindingSection[] = [];\n const steps: string[] = [];\n if (coverage && coverage.unreached.length > 0) {\n steps.push(\n `mount the ${coverage.unreached.length} unreached capabilit${\n coverage.unreached.length === 1 ? \"y\" : \"ies\"\n } from a scenario, or record the decision in ${displayPath(coverage.allowlistPath)}`,\n );\n }\n if (coverage && coverage.unresolved.length > 0 && options.allowUnresolved !== true) {\n steps.push(\n `make the ${coverage.unresolved.length} unread call site${\n coverage.unresolved.length === 1 ? \"\" : \"s\"\n } readable, or paste each printed key into ${displayPath(coverage.unreadAllowlistPath)}`,\n );\n }\n\n if (rejected.length > 0) {\n const total = rejected.reduce((sum, entry) => sum + entry.rejections.length, 0);\n sections.push({\n title: \"REJECTED REGISTRATIONS\",\n gloss: \"the runtime refused authored surface during the mount\",\n count: total,\n lines: rejected.flatMap((entry) =>\n entry.rejections.map(\n (rejection) =>\n `${entry.scenario}: ${rejection.componentType}@${rejection.instanceId} (${rejection.reason})`,\n ),\n ),\n hint:\n \"a dead handle registers nothing — give the second registration its own instanceId, \" +\n \"or remove the duplicated component type\",\n });\n steps.push(\n `resolve ${total} rejected registration${total === 1 ? \"\" : \"s\"} — the capabilities ` +\n \"behind them reach no agent\",\n );\n }\n\n if (scenarioManifestMismatch || staleBaselineFiles.length > 0) {\n sections.push({\n title: \"SCENARIO DRIFT\",\n gloss: \"the committed baselines do not match the config\",\n count: 0,\n lines: [\n `config: ${declared.join(\", \")}`,\n `manifest: ${committedScenarios?.join(\", \") ?? \"missing\"}`,\n ...(staleBaselineFiles.length > 0 ? [`stale: ${staleBaselineFiles.join(\", \")}`] : []),\n ],\n hint:\n \"run `agent-surface snapshot`, commit the manifest, and delete any baseline for a \" +\n \"scenario the config no longer declares\",\n });\n }\n\n if (missing.length > 0) {\n sections.push({\n title: \"NO BASELINE\",\n gloss: \"nothing to compare against, which is not the same as a match\",\n count: missing.length,\n lines: missing.map(\n (entry) =>\n `${entry.scenario}: ${displayPath(\n baselinePath(runtime.baselineDir, entry.scenario),\n )} does not exist`,\n ),\n hint: \"run `agent-surface snapshot` and commit the files it writes\",\n });\n }\n\n if (changed.length > 0) {\n sections.push({\n title: \"DRIFT\",\n gloss: \"the surface changed against its baseline\",\n count: changed.length,\n lines: changed.flatMap((entry) => renderDriftPlain(entry.scenario, entry.entries)),\n hint:\n \"review the change, then `agent-surface snapshot` to accept it\" +\n (runtime.scope\n ? \". A scope filters the projection while baselines are written from whatever \" +\n \"scope wrote them, so re-check without --scope before believing this one\"\n : \"\"),\n });\n }\n\n const baselinesStale =\n missing.length > 0 ||\n changed.length > 0 ||\n scenarioManifestMismatch ||\n staleBaselineFiles.length > 0;\n if (baselinesStale) {\n steps.push(\n \"`agent-surface snapshot`, then commit .agent-surface/ — this accepts the surface \" +\n \"above as reviewed\",\n );\n }\n\n if (sections.length > 0) parts.push({ kind: \"findings\", stream: \"err\", sections });\n\n if (couldNotRun) {\n parts.push({\n kind: \"findings\",\n stream: \"err\",\n sections: [\n failureSection(runtime.failures),\n ...(inventory ? [noVerdictSection(runtime.failures)] : []),\n ],\n });\n // First, and above every other remedy: nothing else in this report can be\n // trusted while a scenario the config declares never ran.\n steps.unshift(\n `fix the mount for ${runtime.failures\n .map((failure) => failure.scenario)\n .join(\", \")} — every count above is missing whatever ${\n runtime.failures.length === 1 ? \"it\" : \"they\"\n } would have surfaced`,\n );\n }\n\n // Last, and only when something failed: the tail of a CI log is what a reader\n // sees first, and a list of findings without the commands that clear them\n // leaves the reader to derive those from six different sections.\n if (!ok && steps.length > 0) {\n parts.push({ kind: \"steps\", stream: \"err\", title: \"NEXT STEPS\", steps });\n }\n\n await present.emit(...parts);\n return couldNotRun ? 2 : ok ? 0 : 1;\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-A2G4QLX5.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
// src/mount.ts
|
|
2
|
-
import { act } from "@testing-library/react";
|
|
3
|
-
import { renderAgentSurface } from "@agent-surface/testing/react";
|
|
4
|
-
var DEFAULT_CLI_CONSUMER = { id: "cli", kind: "test" };
|
|
5
|
-
async function mountScenario(config, scenario, options = {}) {
|
|
6
|
-
const props = config.scenarios[scenario];
|
|
7
|
-
if (!props) {
|
|
8
|
-
const known = Object.keys(config.scenarios);
|
|
9
|
-
throw new Error(
|
|
10
|
-
`unknown scenario "${scenario}" \u2014 this config defines ${known.length > 0 ? known.map((name) => `"${name}"`).join(", ") : "none"}`
|
|
11
|
-
);
|
|
12
|
-
}
|
|
13
|
-
const consumer = options.consumer ?? config.consumer ?? DEFAULT_CLI_CONSUMER;
|
|
14
|
-
const mounted = await config.mount({ ...props, scenario });
|
|
15
|
-
const surface = await renderAgentSurface(mounted.ui, {
|
|
16
|
-
registry: mounted.registry,
|
|
17
|
-
consumer
|
|
18
|
-
});
|
|
19
|
-
await act(async () => {
|
|
20
|
-
});
|
|
21
|
-
await config.settle?.(mounted);
|
|
22
|
-
return { scenario, surface, mounted, app: mounted.app, consumer };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export {
|
|
26
|
-
DEFAULT_CLI_CONSUMER,
|
|
27
|
-
mountScenario
|
|
28
|
-
};
|
|
29
|
-
//# sourceMappingURL=chunk-A2G4QLX5.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mount.ts"],"sourcesContent":["import { act } from \"@testing-library/react\";\nimport { renderAgentSurface, type RenderedAgentSurface } from \"@agent-surface/testing/react\";\nimport type { AgentConsumer } from \"@agent-surface/core\";\nimport type { MountResult, ScenarioProps, SurfaceConfig } from \"./config.js\";\n\nexport const DEFAULT_CLI_CONSUMER: AgentConsumer = { id: \"cli\", kind: \"test\" };\n\nexport interface MountScenarioOptions {\n consumer?: AgentConsumer;\n}\n\nexport interface MountedScenario<TApp> {\n scenario: string;\n surface: RenderedAgentSurface;\n mounted: MountResult<TApp>;\n /**\n * Whatever `mount()` returned as `app`. Typed as `TApp` rather than\n * `TApp | undefined` because a config that never sets it infers `TApp` as\n * `unknown`, and forcing a `!` on every test that *does* set it is worse\n * than trusting the config's own return type.\n */\n app: TApp;\n consumer: AgentConsumer;\n}\n\n/**\n * The one mounting path. `agent-surface inspect` and the Vitest helper both\n * come through here, so a scenario cannot behave one way in CI and another in\n * the terminal — which is the entire reason scenarios live in one file.\n */\nexport async function mountScenario<TScenario extends ScenarioProps, TApp>(\n config: SurfaceConfig<TScenario, TApp>,\n scenario: string,\n options: MountScenarioOptions = {},\n): Promise<MountedScenario<TApp>> {\n const props = config.scenarios[scenario];\n if (!props) {\n const known = Object.keys(config.scenarios);\n throw new Error(\n `unknown scenario \"${scenario}\" — this config defines ${\n known.length > 0 ? known.map((name) => `\"${name}\"`).join(\", \") : \"none\"\n }`,\n );\n }\n\n const consumer = options.consumer ?? config.consumer ?? DEFAULT_CLI_CONSUMER;\n const mounted = await config.mount({ ...props, scenario });\n const surface = await renderAgentSurface(mounted.ui, {\n registry: mounted.registry,\n consumer,\n });\n\n // Mount effects have flushed, but whatever the first render *started* has\n // not settled — the initial fetch that fills a table, typically. This flush\n // drains pending microtasks; `settle` covers anything slower.\n await act(async () => {});\n await config.settle?.(mounted);\n\n return { scenario, surface, mounted, app: mounted.app as TApp, consumer };\n}\n"],"mappings":";AAAA,SAAS,WAAW;AACpB,SAAS,0BAAqD;AAIvD,IAAM,uBAAsC,EAAE,IAAI,OAAO,MAAM,OAAO;AAyB7E,eAAsB,cACpB,QACA,UACA,UAAgC,CAAC,GACD;AAChC,QAAM,QAAQ,OAAO,UAAU,QAAQ;AACvC,MAAI,CAAC,OAAO;AACV,UAAM,QAAQ,OAAO,KAAK,OAAO,SAAS;AAC1C,UAAM,IAAI;AAAA,MACR,qBAAqB,QAAQ,gCAC3B,MAAM,SAAS,IAAI,MAAM,IAAI,CAAC,SAAS,IAAI,IAAI,GAAG,EAAE,KAAK,IAAI,IAAI,MACnE;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,QAAQ,YAAY,OAAO,YAAY;AACxD,QAAM,UAAU,MAAM,OAAO,MAAM,EAAE,GAAG,OAAO,SAAS,CAAC;AACzD,QAAM,UAAU,MAAM,mBAAmB,QAAQ,IAAI;AAAA,IACnD,UAAU,QAAQ;AAAA,IAClB;AAAA,EACF,CAAC;AAKD,QAAM,IAAI,YAAY;AAAA,EAAC,CAAC;AACxB,QAAM,OAAO,SAAS,OAAO;AAE7B,SAAO,EAAE,UAAU,SAAS,SAAS,KAAK,QAAQ,KAAa,SAAS;AAC1E;","names":[]}
|