@agent-surface/cli 0.10.0 → 0.11.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.
Files changed (41) hide show
  1. package/README.md +64 -23
  2. package/dist/bin.js +63 -41
  3. package/dist/bin.js.map +1 -1
  4. package/dist/check-XU3FYNGJ.js +122 -0
  5. package/dist/check-XU3FYNGJ.js.map +1 -0
  6. package/dist/chunk-2FG527AM.js +740 -0
  7. package/dist/chunk-2FG527AM.js.map +1 -0
  8. package/dist/chunk-AFLVTBI6.js +316 -0
  9. package/dist/chunk-AFLVTBI6.js.map +1 -0
  10. package/dist/chunk-DYDSJM7R.js +170 -0
  11. package/dist/chunk-DYDSJM7R.js.map +1 -0
  12. package/dist/{chunk-FYEXHWGG.js → chunk-QIVOZAWX.js} +52 -2
  13. package/dist/chunk-QIVOZAWX.js.map +1 -0
  14. package/dist/index.d.ts +36 -2
  15. package/dist/init-ODFEGU3P.js +141 -0
  16. package/dist/init-ODFEGU3P.js.map +1 -0
  17. package/dist/{ink-HBPOQTRS.js → ink-P23VKP4H.js} +102 -33
  18. package/dist/ink-P23VKP4H.js.map +1 -0
  19. package/dist/inspect-6XKULUH2.js +108 -0
  20. package/dist/inspect-6XKULUH2.js.map +1 -0
  21. package/dist/snapshot-YGEDJVTG.js +59 -0
  22. package/dist/snapshot-YGEDJVTG.js.map +1 -0
  23. package/package.json +4 -4
  24. package/dist/capabilities-OLFYMHCL.js +0 -37
  25. package/dist/capabilities-OLFYMHCL.js.map +0 -1
  26. package/dist/check-IN4XKAND.js +0 -84
  27. package/dist/check-IN4XKAND.js.map +0 -1
  28. package/dist/chunk-4AEQKM2X.js +0 -498
  29. package/dist/chunk-4AEQKM2X.js.map +0 -1
  30. package/dist/chunk-A27Y7ALQ.js +0 -51
  31. package/dist/chunk-A27Y7ALQ.js.map +0 -1
  32. package/dist/chunk-FYEXHWGG.js.map +0 -1
  33. package/dist/chunk-ODUIFFPM.js +0 -104
  34. package/dist/chunk-ODUIFFPM.js.map +0 -1
  35. package/dist/coverage-HCHLJTDD.js +0 -133
  36. package/dist/coverage-HCHLJTDD.js.map +0 -1
  37. package/dist/ink-HBPOQTRS.js.map +0 -1
  38. package/dist/inspect-NJNB6CAS.js +0 -213
  39. package/dist/inspect-NJNB6CAS.js.map +0 -1
  40. package/dist/snapshot-JQAB73OV.js +0 -38
  41. package/dist/snapshot-JQAB73OV.js.map +0 -1
@@ -1,16 +1,40 @@
1
1
  import {
2
- formatValue
3
- } from "./chunk-ODUIFFPM.js";
2
+ flatRows
3
+ } from "./chunk-DYDSJM7R.js";
4
4
 
5
5
  // src/render/ink.tsx
6
- import { Box, Static, Text } from "ink";
6
+ import { Box, Static, Text, useInput } from "ink";
7
7
  import Spinner from "ink-spinner";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  var OUTCOME = {
10
- expose: { mark: "\u25CF", color: "green" },
11
- disable: { mark: "\u25D0", color: "yellow" },
12
- hide: { mark: "\u25CB", color: "red" }
10
+ expose: { mark: "\u25CF", color: "green", state: "callable" },
11
+ disable: { mark: "\u25D0", color: "yellow", state: "disabled" },
12
+ hide: { mark: "\u25CB", color: "red", state: "hidden" }
13
13
  };
14
+ var NONE = "\u2014";
15
+ function widthsFor(headers, rows) {
16
+ return headers.map(
17
+ (header, column) => Math.max(header.length, ...rows.map((row) => (row[column] ?? "").length))
18
+ );
19
+ }
20
+ function pad(value, width) {
21
+ return value.padEnd(width);
22
+ }
23
+ function Confirm({
24
+ question,
25
+ onAnswer
26
+ }) {
27
+ useInput((input, key) => {
28
+ if (key.return || input.toLowerCase() === "y") onAnswer(true);
29
+ else if (key.escape || input.toLowerCase() === "n" || key.ctrl && input === "c") {
30
+ onAnswer(false);
31
+ }
32
+ });
33
+ return /* @__PURE__ */ jsxs(Box, { marginTop: 1, children: [
34
+ /* @__PURE__ */ jsx(Text, { bold: true, children: question }),
35
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (Y/n) " })
36
+ ] });
37
+ }
14
38
  function Loading({ label }) {
15
39
  return /* @__PURE__ */ jsxs(Text, { children: [
16
40
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) }),
@@ -106,43 +130,88 @@ function Empty({ view }) {
106
130
  view.explained ? null : /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Re-run with --explain to see whether a policy hid it." })
107
131
  ] });
108
132
  }
109
- function Surface({ view }) {
133
+ var HEADERS = ["CAPABILITY", "KIND", "EFFECT", "STATE", "FLAGS"];
134
+ function cellsFor(row) {
135
+ return [
136
+ row.path,
137
+ row.kind,
138
+ row.effect ?? NONE,
139
+ OUTCOME[row.outcome].state,
140
+ row.flags.length > 0 ? row.flags.join(" \xB7 ") : NONE
141
+ ];
142
+ }
143
+ function TableRow({
144
+ row,
145
+ cells,
146
+ widths
147
+ }) {
148
+ const outcome = OUTCOME[row.outcome];
149
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
150
+ /* @__PURE__ */ jsxs(Box, { children: [
151
+ /* @__PURE__ */ jsx(Text, { bold: true, children: `${pad(cells[0], widths[0])} ` }),
152
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${pad(cells[1], widths[1])} ` }),
153
+ /* @__PURE__ */ jsx(Text, { children: `${pad(cells[2], widths[2])} ` }),
154
+ /* @__PURE__ */ jsx(Text, { color: outcome.color, children: `${pad(cells[3], widths[3])} ` }),
155
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: cells[4] })
156
+ ] }),
157
+ row.reason ? /* @__PURE__ */ jsx(Box, { paddingLeft: 4, children: /* @__PURE__ */ jsx(Text, { color: "yellow", wrap: "wrap", children: `\u2937 ${row.reason}` }) }) : null
158
+ ] });
159
+ }
160
+ function CapabilityTable({ rows }) {
161
+ const cells = rows.map(cellsFor);
162
+ const widths = widthsFor(HEADERS, cells);
163
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
164
+ /* @__PURE__ */ jsx(Box, { children: HEADERS.map((header, column) => /* @__PURE__ */ jsx(Text, { dimColor: true, bold: true, children: column === HEADERS.length - 1 ? header : `${pad(header, widths[column])} ` }, header)) }),
165
+ rows.map((row, index) => /* @__PURE__ */ jsx(
166
+ TableRow,
167
+ {
168
+ row,
169
+ cells: cells[index],
170
+ widths
171
+ },
172
+ `${row.capabilityId}-${index}`
173
+ ))
174
+ ] });
175
+ }
176
+ function Surface({
177
+ view,
178
+ detail
179
+ }) {
110
180
  const populated = view.groups.filter((group) => group.rows.length > 0);
181
+ const rows = flatRows(view);
111
182
  const blocks = [
112
183
  { key: "__header" },
113
- ...populated.map((group) => ({ key: group.heading, group }))
184
+ ...detail ? populated.map((group) => ({ key: group.heading, group })) : rows.length > 0 ? [{ key: "__table", rows }] : []
114
185
  ];
115
- return /* @__PURE__ */ jsx(Static, { items: blocks, children: (block) => block.group ? /* @__PURE__ */ jsx(Group, { group: block.group }, block.key) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
186
+ return /* @__PURE__ */ jsx(Static, { items: blocks, children: (block) => block.group ? /* @__PURE__ */ jsx(Group, { group: block.group }, block.key) : block.rows ? /* @__PURE__ */ jsx(CapabilityTable, { rows: block.rows }, block.key) : /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
116
187
  /* @__PURE__ */ jsx(Header, { view }),
117
188
  view.rejections.length > 0 ? /* @__PURE__ */ jsx(Rejections, { view }) : null,
118
- populated.length === 0 ? /* @__PURE__ */ jsx(Empty, { view }) : null
189
+ rows.length === 0 ? /* @__PURE__ */ jsx(Empty, { view }) : null
119
190
  ] }, block.key) });
120
191
  }
121
- function Drift({
122
- scenario,
123
- entries
124
- }) {
125
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
126
- /* @__PURE__ */ jsxs(Box, { children: [
127
- /* @__PURE__ */ jsx(Text, { backgroundColor: "yellow", color: "black", bold: true, children: ` ${scenario} ` }),
128
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${entries.length} change${entries.length === 1 ? "" : "s"}` })
129
- ] }),
130
- entries.map((entry) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingLeft: 2, children: [
131
- entry.subject ? /* @__PURE__ */ jsxs(Text, { bold: true, children: [
132
- entry.subject,
133
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${entry.path}` })
134
- ] }) : null,
135
- entry.kind === "added" ? /* @__PURE__ */ jsx(Text, { color: "green", wrap: "wrap", children: `+ ${entry.path} ${formatValue(entry.after)}` }) : entry.kind === "removed" ? /* @__PURE__ */ jsx(Text, { color: "red", wrap: "wrap", children: `- ${entry.path} ${formatValue(entry.before)}` }) : /* @__PURE__ */ jsxs(Fragment, { children: [
136
- /* @__PURE__ */ jsx(Text, { color: "yellow", children: `~ ${entry.path}` }),
137
- /* @__PURE__ */ jsx(Text, { color: "red", wrap: "wrap", children: ` before: ${formatValue(entry.before)}` }),
138
- /* @__PURE__ */ jsx(Text, { color: "green", wrap: "wrap", children: ` after: ${formatValue(entry.after)}` })
139
- ] })
140
- ] }, `${entry.kind}-${entry.path}`))
141
- ] });
192
+ function Coverage({ report }) {
193
+ const clean = report.unreached.length === 0 && report.unresolved.length === 0 && report.staleAllowlist.length === 0;
194
+ return /* @__PURE__ */ jsx(Static, { items: [{ key: "__coverage" }], children: (block) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
195
+ report.unreached.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
196
+ /* @__PURE__ */ jsxs(Box, { children: [
197
+ /* @__PURE__ */ jsx(Text, { backgroundColor: "red", color: "black", bold: true, children: " UNREACHED " }),
198
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` authored, and no scenario mounts it ${report.unreached.length}` })
199
+ ] }),
200
+ report.unreached.map((entry) => /* @__PURE__ */ jsxs(Box, { paddingLeft: 2, children: [
201
+ /* @__PURE__ */ jsx(Text, { bold: true, children: entry.capabilityId }),
202
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${entry.origin.file}:${entry.origin.line}` })
203
+ ] }, entry.capabilityId))
204
+ ] }) : null,
205
+ /* @__PURE__ */ jsxs(Box, { marginTop: report.unreached.length > 0 ? 1 : 0, children: [
206
+ /* @__PURE__ */ jsx(Text, { color: clean ? "green" : "red", bold: true, children: `${report.authored} authored \xB7 ${report.reached} reached \xB7 ${report.unreached.length} unreached` }),
207
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${report.scenarios.join(", ")}` })
208
+ ] })
209
+ ] }, block.key) });
142
210
  }
143
211
  export {
144
- Drift,
212
+ Confirm,
213
+ Coverage,
145
214
  Loading,
146
215
  Surface
147
216
  };
148
- //# sourceMappingURL=ink-HBPOQTRS.js.map
217
+ //# sourceMappingURL=ink-P23VKP4H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/render/ink.tsx"],"sourcesContent":["import type { ReactElement } from \"react\";\nimport { Box, Static, Text, useInput } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport type { CapabilityRow, CapabilityGroup, SurfaceView } from \"./model.js\";\nimport { flatRows } from \"./model.js\";\nimport type { CoverageReport } from \"../coverage.js\";\n\nconst OUTCOME = {\n expose: { mark: \"●\", color: \"green\" as const, state: \"callable\" },\n disable: { mark: \"◐\", color: \"yellow\" as const, state: \"disabled\" },\n hide: { mark: \"○\", color: \"red\" as const, state: \"hidden\" },\n};\n\nconst NONE = \"—\";\n\n/**\n * Same column widths as the plain renderer computes, and for the same reason:\n * from the content, never from the terminal. A TTY table that reflows on resize\n * and a piped table that does not would be two different renderings of one view\n * model, which is exactly what this file exists to prevent.\n */\nfunction widthsFor(headers: string[], rows: string[][]): number[] {\n return headers.map((header, column) =>\n Math.max(header.length, ...rows.map((row) => (row[column] ?? \"\").length)),\n );\n}\n\nfunction pad(value: string, width: number): string {\n return value.padEnd(width);\n}\n\n/**\n * `init`'s one question. Enter accepts, because the answer this asks for is the\n * one the summary above it has already made the case for — and because a\n * scaffold is the least destructive thing this package writes.\n */\nexport function Confirm({\n question,\n onAnswer,\n}: {\n question: string;\n onAnswer: (yes: boolean) => void;\n}): ReactElement {\n useInput((input, key) => {\n if (key.return || input.toLowerCase() === \"y\") onAnswer(true);\n else if (key.escape || input.toLowerCase() === \"n\" || (key.ctrl && input === \"c\")) {\n onAnswer(false);\n }\n });\n return (\n <Box marginTop={1}>\n <Text bold>{question}</Text>\n <Text dimColor>{\" (Y/n) \"}</Text>\n </Box>\n );\n}\n\nexport function Loading({ label }: { label: string }): ReactElement {\n return (\n <Text>\n <Text color=\"cyan\">\n <Spinner type=\"dots\" />\n </Text>\n {` ${label}`}\n </Text>\n );\n}\n\nfunction PolicyLine({\n policy,\n}: {\n policy: NonNullable<CapabilityRow[\"policies\"]>[number];\n}): ReactElement {\n const vote = policy.discovery?.decision;\n const color = vote === \"hide\" ? \"red\" : vote === \"disable\" ? \"yellow\" : \"green\";\n return (\n <Box paddingLeft={6}>\n <Text dimColor>policy </Text>\n <Text bold>{policy.name}</Text>\n <Text dimColor>{` (${policy.scope}${policy.phases.length ? `, ${policy.phases.join(\"/\")}` : \"\"}) `}</Text>\n {vote ? (\n <Text color={color}>\n {vote}\n {policy.discovery?.decision === \"disable\" ? ` — ${policy.discovery.reason}` : \"\"}\n </Text>\n ) : (\n <Text dimColor>no discovery hook</Text>\n )}\n {policy.threw ? <Text color=\"red\" bold>{\" THREW\"}</Text> : null}\n {policy.confirmationEscalation ? (\n <Text color=\"magenta\">{\" escalates-confirmation\"}</Text>\n ) : null}\n </Box>\n );\n}\n\nfunction Capability({ row }: { row: CapabilityRow }): ReactElement {\n const outcome = OUTCOME[row.outcome];\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text color={outcome.color}>{` ${outcome.mark} `}</Text>\n <Text bold>{row.name}</Text>\n {row.tags.length > 0 ? <Text dimColor>{` ${row.tags.join(\" · \")}`}</Text> : null}\n </Box>\n <Box paddingLeft={4}>\n <Text dimColor wrap=\"wrap\">\n {row.description}\n </Text>\n </Box>\n {row.reason ? (\n <Box paddingLeft={4}>\n <Text color=\"yellow\" wrap=\"wrap\">{`⤷ ${row.reason}`}</Text>\n </Box>\n ) : null}\n {row.policies\n ? row.policies.length > 0\n ? row.policies.map((policy, index) => (\n <PolicyLine key={`${policy.name}-${index}`} policy={policy} />\n ))\n : [\n <Box key=\"none\" paddingLeft={6}>\n <Text dimColor>policies: none</Text>\n </Box>,\n ]\n : null}\n {row.policies && row.availability && !row.availability.available ? (\n <Box paddingLeft={6}>\n <Text dimColor>{`availability: unavailable${\n row.availability.reason ? ` — ${row.availability.reason}` : \"\"\n }`}</Text>\n </Box>\n ) : null}\n {row.schemas?.input !== undefined ? (\n <Box paddingLeft={6}>\n <Text dimColor wrap=\"wrap\">{`input: ${JSON.stringify(row.schemas.input)}`}</Text>\n </Box>\n ) : null}\n {row.schemas?.output !== undefined ? (\n <Box paddingLeft={6}>\n <Text dimColor wrap=\"wrap\">{`output: ${JSON.stringify(row.schemas.output)}`}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\nfunction Group({ group }: { group: CapabilityGroup }): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text backgroundColor=\"blueBright\" color=\"black\" bold>{` ${group.heading} `}</Text>\n <Text dimColor>{` ${group.rows.length}`}</Text>\n </Box>\n {group.rows.map((row) => (\n <Capability key={`${row.capabilityId}-${row.name}`} row={row} />\n ))}\n </Box>\n );\n}\n\n/**\n * The header states everything the counts are relative to (`AS-CLI-007`): the\n * scenario, the route, and the scope when one is active — a scope filters both\n * projections, so an unqualified count reads as a claim about the whole surface.\n * `hidden` is unconditional here for the same reason it is in plain text.\n */\nfunction Header({ view }: { view: SurfaceView }): ReactElement {\n return (\n <Box>\n <Text bold>{view.scenario}</Text>\n {view.route ? <Text dimColor>{` ${view.route}`}</Text> : null}\n {view.scope && view.scope.length > 0 ? (\n <Text color=\"cyan\">{` scope ${view.scope.join(\" \")}`}</Text>\n ) : null}\n <Text dimColor>{\" · \"}</Text>\n <Text color=\"green\">{`${view.counts.callable} callable`}</Text>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"yellow\">{`${view.counts.disabled} visible-disabled`}</Text>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"red\">{`${view.counts.hidden} hidden`}</Text>\n {view.rejections.length > 0 ? (\n <>\n <Text dimColor>{\", \"}</Text>\n <Text color=\"magenta\">\n {`${view.rejections.length} registration${\n view.rejections.length === 1 ? \"\" : \"s\"\n } rejected`}\n </Text>\n </>\n ) : null}\n </Box>\n );\n}\n\n/**\n * Rejected registrations (`AS-CLI-006`). A dead handle leaves no trace in either\n * projection, so without this block a copy-pasted component `type` removes a\n * capability and prints nothing anywhere.\n */\nfunction Rejections({ view }: { view: SurfaceView }): ReactElement {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n <Text backgroundColor=\"magenta\" color=\"black\" bold>\n {\" rejected during mount \"}\n </Text>\n <Text dimColor>{` ${view.rejections.length}`}</Text>\n </Box>\n {view.rejections.map((rejection) => (\n <Box key={`${rejection.componentType}@${rejection.instanceId}-${rejection.reason}`}>\n <Text color=\"magenta\">{\" ! \"}</Text>\n <Text bold>{`${rejection.componentType} (${rejection.instanceId})`}</Text>\n <Text dimColor>\n {rejection.reason === \"duplicate\"\n ? \" duplicate — an earlier registration holds this key\"\n : \" guard — onRegister rejected this registration\"}\n </Text>\n </Box>\n ))}\n </Box>\n );\n}\n\nfunction Empty({ view }: { view: SurfaceView }): ReactElement {\n if (view.counts.hidden > 0) {\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor wrap=\"wrap\">\n {`Nothing is callable here — all ${view.counts.hidden} registered capabilities were hidden by policy. `}\n The surface is empty by decision, not because nothing was annotated.\n </Text>\n {view.explained ? null : (\n <Text dimColor>Re-run with --explain to see which policy hid them.</Text>\n )}\n </Box>\n );\n }\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text dimColor wrap=\"wrap\">\n Nothing is registered for this scenario — the agent has no surface here. That is the\n default: capabilities exist only where they were explicitly annotated.\n </Text>\n {view.explained ? null : (\n <Text dimColor>Re-run with --explain to see whether a policy hid it.</Text>\n )}\n </Box>\n );\n}\n\nconst HEADERS = [\"CAPABILITY\", \"KIND\", \"EFFECT\", \"STATE\", \"FLAGS\"];\n\nfunction cellsFor(row: CapabilityRow): string[] {\n return [\n row.path,\n row.kind,\n row.effect ?? NONE,\n OUTCOME[row.outcome].state,\n row.flags.length > 0 ? row.flags.join(\" · \") : NONE,\n ];\n}\n\nfunction TableRow({\n row,\n cells,\n widths,\n}: {\n row: CapabilityRow;\n cells: string[];\n widths: number[];\n}): ReactElement {\n const outcome = OUTCOME[row.outcome];\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Text bold>{`${pad(cells[0]!, widths[0]!)} `}</Text>\n <Text dimColor>{`${pad(cells[1]!, widths[1]!)} `}</Text>\n <Text>{`${pad(cells[2]!, widths[2]!)} `}</Text>\n <Text color={outcome.color}>{`${pad(cells[3]!, widths[3]!)} `}</Text>\n <Text dimColor>{cells[4]!}</Text>\n </Box>\n {row.reason ? (\n <Box paddingLeft={4}>\n <Text color=\"yellow\" wrap=\"wrap\">{`⤷ ${row.reason}`}</Text>\n </Box>\n ) : null}\n </Box>\n );\n}\n\n/**\n * One capability per line, aligned — the scanning view, and the default. The\n * grouped paragraphs below stay for `--detail`, `--explain` and `--schemas`,\n * whose payloads (policy chains, JSON Schemas) cannot live in a table cell.\n */\nfunction CapabilityTable({ rows }: { rows: CapabilityRow[] }): ReactElement {\n const cells = rows.map(cellsFor);\n const widths = widthsFor(HEADERS, cells);\n return (\n <Box flexDirection=\"column\" marginTop={1}>\n <Box>\n {HEADERS.map((header, column) => (\n <Text key={header} dimColor bold>\n {column === HEADERS.length - 1 ? header : `${pad(header, widths[column]!)} `}\n </Text>\n ))}\n </Box>\n {rows.map((row, index) => (\n <TableRow\n key={`${row.capabilityId}-${index}`}\n row={row}\n cells={cells[index]!}\n widths={widths}\n />\n ))}\n </Box>\n );\n}\n\ntype Block = { key: string; group?: CapabilityGroup; rows?: CapabilityRow[] };\n\nexport function Surface({\n view,\n detail,\n}: {\n view: SurfaceView;\n detail?: boolean;\n}): ReactElement {\n const populated = view.groups.filter((group) => group.rows.length > 0);\n const rows = flatRows(view);\n\n // Everything goes through <Static>, header included. Ink paints static output\n // once, permanently, above the live frame — and erases the live frame on\n // unmount. A one-shot render that leaves anything outside <Static> therefore\n // prints it and then wipes it, which is exactly what happened to this header.\n const blocks: Block[] = [\n { key: \"__header\" },\n ...(detail\n ? populated.map((group) => ({ key: group.heading, group }))\n : rows.length > 0\n ? [{ key: \"__table\", rows }]\n : []),\n ];\n\n return (\n <Static items={blocks}>\n {(block) =>\n block.group ? (\n <Group key={block.key} group={block.group} />\n ) : block.rows ? (\n <CapabilityTable key={block.key} rows={block.rows} />\n ) : (\n <Box key={block.key} flexDirection=\"column\">\n <Header view={view} />\n {view.rejections.length > 0 ? <Rejections view={view} /> : null}\n {rows.length === 0 ? <Empty view={view} /> : null}\n </Box>\n )\n }\n </Static>\n );\n}\n\n/**\n * The verdict — authored minus reached. The finding the command surface used to\n * keep behind a fifth command, so it is the last thing painted and the thing a\n * reader stops on.\n */\nexport function Coverage({ report }: { report: CoverageReport }): ReactElement {\n const clean =\n report.unreached.length === 0 &&\n report.unresolved.length === 0 &&\n report.staleAllowlist.length === 0;\n\n return (\n <Static items={[{ key: \"__coverage\" }]}>\n {(block) => (\n <Box key={block.key} flexDirection=\"column\" marginTop={1}>\n {report.unreached.length > 0 ? (\n <Box flexDirection=\"column\">\n <Box>\n <Text backgroundColor=\"red\" color=\"black\" bold>\n {\" UNREACHED \"}\n </Text>\n <Text dimColor>{` authored, and no scenario mounts it ${report.unreached.length}`}</Text>\n </Box>\n {report.unreached.map((entry) => (\n <Box key={entry.capabilityId} paddingLeft={2}>\n <Text bold>{entry.capabilityId}</Text>\n <Text dimColor>{` ${entry.origin.file}:${entry.origin.line}`}</Text>\n </Box>\n ))}\n </Box>\n ) : null}\n <Box marginTop={report.unreached.length > 0 ? 1 : 0}>\n <Text color={clean ? \"green\" : \"red\"} bold>\n {`${report.authored} authored · ${report.reached} reached · ${report.unreached.length} unreached`}\n </Text>\n <Text dimColor>{` ${report.scenarios.join(\", \")}`}</Text>\n </Box>\n </Box>\n )}\n </Static>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,108 @@
1
+ import {
2
+ renderCatalogPlain,
3
+ renderCoveragePlain,
4
+ renderFailuresPlain,
5
+ renderNoVerdictPlain,
6
+ renderSurfacePlain
7
+ } from "./chunk-AFLVTBI6.js";
8
+ import {
9
+ buildView
10
+ } from "./chunk-DYDSJM7R.js";
11
+ import {
12
+ joinCoverage,
13
+ mountScenarios,
14
+ readInventory
15
+ } from "./chunk-2FG527AM.js";
16
+ import {
17
+ isPlain,
18
+ loadInk,
19
+ paint,
20
+ write,
21
+ writeError
22
+ } from "./chunk-QIVOZAWX.js";
23
+
24
+ // src/commands/inspect.tsx
25
+ import { jsx } from "react/jsx-runtime";
26
+ function jsonForScenario(result, explain) {
27
+ return {
28
+ scenario: result.scenario,
29
+ ...result.scope ? { scope: result.scope } : {},
30
+ snapshot: result.snapshot,
31
+ // Unconditional, and unconditionally present even when empty (`AS-CLI-006`):
32
+ // a consumer that has to distinguish "no rejections" from "this CLI predates
33
+ // the field" cannot rely on an absent key.
34
+ rejections: result.rejections,
35
+ ...explain ? { explanation: result.explanation } : {}
36
+ };
37
+ }
38
+ async function runInspect(options) {
39
+ const analysis = {
40
+ configPath: options.configPath,
41
+ depth: options.depth,
42
+ ...options.scenario ? { scenario: options.scenario } : {},
43
+ ...options.scope ? { scope: options.scope } : {},
44
+ ...options.tsconfig ? { tsconfig: options.tsconfig } : {},
45
+ ...options.baselineDir ? { baselineDir: options.baselineDir } : {}
46
+ };
47
+ const detail = options.detail === true || options.explain === true || options.schemas === true;
48
+ const inventory = readInventory(analysis);
49
+ if (inventory && !options.json) {
50
+ write(renderCatalogPlain(inventory, { standalone: options.depth === "static" }));
51
+ }
52
+ const ink = isPlain(options) ? null : await loadInk();
53
+ const scenarios = [];
54
+ let printed = 0;
55
+ const runtime = await mountScenarios(analysis, async (result) => {
56
+ if (options.json) {
57
+ scenarios.push(jsonForScenario(result, options.explain === true));
58
+ return;
59
+ }
60
+ const view = buildView(result, {
61
+ ...options.explain ? { explain: true } : {},
62
+ ...options.schemas ? { schemas: true } : {}
63
+ });
64
+ if (ink) await paint(/* @__PURE__ */ jsx(ink.Surface, { view, detail }));
65
+ else {
66
+ const rendered = renderSurfacePlain(view, { detail });
67
+ write(printed === 0 && !inventory ? rendered : `
68
+ ${rendered}`);
69
+ }
70
+ printed += 1;
71
+ });
72
+ const coverage = joinCoverage(inventory, runtime, analysis);
73
+ if (options.json) {
74
+ write(
75
+ JSON.stringify(
76
+ {
77
+ // One shape whatever the depth, so a consumer never branches on how
78
+ // the command was invoked. A half the depth did not compute is
79
+ // `null`, which is a different statement from `[]` or `{}`.
80
+ depth: options.depth,
81
+ catalog: inventory ?? null,
82
+ scenarios,
83
+ failures: runtime?.failures ?? [],
84
+ coverage: coverage ?? null
85
+ },
86
+ null,
87
+ 2
88
+ )
89
+ );
90
+ return runtime && runtime.failures.length > 0 ? 2 : 0;
91
+ }
92
+ if (runtime && runtime.failures.length > 0) {
93
+ writeError(`
94
+ ${renderFailuresPlain(runtime.failures)}`);
95
+ }
96
+ if (coverage) {
97
+ write("");
98
+ write(renderCoveragePlain(coverage));
99
+ } else if (inventory && runtime && runtime.failures.length > 0) {
100
+ writeError(`
101
+ ${renderNoVerdictPlain(runtime.failures)}`);
102
+ }
103
+ return runtime && runtime.failures.length > 0 ? 2 : 0;
104
+ }
105
+ export {
106
+ runInspect
107
+ };
108
+ //# sourceMappingURL=inspect-6XKULUH2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/inspect.tsx"],"sourcesContent":["import {\n joinCoverage,\n mountScenarios,\n readInventory,\n type AnalysisOptions,\n type Depth,\n} from \"../analysis.js\";\nimport { buildView } from \"../render/model.js\";\nimport {\n renderCatalogPlain,\n renderCoveragePlain,\n renderFailuresPlain,\n renderNoVerdictPlain,\n renderSurfacePlain,\n} from \"../render/plain.js\";\nimport { isPlain, loadInk, paint, transient, write, writeError } from \"../output.js\";\nimport type { CollectResult } from \"../collect.js\";\n\nexport interface InspectOptions {\n configPath: string;\n depth: Depth;\n scenario?: string;\n scope?: string[];\n tsconfig?: string;\n baselineDir?: string;\n detail?: boolean;\n explain?: boolean;\n schemas?: boolean;\n json?: boolean;\n plain?: boolean;\n}\n\nfunction jsonForScenario(result: CollectResult, explain: boolean): Record<string, unknown> {\n return {\n scenario: result.scenario,\n ...(result.scope ? { scope: result.scope } : {}),\n snapshot: result.snapshot,\n // Unconditional, and unconditionally present even when empty (`AS-CLI-006`):\n // a consumer that has to distinguish \"no rejections\" from \"this CLI predates\n // the field\" cannot rely on an absent key.\n rejections: result.rejections,\n ...(explain ? { explanation: result.explanation } : {}),\n };\n}\n\n/**\n * The whole surface: what this codebase authors, what a mount surfaces, and the\n * difference between them.\n *\n * It answers findings, it does not gate on them — exit `0` whatever it reports,\n * because `check` is the gate and a viewer that sometimes fails is a viewer\n * nobody puts in a pipeline. The exception is `2`, which is not a finding: the\n * command could not run at all.\n *\n * **Order is the design.** The catalog prints first because it is ready before\n * anything mounts; each scenario prints as it finishes, so a config with ten of\n * them is not ten mounts of blank terminal; the verdict prints last, because it\n * is the only part that needs every scenario to have finished — and because a\n * reader who stops at the bottom should stop on the finding.\n */\nexport async function runInspect(options: InspectOptions): Promise<number> {\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 // Policy chains and JSON Schemas are multi-line by nature, so asking for\n // either is asking for the view that can hold them.\n const detail = options.detail === true || options.explain === true || options.schemas === true;\n\n const inventory = readInventory(analysis);\n if (inventory && !options.json) {\n // At `--depth static` this listing is the output. At `--depth full` the\n // scenario tables below carry the same capabilities and the verdict names\n // the ones they miss, so only the summary line prints here.\n write(renderCatalogPlain(inventory, { standalone: options.depth === \"static\" }));\n }\n\n // `null` when Ink cannot run here (React 18 host), which is a fallback to\n // plain text rather than a failed command — see loadInk().\n const ink = isPlain(options) ? null : await loadInk();\n const scenarios: Array<Record<string, unknown>> = [];\n let printed = 0;\n\n const runtime = await mountScenarios(analysis, async (result) => {\n if (options.json) {\n scenarios.push(jsonForScenario(result, options.explain === true));\n return;\n }\n const view = buildView(result, {\n ...(options.explain ? { explain: true } : {}),\n ...(options.schemas ? { schemas: true } : {}),\n });\n if (ink) await paint(<ink.Surface view={view} detail={detail} />);\n else {\n const rendered = renderSurfacePlain(view, { detail });\n write(printed === 0 && !inventory ? rendered : `\\n${rendered}`);\n }\n printed += 1;\n });\n\n const coverage = joinCoverage(inventory, runtime, analysis);\n\n if (options.json) {\n write(\n JSON.stringify(\n {\n // One shape whatever the depth, so a consumer never branches on how\n // the command was invoked. A half the depth did not compute is\n // `null`, which is a different statement from `[]` or `{}`.\n depth: options.depth,\n catalog: inventory ?? null,\n scenarios,\n failures: runtime?.failures ?? [],\n coverage: coverage ?? null,\n },\n null,\n 2,\n ),\n );\n return runtime && runtime.failures.length > 0 ? 2 : 0;\n }\n\n if (runtime && runtime.failures.length > 0) {\n writeError(`\\n${renderFailuresPlain(runtime.failures)}`);\n }\n if (coverage) {\n write(\"\");\n write(renderCoveragePlain(coverage));\n } else if (inventory && runtime && runtime.failures.length > 0) {\n writeError(`\\n${renderNoVerdictPlain(runtime.failures)}`);\n }\n\n // A scenario that would not mount is not a finding about the surface, it is\n // the command failing to observe one. `2` — the same code a usage error gets,\n // because CI has to tell both apart from \"the surface changed\".\n return runtime && runtime.failures.length > 0 ? 2 : 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAiGyB;AAjEzB,SAAS,gBAAgB,QAAuB,SAA2C;AACzF,SAAO;AAAA,IACL,UAAU,OAAO;AAAA,IACjB,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,IAC9C,UAAU,OAAO;AAAA;AAAA;AAAA;AAAA,IAIjB,YAAY,OAAO;AAAA,IACnB,GAAI,UAAU,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,EACvD;AACF;AAiBA,eAAsB,WAAW,SAA0C;AACzE,QAAM,WAA4B;AAAA,IAChC,YAAY,QAAQ;AAAA,IACpB,OAAO,QAAQ;AAAA,IACf,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IACzD,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,IAChD,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IACzD,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;AAAA,EACpE;AAIA,QAAM,SAAS,QAAQ,WAAW,QAAQ,QAAQ,YAAY,QAAQ,QAAQ,YAAY;AAE1F,QAAM,YAAY,cAAc,QAAQ;AACxC,MAAI,aAAa,CAAC,QAAQ,MAAM;AAI9B,UAAM,mBAAmB,WAAW,EAAE,YAAY,QAAQ,UAAU,SAAS,CAAC,CAAC;AAAA,EACjF;AAIA,QAAM,MAAM,QAAQ,OAAO,IAAI,OAAO,MAAM,QAAQ;AACpD,QAAM,YAA4C,CAAC;AACnD,MAAI,UAAU;AAEd,QAAM,UAAU,MAAM,eAAe,UAAU,OAAO,WAAW;AAC/D,QAAI,QAAQ,MAAM;AAChB,gBAAU,KAAK,gBAAgB,QAAQ,QAAQ,YAAY,IAAI,CAAC;AAChE;AAAA,IACF;AACA,UAAM,OAAO,UAAU,QAAQ;AAAA,MAC7B,GAAI,QAAQ,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,MAC3C,GAAI,QAAQ,UAAU,EAAE,SAAS,KAAK,IAAI,CAAC;AAAA,IAC7C,CAAC;AACD,QAAI,IAAK,OAAM,MAAM,oBAAC,IAAI,SAAJ,EAAY,MAAY,QAAgB,CAAE;AAAA,SAC3D;AACH,YAAM,WAAW,mBAAmB,MAAM,EAAE,OAAO,CAAC;AACpD,YAAM,YAAY,KAAK,CAAC,YAAY,WAAW;AAAA,EAAK,QAAQ,EAAE;AAAA,IAChE;AACA,eAAW;AAAA,EACb,CAAC;AAED,QAAM,WAAW,aAAa,WAAW,SAAS,QAAQ;AAE1D,MAAI,QAAQ,MAAM;AAChB;AAAA,MACE,KAAK;AAAA,QACH;AAAA;AAAA;AAAA;AAAA,UAIE,OAAO,QAAQ;AAAA,UACf,SAAS,aAAa;AAAA,UACtB;AAAA,UACA,UAAU,SAAS,YAAY,CAAC;AAAA,UAChC,UAAU,YAAY;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AACA,WAAO,WAAW,QAAQ,SAAS,SAAS,IAAI,IAAI;AAAA,EACtD;AAEA,MAAI,WAAW,QAAQ,SAAS,SAAS,GAAG;AAC1C,eAAW;AAAA,EAAK,oBAAoB,QAAQ,QAAQ,CAAC,EAAE;AAAA,EACzD;AACA,MAAI,UAAU;AACZ,UAAM,EAAE;AACR,UAAM,oBAAoB,QAAQ,CAAC;AAAA,EACrC,WAAW,aAAa,WAAW,QAAQ,SAAS,SAAS,GAAG;AAC9D,eAAW;AAAA,EAAK,qBAAqB,QAAQ,QAAQ,CAAC,EAAE;AAAA,EAC1D;AAKA,SAAO,WAAW,QAAQ,SAAS,SAAS,IAAI,IAAI;AACtD;","names":[]}
@@ -0,0 +1,59 @@
1
+ import {
2
+ renderCoveragePlain,
3
+ renderFailuresPlain
4
+ } from "./chunk-AFLVTBI6.js";
5
+ import "./chunk-DYDSJM7R.js";
6
+ import {
7
+ baselinePath,
8
+ joinCoverage,
9
+ mountScenarios,
10
+ normalize,
11
+ readInventory,
12
+ writeBaseline
13
+ } from "./chunk-2FG527AM.js";
14
+ import {
15
+ UsageError,
16
+ write,
17
+ writeError
18
+ } from "./chunk-QIVOZAWX.js";
19
+
20
+ // src/commands/snapshot.ts
21
+ import { relative } from "path";
22
+ async function runSnapshot(options) {
23
+ if (options.depth === "static") {
24
+ throw new UsageError(
25
+ "snapshot --depth static has nothing to write \u2014 a baseline is a projection, and at this depth nothing is mounted."
26
+ );
27
+ }
28
+ const analysis = {
29
+ configPath: options.configPath,
30
+ depth: options.depth,
31
+ ...options.scenario ? { scenario: options.scenario } : {},
32
+ ...options.scope ? { scope: options.scope } : {},
33
+ ...options.tsconfig ? { tsconfig: options.tsconfig } : {},
34
+ ...options.baselineDir ? { baselineDir: options.baselineDir } : {}
35
+ };
36
+ const inventory = readInventory(analysis);
37
+ const runtime = await mountScenarios(analysis);
38
+ if (!runtime) throw new UsageError("snapshot needs a mount, and this depth performs none");
39
+ for (const result of runtime.results) {
40
+ const path = baselinePath(runtime.baselineDir, result.scenario);
41
+ writeBaseline(path, normalize(result.snapshot));
42
+ write(`wrote ${relative(process.cwd(), path)}`);
43
+ }
44
+ const coverage = joinCoverage(inventory, runtime, analysis);
45
+ if (coverage) {
46
+ write("");
47
+ write(renderCoveragePlain(coverage));
48
+ }
49
+ if (runtime.failures.length > 0) {
50
+ writeError(`
51
+ ${renderFailuresPlain(runtime.failures)}`);
52
+ return 2;
53
+ }
54
+ return 0;
55
+ }
56
+ export {
57
+ runSnapshot
58
+ };
59
+ //# sourceMappingURL=snapshot-YGEDJVTG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/snapshot.ts"],"sourcesContent":["import { relative } from \"node:path\";\nimport {\n joinCoverage,\n mountScenarios,\n readInventory,\n UsageError,\n type AnalysisOptions,\n type Depth,\n} from \"../analysis.js\";\nimport { baselinePath, normalize, writeBaseline } from \"../baseline.js\";\nimport { renderCoveragePlain, renderFailuresPlain } from \"../render/plain.js\";\nimport { write, writeError } from \"../output.js\";\n\nexport interface SnapshotOptions {\n configPath: string;\n depth: Depth;\n scenario?: string;\n scope?: string[];\n tsconfig?: string;\n baselineDir?: string;\n}\n\n/**\n * Writes (or refreshes) the committed baseline `check` compares against.\n *\n * It prints the coverage verdict too. This is the command you run to *accept* a\n * change to the surface, which makes it the last moment before a reviewer sees\n * the diff — and accepting a projection while a capability sits behind a route\n * no scenario visits is exactly the state worth hearing about. It reports;\n * `check` is still the only thing that fails.\n */\nexport async function runSnapshot(options: SnapshotOptions): Promise<number> {\n if (options.depth === \"static\") {\n throw new UsageError(\n \"snapshot --depth static has nothing to write — a baseline is a projection, and \" +\n \"at this depth nothing is mounted.\",\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 inventory = readInventory(analysis);\n const runtime = await mountScenarios(analysis);\n if (!runtime) throw new UsageError(\"snapshot needs a mount, and this depth performs none\");\n\n for (const result of runtime.results) {\n const path = baselinePath(runtime.baselineDir, result.scenario);\n writeBaseline(path, normalize(result.snapshot));\n write(`wrote ${relative(process.cwd(), path)}`);\n }\n\n const coverage = joinCoverage(inventory, runtime, analysis);\n if (coverage) {\n write(\"\");\n write(renderCoveragePlain(coverage));\n }\n\n if (runtime.failures.length > 0) {\n // A baseline written for some scenarios and not others is a baseline that\n // will fail `check` for a reason that has nothing to do with the surface.\n writeError(`\\n${renderFailuresPlain(runtime.failures)}`);\n return 2;\n }\n return 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AA+BzB,eAAsB,YAAY,SAA2C;AAC3E,MAAI,QAAQ,UAAU,UAAU;AAC9B,UAAM,IAAI;AAAA,MACR;AAAA,IAEF;AAAA,EACF;AAEA,QAAM,WAA4B;AAAA,IAChC,YAAY,QAAQ;AAAA,IACpB,OAAO,QAAQ;AAAA,IACf,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IACzD,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,IAChD,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,IACzD,GAAI,QAAQ,cAAc,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;AAAA,EACpE;AAEA,QAAM,YAAY,cAAc,QAAQ;AACxC,QAAM,UAAU,MAAM,eAAe,QAAQ;AAC7C,MAAI,CAAC,QAAS,OAAM,IAAI,WAAW,sDAAsD;AAEzF,aAAW,UAAU,QAAQ,SAAS;AACpC,UAAM,OAAO,aAAa,QAAQ,aAAa,OAAO,QAAQ;AAC9D,kBAAc,MAAM,UAAU,OAAO,QAAQ,CAAC;AAC9C,UAAM,SAAS,SAAS,QAAQ,IAAI,GAAG,IAAI,CAAC,EAAE;AAAA,EAChD;AAEA,QAAM,WAAW,aAAa,WAAW,SAAS,QAAQ;AAC1D,MAAI,UAAU;AACZ,UAAM,EAAE;AACR,UAAM,oBAAoB,QAAQ,CAAC;AAAA,EACrC;AAEA,MAAI,QAAQ,SAAS,SAAS,GAAG;AAG/B,eAAW;AAAA,EAAK,oBAAoB,QAAQ,QAAQ,CAAC,EAAE;AACvD,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-surface/cli",
3
- "version": "0.10.0",
3
+ "version": "0.11.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",
@@ -31,9 +31,9 @@
31
31
  "typescript": "^5.9.2",
32
32
  "vite": "^7.1.3",
33
33
  "vite-node": "^3.2.4",
34
- "@agent-surface/core": "^0.10.0",
35
- "@agent-surface/react": "^0.10.0",
36
- "@agent-surface/testing": "^0.10.0"
34
+ "@agent-surface/core": "^0.11.1",
35
+ "@agent-surface/react": "^0.11.1",
36
+ "@agent-surface/testing": "^0.11.1"
37
37
  },
38
38
  "peerDependencies": {
39
39
  "@testing-library/react": ">=14",
@@ -1,37 +0,0 @@
1
- import {
2
- extractCapabilities,
3
- renderInventoryPlain,
4
- unresolved
5
- } from "./chunk-4AEQKM2X.js";
6
- import {
7
- write,
8
- writeError
9
- } from "./chunk-A27Y7ALQ.js";
10
- import "./chunk-ODUIFFPM.js";
11
-
12
- // src/commands/capabilities.ts
13
- import { dirname } from "path";
14
- async function runCapabilities(options) {
15
- const inventory = extractCapabilities({
16
- root: dirname(options.configPath),
17
- ...options.tsconfig ? { tsconfig: options.tsconfig } : {}
18
- });
19
- const unresolvedEntries = unresolved(inventory);
20
- if (options.json) {
21
- write(JSON.stringify(inventory, null, 2));
22
- } else {
23
- write(renderInventoryPlain(inventory));
24
- }
25
- if (unresolvedEntries.length === 0) return 0;
26
- if (options.allowUnresolved) return 0;
27
- if (!options.json) {
28
- writeError(
29
- "\nexiting 1 because the inventory is incomplete \u2014 pass --allow-unresolved to accept it"
30
- );
31
- }
32
- return 1;
33
- }
34
- export {
35
- runCapabilities
36
- };
37
- //# sourceMappingURL=capabilities-OLFYMHCL.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/capabilities.ts"],"sourcesContent":["import { dirname } from \"node:path\";\nimport { extractCapabilities, unresolved } from \"../extract.js\";\nimport { renderInventoryPlain } from \"../render/plain.js\";\nimport { write, writeError } from \"../output.js\";\n\nexport interface CapabilitiesOptions {\n configPath: string;\n tsconfig?: string;\n json?: boolean;\n allowUnresolved?: boolean;\n}\n\n/**\n * The static inventory: what this codebase *authors*, as opposed to what a\n * scenario happens to mount. No Vite dev server, no jsdom, no scenarios, no\n * mount — it reads the TypeScript program and nothing else.\n *\n * The non-zero exit on an unresolved call site is the load-bearing part\n * (`AS-COVER-003`). A partial understanding of the codebase that reports itself\n * as complete is the exact failure this command exists to remove: every number\n * downstream — the coverage denominator above all — is only as trustworthy as\n * the extractor's own admission of what it could not read.\n */\nexport async function runCapabilities(options: CapabilitiesOptions): Promise<number> {\n const inventory = extractCapabilities({\n root: dirname(options.configPath),\n ...(options.tsconfig ? { tsconfig: options.tsconfig } : {}),\n });\n const unresolvedEntries = unresolved(inventory);\n\n if (options.json) {\n write(JSON.stringify(inventory, null, 2));\n } else {\n write(renderInventoryPlain(inventory));\n }\n\n if (unresolvedEntries.length === 0) return 0;\n if (options.allowUnresolved) return 0;\n if (!options.json) {\n writeError(\n \"\\nexiting 1 because the inventory is incomplete — pass --allow-unresolved to accept it\",\n );\n }\n return 1;\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,eAAe;AAuBxB,eAAsB,gBAAgB,SAA+C;AACnF,QAAM,YAAY,oBAAoB;AAAA,IACpC,MAAM,QAAQ,QAAQ,UAAU;AAAA,IAChC,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,EAC3D,CAAC;AACD,QAAM,oBAAoB,WAAW,SAAS;AAE9C,MAAI,QAAQ,MAAM;AAChB,UAAM,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AAAA,EAC1C,OAAO;AACL,UAAM,qBAAqB,SAAS,CAAC;AAAA,EACvC;AAEA,MAAI,kBAAkB,WAAW,EAAG,QAAO;AAC3C,MAAI,QAAQ,gBAAiB,QAAO;AACpC,MAAI,CAAC,QAAQ,MAAM;AACjB;AAAA,MACE;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;","names":[]}
@@ -1,84 +0,0 @@
1
- import {
2
- renderDiffPlain
3
- } from "./chunk-4AEQKM2X.js";
4
- import {
5
- createSurfaceRunner
6
- } from "./chunk-FYEXHWGG.js";
7
- import {
8
- isPlain,
9
- loadInk,
10
- paint,
11
- write,
12
- writeError
13
- } from "./chunk-A27Y7ALQ.js";
14
- import {
15
- annotate,
16
- baselineDirFor,
17
- baselinePath,
18
- diff,
19
- normalize,
20
- readBaseline
21
- } from "./chunk-ODUIFFPM.js";
22
-
23
- // src/commands/check.tsx
24
- import { relative } from "path";
25
- import { jsx } from "react/jsx-runtime";
26
- async function runCheck(options) {
27
- const runner = await createSurfaceRunner(options.configPath);
28
- try {
29
- const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;
30
- const dir = baselineDirFor(options.configPath, options.baselineDir ?? runner.config.baselineDir);
31
- const drifted = [];
32
- for (const scenario of scenarios) {
33
- const result = await runner.collect({
34
- scenario,
35
- ...options.scope ? { scope: options.scope } : {}
36
- });
37
- const path = baselinePath(dir, scenario);
38
- const expected = readBaseline(path);
39
- if (expected === void 0) {
40
- drifted.push({ scenario, missingBaseline: true, entries: [] });
41
- continue;
42
- }
43
- const actual = normalize(result.snapshot);
44
- const entries = annotate(diff(expected, actual), actual, expected);
45
- if (entries.length > 0) drifted.push({ scenario, entries });
46
- }
47
- if (options.json) {
48
- write(JSON.stringify({ ok: drifted.length === 0, drifted }, null, 2));
49
- return drifted.length === 0 ? 0 : 1;
50
- }
51
- if (drifted.length === 0) {
52
- write(`surface matches the baseline in ${scenarios.join(", ")}`);
53
- write(
54
- "that is a statement about these scenarios only; capabilities no scenario mounts are `agent-surface coverage`'s question"
55
- );
56
- return 0;
57
- }
58
- const ink = isPlain(options) ? null : await loadInk();
59
- for (const entry of drifted) {
60
- if (entry.missingBaseline) {
61
- writeError(
62
- `${entry.scenario}: no baseline at ${relative(
63
- process.cwd(),
64
- baselinePath(dir, entry.scenario)
65
- )} \u2014 run \`agent-surface snapshot\` and commit it`
66
- );
67
- continue;
68
- }
69
- if (ink) await paint(/* @__PURE__ */ jsx(ink.Drift, { scenario: entry.scenario, entries: entry.entries }));
70
- else write(renderDiffPlain(entry.scenario, entry.entries));
71
- }
72
- writeError(
73
- `
74
- surface drift in ${drifted.length} scenario${drifted.length === 1 ? "" : "s"} \u2014 review the change, then \`agent-surface snapshot\` to accept it`
75
- );
76
- return 1;
77
- } finally {
78
- await runner.close();
79
- }
80
- }
81
- export {
82
- runCheck
83
- };
84
- //# sourceMappingURL=check-IN4XKAND.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/commands/check.tsx"],"sourcesContent":["import { relative } from \"node:path\";\nimport { createSurfaceRunner } from \"../load.js\";\nimport {\n annotate,\n baselineDirFor,\n baselinePath,\n diff,\n normalize,\n readBaseline,\n type DiffEntry,\n} from \"../baseline.js\";\nimport { renderDiffPlain } from \"../render/plain.js\";\nimport { isPlain, loadInk, paint, write, writeError } from \"../output.js\";\n\nexport interface CheckOptions {\n configPath: string;\n scenario?: string;\n scope?: string[];\n baselineDir?: string;\n json?: boolean;\n plain?: boolean;\n}\n\ninterface ScenarioDrift {\n scenario: string;\n missingBaseline?: boolean;\n entries: DiffEntry[];\n}\n\n/**\n * Compares every scenario against its committed baseline. Exit code is the\n * point: 0 when the surface is what the repo says it is, 1 when it drifted —\n * so CI fails on an unreviewed change to what agents can see.\n */\nexport async function runCheck(options: CheckOptions): Promise<number> {\n const runner = await createSurfaceRunner(options.configPath);\n try {\n const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;\n const dir = baselineDirFor(options.configPath, options.baselineDir ?? runner.config.baselineDir);\n const drifted: ScenarioDrift[] = [];\n\n for (const scenario of scenarios) {\n const result = await runner.collect({\n scenario,\n ...(options.scope ? { scope: options.scope } : {}),\n });\n const path = baselinePath(dir, scenario);\n const expected = readBaseline(path);\n\n if (expected === undefined) {\n drifted.push({ scenario, missingBaseline: true, entries: [] });\n continue;\n }\n const actual = normalize(result.snapshot);\n const entries = annotate(diff(expected, actual), actual, expected);\n if (entries.length > 0) drifted.push({ scenario, entries });\n }\n\n if (options.json) {\n write(JSON.stringify({ ok: drifted.length === 0, drifted }, null, 2));\n return drifted.length === 0 ? 0 : 1;\n }\n\n if (drifted.length === 0) {\n // Naming them is the point (`AS-CLI-007`). A green `check` means \"the\n // surface did not change *in these scenarios*\", and a count alone lets it\n // read as a statement about the surface as a whole — which it never was.\n write(`surface matches the baseline in ${scenarios.join(\", \")}`);\n write(\n \"that is a statement about these scenarios only; capabilities no scenario mounts are `agent-surface coverage`'s question\",\n );\n return 0;\n }\n\n const ink = isPlain(options) ? null : await loadInk();\n for (const entry of drifted) {\n if (entry.missingBaseline) {\n writeError(\n `${entry.scenario}: no baseline at ${relative(\n process.cwd(),\n baselinePath(dir, entry.scenario),\n )} — run \\`agent-surface snapshot\\` and commit it`,\n );\n continue;\n }\n if (ink) await paint(<ink.Drift scenario={entry.scenario} entries={entry.entries} />);\n else write(renderDiffPlain(entry.scenario, entry.entries));\n }\n writeError(\n `\\nsurface drift in ${drifted.length} scenario${drifted.length === 1 ? \"\" : \"s\"} — review the change, then \\`agent-surface snapshot\\` to accept it`,\n );\n return 1;\n } finally {\n await runner.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AAqFE;AAnD3B,eAAsB,SAAS,SAAwC;AACrE,QAAM,SAAS,MAAM,oBAAoB,QAAQ,UAAU;AAC3D,MAAI;AACF,UAAM,YAAY,QAAQ,WAAW,CAAC,QAAQ,QAAQ,IAAI,OAAO;AACjE,UAAM,MAAM,eAAe,QAAQ,YAAY,QAAQ,eAAe,OAAO,OAAO,WAAW;AAC/F,UAAM,UAA2B,CAAC;AAElC,eAAW,YAAY,WAAW;AAChC,YAAM,SAAS,MAAM,OAAO,QAAQ;AAAA,QAClC;AAAA,QACA,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,MAClD,CAAC;AACD,YAAM,OAAO,aAAa,KAAK,QAAQ;AACvC,YAAM,WAAW,aAAa,IAAI;AAElC,UAAI,aAAa,QAAW;AAC1B,gBAAQ,KAAK,EAAE,UAAU,iBAAiB,MAAM,SAAS,CAAC,EAAE,CAAC;AAC7D;AAAA,MACF;AACA,YAAM,SAAS,UAAU,OAAO,QAAQ;AACxC,YAAM,UAAU,SAAS,KAAK,UAAU,MAAM,GAAG,QAAQ,QAAQ;AACjE,UAAI,QAAQ,SAAS,EAAG,SAAQ,KAAK,EAAE,UAAU,QAAQ,CAAC;AAAA,IAC5D;AAEA,QAAI,QAAQ,MAAM;AAChB,YAAM,KAAK,UAAU,EAAE,IAAI,QAAQ,WAAW,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC;AACpE,aAAO,QAAQ,WAAW,IAAI,IAAI;AAAA,IACpC;AAEA,QAAI,QAAQ,WAAW,GAAG;AAIxB,YAAM,mCAAmC,UAAU,KAAK,IAAI,CAAC,EAAE;AAC/D;AAAA,QACE;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,UAAM,MAAM,QAAQ,OAAO,IAAI,OAAO,MAAM,QAAQ;AACpD,eAAW,SAAS,SAAS;AAC3B,UAAI,MAAM,iBAAiB;AACzB;AAAA,UACE,GAAG,MAAM,QAAQ,oBAAoB;AAAA,YACnC,QAAQ,IAAI;AAAA,YACZ,aAAa,KAAK,MAAM,QAAQ;AAAA,UAClC,CAAC;AAAA,QACH;AACA;AAAA,MACF;AACA,UAAI,IAAK,OAAM,MAAM,oBAAC,IAAI,OAAJ,EAAU,UAAU,MAAM,UAAU,SAAS,MAAM,SAAS,CAAE;AAAA,UAC/E,OAAM,gBAAgB,MAAM,UAAU,MAAM,OAAO,CAAC;AAAA,IAC3D;AACA;AAAA,MACE;AAAA,mBAAsB,QAAQ,MAAM,YAAY,QAAQ,WAAW,IAAI,KAAK,GAAG;AAAA,IACjF;AACA,WAAO;AAAA,EACT,UAAE;AACA,UAAM,OAAO,MAAM;AAAA,EACrB;AACF;","names":[]}