@agent-surface/cli 0.13.0 → 0.15.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.
Files changed (38) hide show
  1. package/dist/bin.js +41 -14
  2. package/dist/bin.js.map +1 -1
  3. package/dist/{check-TJIX7NDE.js → check-VWRYNYLN.js} +66 -48
  4. package/dist/check-VWRYNYLN.js.map +1 -0
  5. package/dist/chunk-A5UCBF7D.js +246 -0
  6. package/dist/chunk-A5UCBF7D.js.map +1 -0
  7. package/dist/chunk-GYYWHZPM.js +532 -0
  8. package/dist/chunk-GYYWHZPM.js.map +1 -0
  9. package/dist/chunk-NFK3XWWH.js +1475 -0
  10. package/dist/chunk-NFK3XWWH.js.map +1 -0
  11. package/dist/chunk-Y2LSPEVK.js +61 -0
  12. package/dist/chunk-Y2LSPEVK.js.map +1 -0
  13. package/dist/index.d.ts +3 -2
  14. package/dist/{init-6XV64LK2.js → init-BVZR6CRS.js} +62 -40
  15. package/dist/init-BVZR6CRS.js.map +1 -0
  16. package/dist/{ink-QR7X7TAC.js → ink-ZCQ26EY4.js} +71 -17
  17. package/dist/ink-ZCQ26EY4.js.map +1 -0
  18. package/dist/{inspect-NJ4J3MPP.js → inspect-3YFPCYQJ.js} +88 -104
  19. package/dist/inspect-3YFPCYQJ.js.map +1 -0
  20. package/dist/snapshot-5QKLZKZI.js +130 -0
  21. package/dist/snapshot-5QKLZKZI.js.map +1 -0
  22. package/package.json +4 -4
  23. package/dist/check-TJIX7NDE.js.map +0 -1
  24. package/dist/chunk-3AJ343NA.js +0 -178
  25. package/dist/chunk-3AJ343NA.js.map +0 -1
  26. package/dist/chunk-IALBMW3R.js +0 -278
  27. package/dist/chunk-IALBMW3R.js.map +0 -1
  28. package/dist/chunk-L7GHSC2Z.js +0 -465
  29. package/dist/chunk-L7GHSC2Z.js.map +0 -1
  30. package/dist/chunk-UGCLJ5JX.js +0 -724
  31. package/dist/chunk-UGCLJ5JX.js.map +0 -1
  32. package/dist/chunk-VX6GBEP3.js +0 -539
  33. package/dist/chunk-VX6GBEP3.js.map +0 -1
  34. package/dist/init-6XV64LK2.js.map +0 -1
  35. package/dist/ink-QR7X7TAC.js.map +0 -1
  36. package/dist/inspect-NJ4J3MPP.js.map +0 -1
  37. package/dist/snapshot-ZGTAF2Y5.js +0 -77
  38. package/dist/snapshot-ZGTAF2Y5.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/init.tsx"],"sourcesContent":["import { existsSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { UsageError } from \"../analysis.js\";\nimport { authoredIds, extractCapabilities, findTsconfig } from \"../extract.js\";\nimport { isPlain, loadInk, writeError } from \"../output.js\";\nimport { createPresenter } from \"../render/present.js\";\nimport {\n catalogDetailParts,\n catalogRows,\n displayPath,\n READING_SOURCE,\n type ReportPart,\n} from \"../render/summary.js\";\n\nexport interface InitOptions {\n cwd: string;\n tsconfig?: string;\n yes?: boolean;\n plain?: boolean;\n}\n\nconst CONFIG_NAME = \"agent-surface.config.tsx\";\n\n/**\n * Where an app is usually assembled. `init` does not *probe* these — it cannot,\n * because a surface config needs a `mount()` that builds the app, and there is\n * no export a tool can import to get one. It names the likeliest file so the\n * scaffold's import line points somewhere real more often than not.\n */\nconst ENTRY_CANDIDATES = [\n \"src/main.tsx\",\n \"src/main.ts\",\n \"src/index.tsx\",\n \"src/App.tsx\",\n \"src/app/App.tsx\",\n \"app/root.tsx\",\n];\n\nfunction scaffold(entry: string | undefined): string {\n const importPath = entry ? `./${entry.replace(/\\.tsx?$/, \".js\")}` : \"./src/App.js\";\n return `import { defineSurface } from \"@agent-surface/cli\";\n// TODO: point these at your own composition root — whatever \\`main.tsx\\` calls.\n// The config should *reuse* how the app builds itself, not restate it.\nimport { App } from \"${importPath}\";\n\nexport default defineSurface({\n mount: ({ user }) => {\n // TODO: build the app the way the app builds itself, and hand back the\n // registry it created plus the tree that registers into it.\n const app = createApp({ environment: \"test\", user });\n return { registry: app.registry, ui: <App app={app} />, app };\n },\n\n // Named prop bundles. Free-form — a user, a route, a feature flag; the CLI\n // never interprets them. Every scenario you leave out is a surface nothing\n // measures, which is what \\`--depth full\\` reports as unreached.\n scenarios: {\n default: { user: { id: \"u_1\", permissions: [] } },\n },\n});\n`;\n}\n\n/**\n * `agent-surface init` — the on-ramp.\n *\n * It reads the codebase first and writes nothing before it has shown you what\n * it found. That order is the whole point: the number it prints is the one\n * every later command is relative to, and a scaffold that appears before the\n * summary asks you to accept a config for a codebase neither of you has looked\n * at yet.\n *\n * It mounts nothing and needs no config to exist — it is `--depth static` with\n * a file write on the end, and it says so in the same blocks `inspect --depth\n * static` uses, so the first report a reader ever sees is the one they will go\n * on seeing.\n */\nexport async function runInit(options: InitOptions): Promise<number> {\n const configPath = join(options.cwd, CONFIG_NAME);\n if (existsSync(configPath)) {\n throw new UsageError(\n `${displayPath(configPath)} already exists — edit it, or delete it and re-run`,\n );\n }\n\n const tsconfig = options.tsconfig ?? findTsconfig(options.cwd);\n if (!tsconfig) {\n throw new UsageError(\n `no tsconfig.json found from ${options.cwd} — agent-surface reads your TypeScript program ` +\n \"to find registration call sites, and cannot do that without one\",\n );\n }\n\n const present = await createPresenter(options);\n await present.wait(READING_SOURCE);\n const inventory = extractCapabilities({ root: options.cwd, tsconfig });\n const ids = authoredIds(inventory);\n const entry = ENTRY_CANDIDATES.find((candidate) => existsSync(join(options.cwd, candidate)));\n\n const parts: ReportPart[] = [\n {\n kind: \"blocks\",\n blocks: [\n {\n title: \"SURFACE INIT\",\n rows: [\n { label: \"Tsconfig\", text: displayPath(tsconfig) },\n { label: \"Config\", text: `${displayPath(configPath)} — to be written` },\n ],\n },\n { title: \"STATIC CATALOG\", rows: catalogRows(inventory) },\n ],\n },\n ...(ids.size > 0 ? catalogDetailParts(inventory) : []),\n ];\n\n if (ids.size === 0) {\n parts.push({\n kind: \"note\",\n lines: [\n \"Nothing is annotated yet — that is the default, and it is the safe one: a capability\",\n \"exists only where someone wrote one. Start with `useAgentComponent` in a component\",\n \"that owns state worth acting on, then re-run this.\",\n ],\n });\n }\n\n parts.push({\n kind: \"note\",\n title: \"SCAFFOLD\",\n lines: [\n ` ${displayPath(configPath)}`,\n entry\n ? ` imports ./${entry}, which you will still have to wire into a mount()`\n : \" no app entry found, so the import line is a placeholder you will have to point somewhere\",\n ],\n });\n await present.emit(...parts);\n\n if (!options.yes) {\n const answered = await ask(options, `Write ${CONFIG_NAME}?`);\n if (!answered) {\n await present.emit({ kind: \"note\", lines: [\"Nothing written.\"] });\n return 0;\n }\n }\n\n writeFileSync(configPath, scaffold(entry), \"utf8\");\n await present.emit(\n { kind: \"note\", lines: [`wrote ${displayPath(configPath)}`] },\n {\n kind: \"steps\",\n title: \"NEXT STEPS\",\n steps: [\n \"fill in mount() — it should call your existing composition root\",\n \"`agent-surface inspect` to see what an agent can reach\",\n \"`agent-surface snapshot` to commit the baseline, then `check` in CI\",\n ],\n },\n );\n return 0;\n}\n\n/**\n * There is no prompt to give when nothing is attached to answer it. Failing\n * with the flag that would have worked beats writing a file the caller never\n * agreed to, and beats hanging on a read that will never return.\n */\nasync function ask(options: InitOptions, question: string): Promise<boolean> {\n if (isPlain(options) || process.stdin.isTTY !== true) {\n writeError(\"\");\n writeError(\"stdin is not a terminal, so there is nobody to ask — re-run with --yes to accept.\");\n return false;\n }\n const ink = await loadInk();\n if (!ink) {\n writeError(\"\");\n writeError(\"no interactive renderer available here — re-run with --yes to accept.\");\n return false;\n }\n const { render } = await import(\"ink\");\n return new Promise<boolean>((resolve) => {\n const instance = render(\n <ink.Confirm\n question={question}\n onAnswer={(yes) => {\n instance.clear();\n instance.unmount();\n resolve(yes);\n }}\n />,\n );\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY,qBAAqB;AAC1C,SAAS,YAAY;AAsLf;AAlKN,IAAM,cAAc;AAQpB,IAAM,mBAAmB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,SAAS,OAAmC;AACnD,QAAM,aAAa,QAAQ,KAAK,MAAM,QAAQ,WAAW,KAAK,CAAC,KAAK;AACpE,SAAO;AAAA;AAAA;AAAA,uBAGc,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkBjC;AAgBA,eAAsB,QAAQ,SAAuC;AACnE,QAAM,aAAa,KAAK,QAAQ,KAAK,WAAW;AAChD,MAAI,WAAW,UAAU,GAAG;AAC1B,UAAM,IAAI;AAAA,MACR,GAAG,YAAY,UAAU,CAAC;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,WAAW,QAAQ,YAAY,aAAa,QAAQ,GAAG;AAC7D,MAAI,CAAC,UAAU;AACb,UAAM,IAAI;AAAA,MACR,+BAA+B,QAAQ,GAAG;AAAA,IAE5C;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,gBAAgB,OAAO;AAC7C,QAAM,QAAQ,KAAK,cAAc;AACjC,QAAM,YAAY,oBAAoB,EAAE,MAAM,QAAQ,KAAK,SAAS,CAAC;AACrE,QAAM,MAAM,YAAY,SAAS;AACjC,QAAM,QAAQ,iBAAiB,KAAK,CAAC,cAAc,WAAW,KAAK,QAAQ,KAAK,SAAS,CAAC,CAAC;AAE3F,QAAM,QAAsB;AAAA,IAC1B;AAAA,MACE,MAAM;AAAA,MACN,QAAQ;AAAA,QACN;AAAA,UACE,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,EAAE,OAAO,YAAY,MAAM,YAAY,QAAQ,EAAE;AAAA,YACjD,EAAE,OAAO,UAAU,MAAM,GAAG,YAAY,UAAU,CAAC,wBAAmB;AAAA,UACxE;AAAA,QACF;AAAA,QACA,EAAE,OAAO,kBAAkB,MAAM,YAAY,SAAS,EAAE;AAAA,MAC1D;AAAA,IACF;AAAA,IACA,GAAI,IAAI,OAAO,IAAI,mBAAmB,SAAS,IAAI,CAAC;AAAA,EACtD;AAEA,MAAI,IAAI,SAAS,GAAG;AAClB,UAAM,KAAK;AAAA,MACT,MAAM;AAAA,MACN,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,KAAK;AAAA,IACT,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO;AAAA,MACL,KAAK,YAAY,UAAU,CAAC;AAAA,MAC5B,QACI,eAAe,KAAK,uDACpB;AAAA,IACN;AAAA,EACF,CAAC;AACD,QAAM,QAAQ,KAAK,GAAG,KAAK;AAE3B,MAAI,CAAC,QAAQ,KAAK;AAChB,UAAM,WAAW,MAAM,IAAI,SAAS,SAAS,WAAW,GAAG;AAC3D,QAAI,CAAC,UAAU;AACb,YAAM,QAAQ,KAAK,EAAE,MAAM,QAAQ,OAAO,CAAC,kBAAkB,EAAE,CAAC;AAChE,aAAO;AAAA,IACT;AAAA,EACF;AAEA,gBAAc,YAAY,SAAS,KAAK,GAAG,MAAM;AACjD,QAAM,QAAQ;AAAA,IACZ,EAAE,MAAM,QAAQ,OAAO,CAAC,SAAS,YAAY,UAAU,CAAC,EAAE,EAAE;AAAA,IAC5D;AAAA,MACE,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOA,eAAe,IAAI,SAAsB,UAAoC;AAC3E,MAAI,QAAQ,OAAO,KAAK,QAAQ,MAAM,UAAU,MAAM;AACpD,eAAW,EAAE;AACb,eAAW,wFAAmF;AAC9F,WAAO;AAAA,EACT;AACA,QAAM,MAAM,MAAM,QAAQ;AAC1B,MAAI,CAAC,KAAK;AACR,eAAW,EAAE;AACb,eAAW,4EAAuE;AAClF,WAAO;AAAA,EACT;AACA,QAAM,EAAE,OAAO,IAAI,MAAM,OAAO,KAAK;AACrC,SAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,UAAM,WAAW;AAAA,MACf;AAAA,QAAC,IAAI;AAAA,QAAJ;AAAA,UACC;AAAA,UACA,UAAU,CAAC,QAAQ;AACjB,qBAAS,MAAM;AACf,qBAAS,QAAQ;AACjB,oBAAQ,GAAG;AAAA,UACb;AAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -1,8 +1,9 @@
1
1
  import {
2
+ STATUS_WIDTH,
2
3
  flatRows,
4
+ reportGrid,
3
5
  riskClause
4
- } from "./chunk-VX6GBEP3.js";
5
- import "./chunk-UGCLJ5JX.js";
6
+ } from "./chunk-NFK3XWWH.js";
6
7
 
7
8
  // src/render/ink.tsx
8
9
  import { Box, Static, Text, useInput } from "ink";
@@ -21,8 +22,6 @@ var STATUS_COLOR = {
21
22
  };
22
23
  var TONE_COLOR = { good: "green", warn: "yellow", bad: "red" };
23
24
  var NONE = "\u2014";
24
- var LABEL_WIDTH = 12;
25
- var STATUS_WIDTH = 7;
26
25
  function widthsFor(headers, rows) {
27
26
  return headers.map(
28
27
  (header, column) => Math.max(header.length, ...rows.map((row) => (row[column] ?? "").length))
@@ -59,27 +58,24 @@ function Row({ row, width, statuses }) {
59
58
  /* @__PURE__ */ jsx(Text, { color: row.tone ? TONE_COLOR[row.tone] : void 0, wrap: "wrap", children: row.text })
60
59
  ] });
61
60
  }
62
- function Report({ blocks }) {
63
- const width = Math.max(
64
- LABEL_WIDTH,
65
- ...blocks.flatMap((block) => block.rows.map((row) => row.label.length + 2))
66
- );
67
- const statuses = blocks.some((block) => block.rows.some((row) => row.status));
61
+ function Report({
62
+ blocks,
63
+ labelWidth
64
+ }) {
65
+ const grid = reportGrid(blocks, labelWidth);
66
+ const drawn = blocks.filter((block) => block.title || block.rows.length > 0);
68
67
  return /* @__PURE__ */ jsx(
69
68
  Static,
70
69
  {
71
- items: blocks.map((block, index) => ({ key: block.title ?? `block-${index}`, block, index })),
70
+ items: drawn.map((block, index) => ({ key: block.title ?? `block-${index}`, block, index })),
72
71
  children: ({ key, block, index }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [
73
72
  block.title ? /* @__PURE__ */ jsx(Text, { bold: true, children: block.title }) : null,
74
- block.rows.map((row) => /* @__PURE__ */ jsx(Row, { row, width, statuses }, row.label))
73
+ block.rows.map((row) => /* @__PURE__ */ jsx(Row, { row, width: grid.label, statuses: grid.statuses }, row.label))
75
74
  ] }, key)
76
75
  }
77
76
  );
78
77
  }
79
- function Grid({
80
- headers,
81
- rows
82
- }) {
78
+ function Grid({ headers, rows }) {
83
79
  const widths = widthsFor(
84
80
  headers,
85
81
  rows.map((row) => row.cells)
@@ -94,14 +90,35 @@ function Grid({
94
90
  }
95
91
  function Table({
96
92
  title,
93
+ lead,
97
94
  headers,
98
95
  rows
99
96
  }) {
100
97
  return /* @__PURE__ */ jsx(Static, { items: [{ key: title }], children: (block) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
101
98
  /* @__PURE__ */ jsx(Text, { bold: true, children: title }),
99
+ lead ? /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: lead }) : null,
102
100
  /* @__PURE__ */ jsx(Grid, { headers, rows })
103
101
  ] }, block.key) });
104
102
  }
103
+ function Note({
104
+ title,
105
+ lines,
106
+ muted
107
+ }) {
108
+ return /* @__PURE__ */ jsx(Static, { items: [{ key: title ?? lines[0] ?? "note" }], children: (block) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
109
+ title ? /* @__PURE__ */ jsx(Text, { bold: true, children: title }) : null,
110
+ lines.map((line, index) => /* @__PURE__ */ jsx(Text, { dimColor: muted === true, children: line }, `${index}`))
111
+ ] }, block.key) });
112
+ }
113
+ function Steps({ title, steps }) {
114
+ return /* @__PURE__ */ jsx(Static, { items: [{ key: title }], children: (block) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
115
+ /* @__PURE__ */ jsx(Text, { bold: true, children: title }),
116
+ steps.map((step, index) => /* @__PURE__ */ jsxs(Box, { paddingLeft: 2, children: [
117
+ /* @__PURE__ */ jsx(Text, { color: "cyan", children: `${index + 1}. ` }),
118
+ /* @__PURE__ */ jsx(Text, { wrap: "wrap", children: step })
119
+ ] }, `${index}`))
120
+ ] }, block.key) });
121
+ }
105
122
  function Findings({ sections }) {
106
123
  return /* @__PURE__ */ jsx(
107
124
  Static,
@@ -264,6 +281,40 @@ function CapabilityTable({ rows }) {
264
281
  ))
265
282
  ] });
266
283
  }
284
+ function Part({
285
+ part,
286
+ labelWidth
287
+ }) {
288
+ switch (part.kind) {
289
+ case "blocks":
290
+ return /* @__PURE__ */ jsx(Report, { blocks: part.blocks, ...labelWidth ? { labelWidth } : {} });
291
+ case "table":
292
+ return /* @__PURE__ */ jsx(
293
+ Table,
294
+ {
295
+ title: part.title,
296
+ ...part.lead ? { lead: part.lead } : {},
297
+ headers: part.headers,
298
+ rows: part.rows
299
+ }
300
+ );
301
+ case "findings":
302
+ return /* @__PURE__ */ jsx(Findings, { sections: part.sections });
303
+ case "surface":
304
+ return /* @__PURE__ */ jsx(Surface, { view: part.view, ...part.detail ? { detail: true } : {} });
305
+ case "note":
306
+ return /* @__PURE__ */ jsx(
307
+ Note,
308
+ {
309
+ ...part.title ? { title: part.title } : {},
310
+ ...part.muted ? { muted: true } : {},
311
+ lines: part.lines
312
+ }
313
+ );
314
+ case "steps":
315
+ return /* @__PURE__ */ jsx(Steps, { title: part.title, steps: part.steps });
316
+ }
317
+ }
267
318
  function Surface({
268
319
  view,
269
320
  detail
@@ -284,8 +335,11 @@ export {
284
335
  Confirm,
285
336
  Findings,
286
337
  Loading,
338
+ Note,
339
+ Part,
287
340
  Report,
341
+ Steps,
288
342
  Surface,
289
343
  Table
290
344
  };
291
- //# sourceMappingURL=ink-QR7X7TAC.js.map
345
+ //# sourceMappingURL=ink-ZCQ26EY4.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 {\n reportGrid,\n riskClause,\n STATUS_WIDTH,\n type FindingSection,\n type ReportBlock,\n type ReportPart,\n type ReportRow,\n type TableRow,\n} from \"./summary.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 STATUS_COLOR = {\n PASS: \"green\",\n WARN: \"yellow\",\n FAIL: \"red\",\n ERROR: \"red\",\n} as const;\n\nconst TONE_COLOR = { good: \"green\", warn: \"yellow\", bad: \"red\" } as const;\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 <Text dimColor>{` ${label}…`}</Text>\n </Text>\n );\n}\n\n/** One `label STATUS text` row, coloured by whichever of the two it carries. */\nfunction Row({ row, width, statuses }: { row: ReportRow; width: number; statuses: boolean }): ReactElement {\n return (\n <Box>\n <Text dimColor>{pad(row.label, width)}</Text>\n {statuses ? (\n <Text bold color={row.status ? STATUS_COLOR[row.status] : undefined}>\n {pad(row.status ?? \"\", STATUS_WIDTH)}\n </Text>\n ) : null}\n <Text color={row.tone ? TONE_COLOR[row.tone] : undefined} wrap=\"wrap\">\n {row.text}\n </Text>\n </Box>\n );\n}\n\n/**\n * The labelled blocks a report is built from — the run header, the catalog\n * summary, the closing verdict. Same rows the plain renderer prints, same\n * widths from the same `reportGrid`, with the status word and the tone carrying\n * colour on a terminal.\n */\nexport function Report({\n blocks,\n labelWidth,\n}: {\n blocks: ReportBlock[];\n labelWidth?: number;\n}): ReactElement {\n const grid = reportGrid(blocks, labelWidth);\n // An empty, untitled block is dropped rather than painted, exactly as the\n // plain renderer drops it — otherwise the same report carries a blank line in\n // the terminal that a CI log does not have.\n const drawn = blocks.filter((block) => block.title || block.rows.length > 0);\n // Ink prints a newline of its own after each painted frame, so only the\n // blocks *within* one frame ask for the blank line above them. A margin on\n // the first would double it, which reads as a missing block rather than as\n // breathing room.\n return (\n <Static\n items={drawn.map((block, index) => ({ key: block.title ?? `block-${index}`, block, index }))}\n >\n {({ key, block, index }) => (\n <Box key={key} flexDirection=\"column\" marginTop={index === 0 ? 0 : 1}>\n {block.title ? <Text bold>{block.title}</Text> : null}\n {block.rows.map((row) => (\n <Row key={row.label} row={row} width={grid.label} statuses={grid.statuses} />\n ))}\n </Box>\n )}\n </Static>\n );\n}\n\nfunction Grid({ headers, rows }: { headers: string[]; rows: TableRow[] }): ReactElement {\n const widths = widthsFor(\n headers,\n rows.map((row) => row.cells),\n );\n return (\n <Box flexDirection=\"column\">\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 <Box key={`${row.cells[0]}-${index}`} flexDirection=\"column\">\n <Box>\n {row.cells.map((cell, column) => (\n <Text key={`${column}`} bold={column === 0}>\n {column === headers.length - 1 ? cell : `${pad(cell, widths[column]!)} `}\n </Text>\n ))}\n </Box>\n {row.note ? (\n <Box paddingLeft={4}>\n <Text dimColor wrap=\"wrap\">{`⤷ ${row.note}`}</Text>\n </Box>\n ) : null}\n </Box>\n ))}\n </Box>\n );\n}\n\nexport function Table({\n title,\n lead,\n headers,\n rows,\n}: {\n title: string;\n lead?: string;\n headers: string[];\n rows: TableRow[];\n}): ReactElement {\n return (\n <Static items={[{ key: title }]}>\n {(block) => (\n <Box key={block.key} flexDirection=\"column\">\n <Text bold>{title}</Text>\n {lead ? (\n <Text dimColor wrap=\"wrap\">\n {lead}\n </Text>\n ) : null}\n <Grid headers={headers} rows={rows} />\n </Box>\n )}\n </Static>\n );\n}\n\n/**\n * Lines that are neither a grid nor a finding: a closing hint, a list of keys\n * to copy. Never wrapped, and dimmed only where the part says so — an allowlist\n * key is read by selecting it, and a reflowed or greyed-out one is a key nobody\n * pastes.\n */\nexport function Note({\n title,\n lines,\n muted,\n}: {\n title?: string;\n lines: string[];\n muted?: boolean;\n}): ReactElement {\n return (\n <Static items={[{ key: title ?? lines[0] ?? \"note\" }]}>\n {(block) => (\n <Box key={block.key} flexDirection=\"column\">\n {title ? <Text bold>{title}</Text> : null}\n {lines.map((line, index) => (\n <Text key={`${index}`} dimColor={muted === true}>\n {line}\n </Text>\n ))}\n </Box>\n )}\n </Static>\n );\n}\n\n/** The commands that clear a report, in the order worth running them. */\nexport function Steps({ title, steps }: { title: string; steps: string[] }): ReactElement {\n return (\n <Static items={[{ key: title }]}>\n {(block) => (\n <Box key={block.key} flexDirection=\"column\">\n <Text bold>{title}</Text>\n {steps.map((step, index) => (\n <Box key={`${index}`} paddingLeft={2}>\n <Text color=\"cyan\">{`${index + 1}. `}</Text>\n <Text wrap=\"wrap\">{step}</Text>\n </Box>\n ))}\n </Box>\n )}\n </Static>\n );\n}\n\n/**\n * Findings. The heading says what it is, the gloss why it matters, and the hint\n * what to do — printed with the finding rather than left to be inferred.\n */\nexport function Findings({ sections }: { sections: FindingSection[] }): ReactElement {\n return (\n <Static\n items={sections.map((section, index) => ({ key: `${section.title}-${index}`, section, index }))}\n >\n {({ key, section, index }) => (\n <Box key={key} flexDirection=\"column\" marginTop={index === 0 ? 0 : 1}>\n <Box>\n <Text\n backgroundColor={section.tone === \"notice\" ? \"yellow\" : \"red\"}\n color=\"black\"\n bold\n >{` ${section.title} `}</Text>\n <Text dimColor>{` ${section.gloss}${section.count > 0 ? ` ${section.count}` : \"\"}`}</Text>\n </Box>\n {section.headers && section.rows ? (\n <Grid headers={section.headers} rows={section.rows} />\n ) : null}\n {(section.lines ?? []).map((line, index) => (\n <Box key={`${index}`} paddingLeft={2}>\n <Text>{line}</Text>\n </Box>\n ))}\n {section.hint ? (\n <Box paddingLeft={2}>\n <Text color=\"cyan\" wrap=\"wrap\">{`→ ${section.hint}`}</Text>\n </Box>\n ) : null}\n </Box>\n )}\n </Static>\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 // What the surface can do, not just how much of it there is: \"one of these\n // deletes a device\" is the part a reader needs before they read anything else.\n const risk = riskClause(flatRows(view));\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 {risk ? (\n <>\n <Text dimColor>{\" · \"}</Text>\n <Text color=\"magenta\">{risk}</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\n/**\n * One part of a report, drawn.\n *\n * This switch is the only place the terminal UI learns what a report can\n * contain, and it is the same list `renderPartPlain` switches on — so a part\n * that renders here renders there, and neither can grow a shape the other has\n * never heard of.\n */\nexport function Part({\n part,\n labelWidth,\n}: {\n part: ReportPart;\n labelWidth?: number;\n}): ReactElement {\n switch (part.kind) {\n case \"blocks\":\n return <Report blocks={part.blocks} {...(labelWidth ? { labelWidth } : {})} />;\n case \"table\":\n return (\n <Table\n title={part.title}\n {...(part.lead ? { lead: part.lead } : {})}\n headers={part.headers}\n rows={part.rows}\n />\n );\n case \"findings\":\n return <Findings sections={part.sections} />;\n case \"surface\":\n return <Surface view={part.view} {...(part.detail ? { detail: true } : {})} />;\n case \"note\":\n return (\n <Note\n {...(part.title ? { title: part.title } : {})}\n {...(part.muted ? { muted: true } : {})}\n lines={part.lines}\n />\n );\n case \"steps\":\n return <Steps title={part.title} steps={part.steps} />;\n }\n}\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"],"mappings":"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","names":["index"]}
@@ -1,48 +1,40 @@
1
1
  import {
2
2
  coverageReport,
3
3
  inventoryReport,
4
- renderCatalogDetailPlain,
5
- renderFailuresPlain,
6
- renderNoVerdictPlain,
7
- renderReportPlain,
8
- renderSectionsPlain,
9
- renderSurfacePlain,
10
- renderTablePlain,
11
4
  scenarioReport
12
- } from "./chunk-L7GHSC2Z.js";
5
+ } from "./chunk-Y2LSPEVK.js";
13
6
  import {
7
+ createPresenter,
8
+ joinCoverage,
9
+ mountScenarios,
10
+ readInventory,
11
+ scopeCapabilityIds,
12
+ scopeInventory,
13
+ staticConfigScope
14
+ } from "./chunk-A5UCBF7D.js";
15
+ import {
16
+ write
17
+ } from "./chunk-GYYWHZPM.js";
18
+ import {
19
+ READING_SOURCE,
14
20
  buildView,
21
+ catalogDetailParts,
15
22
  catalogRows,
16
23
  coverageSections,
24
+ failureSection,
17
25
  flatRows,
26
+ mountingLabel,
18
27
  neverCallable,
19
28
  neverCallableSection,
29
+ noVerdictSection,
20
30
  runHeaderBlocks,
21
31
  scenarioStats,
22
32
  scenarioTable,
23
33
  surfaceSummaryRows,
24
34
  trackReach
25
- } from "./chunk-VX6GBEP3.js";
26
- import {
27
- joinCoverage,
28
- mountScenarios,
29
- readInventory,
30
- scopeCapabilityIds,
31
- scopeInventory,
32
- staticConfigScope
33
- } from "./chunk-IALBMW3R.js";
34
- import {
35
- isPlain,
36
- loadInk,
37
- paint,
38
- transient,
39
- write,
40
- writeError
41
- } from "./chunk-3AJ343NA.js";
42
- import "./chunk-UGCLJ5JX.js";
35
+ } from "./chunk-NFK3XWWH.js";
43
36
 
44
- // src/commands/inspect.tsx
45
- import { jsx } from "react/jsx-runtime";
37
+ // src/commands/inspect.ts
46
38
  async function runInspect(options) {
47
39
  const analysis = {
48
40
  configPath: options.configPath,
@@ -53,21 +45,8 @@ async function runInspect(options) {
53
45
  ...options.baselineDir ? { baselineDir: options.baselineDir } : {}
54
46
  };
55
47
  const detail = options.detail === true || options.explain === true || options.schemas === true;
56
- const ink = isPlain(options) ? null : await loadInk();
57
- const say = async (blocks, lead = true) => {
58
- if (ink) await paint(/* @__PURE__ */ jsx(ink.Report, { blocks }));
59
- else write(`${lead ? "\n" : ""}${renderReportPlain(blocks)}`);
60
- };
61
- let stopWaiting = ink ? await transient(/* @__PURE__ */ jsx(ink.Loading, { label: "reading the source" })) : void 0;
62
- const settle = () => {
63
- stopWaiting?.();
64
- stopWaiting = void 0;
65
- };
66
- const waitFor = async (label) => {
67
- settle();
68
- if (ink) stopWaiting = await transient(/* @__PURE__ */ jsx(ink.Loading, { label }));
69
- };
70
- const mounting = (list, index) => `mounting ${list[index]}${list.length > 1 ? ` (${index + 1} of ${list.length})` : ""}`;
48
+ const present = await createPresenter(options);
49
+ if (!options.json) await present.wait(READING_SOURCE);
71
50
  const inventory = readInventory(analysis);
72
51
  const scenarios = [];
73
52
  const stats = [];
@@ -78,39 +57,36 @@ async function runInspect(options) {
78
57
  const scopedInventory = () => scopeInventory(inventory, scope());
79
58
  const domainCount = () => plan?.domainManifestConfigured ? scopeCapabilityIds(plan.domainCapabilities, scope()).length : void 0;
80
59
  const header = async () => {
81
- settle();
60
+ present.settle();
82
61
  if (options.json) return;
83
62
  const active = scope();
84
- await say(
63
+ const catalog2 = scopedInventory();
64
+ await present.emit(
85
65
  // At `--depth static` the catalog *is* the answer, so it opens the report
86
66
  // beside the run it came from. At every other depth it is a detail behind
87
67
  // the summary, and prints down there instead.
88
- runHeaderBlocks(
89
- "SURFACE INSPECT",
90
- {
91
- configPath: options.configPath,
92
- depth: options.depth,
93
- ...active ? { scope: active } : {},
94
- ...plan ? { scenarios: plan.scenarios, declaredScenarios: plan.declaredScenarios } : {}
95
- },
96
- plan ? void 0 : scopedInventory(),
97
- domainCount()
98
- ),
99
- false
68
+ {
69
+ kind: "blocks",
70
+ blocks: runHeaderBlocks(
71
+ "SURFACE INSPECT",
72
+ {
73
+ configPath: options.configPath,
74
+ depth: options.depth,
75
+ ...active ? { scope: active } : {},
76
+ ...plan ? { scenarios: plan.scenarios, declaredScenarios: plan.declaredScenarios } : {}
77
+ },
78
+ plan ? void 0 : catalog2,
79
+ domainCount()
80
+ )
81
+ },
82
+ ...!plan && catalog2 ? catalogDetailParts(catalog2, { ...detail ? { detail: true } : {} }) : []
100
83
  );
101
- if (!plan && inventory) {
102
- const catalog2 = renderCatalogDetailPlain(scopedInventory() ?? inventory, {
103
- ...detail ? { detail: true } : {}
104
- });
105
- if (catalog2) write(`
106
- ${catalog2}`);
107
- }
108
84
  };
109
85
  const runtime = await mountScenarios(analysis, {
110
86
  onPlan: async (current) => {
111
87
  plan = current;
112
88
  await header();
113
- if (current.scenarios.length > 0) await waitFor(mounting(current.scenarios, 0));
89
+ if (current.scenarios.length > 0) await present.wait(mountingLabel(current.scenarios, 0));
114
90
  },
115
91
  onEach: async (result) => {
116
92
  const view = buildView(result, {
@@ -130,11 +106,11 @@ ${catalog2}`);
130
106
  views.push(view);
131
107
  }
132
108
  if (plan && stats.length < plan.scenarios.length) {
133
- await waitFor(mounting(plan.scenarios, stats.length));
109
+ await present.wait(mountingLabel(plan.scenarios, stats.length));
134
110
  }
135
111
  }
136
112
  });
137
- settle();
113
+ present.settle();
138
114
  if (!runtime) await header();
139
115
  const effectiveScope = options.scope ?? runtime?.scope ?? staticConfigScope(analysis);
140
116
  const reportInventory = scopeInventory(inventory, effectiveScope);
@@ -182,61 +158,69 @@ ${catalog2}`);
182
158
  }
183
159
  stats.sort((a, b) => runtime.scenarios.indexOf(a.scenario) - runtime.scenarios.indexOf(b.scenario));
184
160
  const mounted = stats.filter((entry) => !entry.failed).length;
185
- await say([
161
+ const parts = [
186
162
  {
187
- title: "SURFACE SUMMARY",
188
- rows: surfaceSummaryRows({
189
- depth: options.depth,
190
- ...coverage ? { coverage } : {},
191
- scenarios: stats,
192
- failures: runtime.failures.length,
193
- reach
194
- })
163
+ kind: "blocks",
164
+ blocks: [
165
+ {
166
+ title: "SURFACE SUMMARY",
167
+ rows: surfaceSummaryRows({
168
+ depth: options.depth,
169
+ ...coverage ? { coverage } : {},
170
+ scenarios: stats,
171
+ failures: runtime.failures.length,
172
+ reach
173
+ })
174
+ }
175
+ ]
195
176
  }
196
- ]);
177
+ ];
197
178
  if (stats.length > 1) {
198
179
  const table = scenarioTable(stats);
199
- const title = `SCENARIOS (${stats.length})`;
200
- if (ink) await paint(/* @__PURE__ */ jsx(ink.Table, { title, headers: table.headers, rows: table.rows }));
201
- else write(`
202
- ${title}
203
- ${renderTablePlain(table.headers, table.rows)}`);
180
+ parts.push({
181
+ kind: "table",
182
+ title: `SCENARIOS (${stats.length})`,
183
+ headers: table.headers,
184
+ rows: table.rows
185
+ });
204
186
  }
205
187
  if (runtime.failures.length > 0) {
206
- writeError(`
207
- ${renderFailuresPlain(runtime.failures)}`);
208
- if (inventory) writeError(`
209
- ${renderNoVerdictPlain(runtime.failures)}`);
188
+ parts.push({
189
+ kind: "findings",
190
+ stream: "err",
191
+ sections: [
192
+ failureSection(runtime.failures),
193
+ ...inventory ? [noVerdictSection(runtime.failures)] : []
194
+ ]
195
+ });
210
196
  }
211
197
  const sections = [
212
198
  ...coverage ? coverageSections(coverage) : [],
213
199
  ...dark.length > 0 && mounted > 1 ? [neverCallableSection(dark)] : []
214
200
  ];
215
- if (sections.length > 0) {
216
- if (ink) await paint(/* @__PURE__ */ jsx(ink.Findings, { sections }));
217
- else write(`
218
- ${renderSectionsPlain(sections)}`);
219
- }
201
+ if (sections.length > 0) parts.push({ kind: "findings", sections });
220
202
  const catalog = scopeInventory(inventory, effectiveScope);
221
203
  if (catalog) {
222
- await say([
223
- {
224
- title: "STATIC CATALOG",
225
- rows: catalogRows(catalog, {
226
- ...domainCount() === void 0 ? {} : { domainCapabilities: domainCount() },
227
- mounted: true
228
- })
229
- }
230
- ]);
204
+ parts.push({
205
+ kind: "blocks",
206
+ blocks: [
207
+ {
208
+ title: "STATIC CATALOG",
209
+ rows: catalogRows(catalog, {
210
+ ...domainCount() === void 0 ? {} : { domainCapabilities: domainCount() },
211
+ mounted: true
212
+ })
213
+ }
214
+ ]
215
+ });
231
216
  }
232
217
  for (const view of views) {
233
- if (ink) await paint(/* @__PURE__ */ jsx(ink.Surface, { view, detail }));
234
- else write(`
235
- ${renderSurfacePlain(view, { detail })}`);
218
+ parts.push({ kind: "surface", view, ...detail ? { detail: true } : {} });
236
219
  }
220
+ await present.emit(...parts);
237
221
  return runtime.failures.length > 0 ? 2 : 0;
238
222
  }
239
223
  export {
240
224
  runInspect
241
225
  };
242
- //# sourceMappingURL=inspect-NJ4J3MPP.js.map
226
+ //# sourceMappingURL=inspect-3YFPCYQJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/inspect.ts"],"sourcesContent":["import {\n joinCoverage,\n mountScenarios,\n readInventory,\n scopeCapabilityIds,\n scopeInventory,\n staticConfigScope,\n type AnalysisOptions,\n type Depth,\n type RuntimePlan,\n} from \"../analysis.js\";\nimport { buildView, flatRows, type SurfaceView } from \"../render/model.js\";\nimport { createPresenter } from \"../render/present.js\";\nimport {\n catalogDetailParts,\n catalogRows,\n coverageSections,\n failureSection,\n mountingLabel,\n neverCallable,\n neverCallableSection,\n noVerdictSection,\n READING_SOURCE,\n runHeaderBlocks,\n scenarioStats,\n scenarioTable,\n surfaceSummaryRows,\n trackReach,\n type CapabilityReach,\n type FindingSection,\n type ReportPart,\n type ScenarioStats,\n} from \"../render/summary.js\";\nimport { write } from \"../output.js\";\nimport {\n coverageReport,\n inventoryReport,\n scenarioReport,\n type ScenarioReport,\n} from \"../report.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\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 * The report is read top-down, so it is written top-down: **summaries first,\n * details after.**\n *\n * 1. **The run** — the config, the depth, the scope, the scenarios. All of it is\n * known before the first mount, and the mounts are the slow half, so it\n * prints immediately rather than making a reader wait to learn what is being\n * measured.\n * 2. **The summary** — reach, what is callable, what the surface can do, the\n * verdict. The answer, before anything it was derived from.\n * 3. **One row per scenario**, for a config with more than one.\n * 4. **The findings** — unreached, never callable, unread call sites — each with\n * what to do about it.\n * 5. **The details** — the static catalog, then each scenario's own table.\n *\n * That order costs the streaming this command used to do: the summary is a\n * statement about every scenario, so it cannot be written until every scenario\n * has mounted. A terminal is told what it is waiting for instead — the header\n * is already on screen and a spinner names the scenario being mounted — and\n * `check`, whose output has always been a report, works the same way.\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 const present = await createPresenter(options);\n\n // The TypeScript program is read synchronously and the app loads after it;\n // together that is seconds on a real repository, and a terminal that shows\n // nothing for them looks wedged.\n if (!options.json) await present.wait(READING_SOURCE);\n\n const inventory = readInventory(analysis);\n const scenarios: ScenarioReport[] = [];\n const stats: ScenarioStats[] = [];\n const views: SurfaceView[] = [];\n const reach = new Map<string, CapabilityReach>();\n let plan: RuntimePlan | undefined;\n\n const scope = (): string[] | undefined => plan?.scope ?? staticConfigScope(analysis);\n const scopedInventory = (): ReturnType<typeof scopeInventory> =>\n scopeInventory(inventory, scope());\n const domainCount = (): number | undefined =>\n plan?.domainManifestConfigured\n ? scopeCapabilityIds(plan.domainCapabilities, scope()).length\n : undefined;\n\n const header = async (): Promise<void> => {\n present.settle();\n if (options.json) return;\n const active = scope();\n const catalog = scopedInventory();\n await present.emit(\n // At `--depth static` the catalog *is* the answer, so it opens the report\n // beside the run it came from. At every other depth it is a detail behind\n // the summary, and prints down there instead.\n {\n kind: \"blocks\",\n blocks: runHeaderBlocks(\n \"SURFACE INSPECT\",\n {\n configPath: options.configPath,\n depth: options.depth,\n ...(active ? { scope: active } : {}),\n ...(plan\n ? { scenarios: plan.scenarios, declaredScenarios: plan.declaredScenarios }\n : {}),\n },\n plan ? undefined : catalog,\n domainCount(),\n ),\n },\n ...(!plan && catalog\n ? catalogDetailParts(catalog, { ...(detail ? { detail: true } : {}) })\n : []),\n );\n };\n\n const runtime = await mountScenarios(analysis, {\n onPlan: async (current) => {\n plan = current;\n await header();\n if (current.scenarios.length > 0) await present.wait(mountingLabel(current.scenarios, 0));\n },\n onEach: async (result) => {\n const view = buildView(result, {\n ...(options.explain ? { explain: true } : {}),\n ...(options.schemas ? { schemas: true } : {}),\n });\n trackReach(reach, flatRows(view), result.scenario);\n stats.push(scenarioStats(result));\n if (options.json) {\n scenarios.push(\n scenarioReport(result, {\n ...(options.explain ? { attribution: true } : {}),\n ...(options.schemas ? { schemas: true } : {}),\n }),\n );\n } else {\n views.push(view);\n }\n // Named, and only while there is one left: a spinner announcing work\n // nobody is doing has to be read twice before it can be dismissed.\n if (plan && stats.length < plan.scenarios.length) {\n await present.wait(mountingLabel(plan.scenarios, stats.length));\n }\n },\n });\n present.settle();\n if (!runtime) await header();\n\n const effectiveScope = options.scope ?? runtime?.scope ?? staticConfigScope(analysis);\n const reportInventory = scopeInventory(inventory, effectiveScope);\n const reportDomain = runtime?.domainManifestConfigured\n ? scopeCapabilityIds(runtime.domainCapabilities, effectiveScope)\n : undefined;\n const coverage = joinCoverage(inventory, runtime, analysis);\n const dark = neverCallable(reach);\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: inventoryReport(reportInventory, reportDomain),\n scenarios,\n failures: runtime?.failures ?? [],\n coverage: coverageReport(coverage),\n // Mounted, and callable in no scenario — a gap the coverage join\n // cannot see, because every one of these *was* reached.\n neverCallable: dark.map((entry) => ({\n capabilityId: entry.capabilityId,\n outcome: entry.best,\n ...(entry.note ? { reason: entry.note } : {}),\n scenarios: entry.scenarios,\n })),\n },\n null,\n 2,\n ),\n );\n return runtime && runtime.failures.length > 0 ? 2 : 0;\n }\n\n if (!runtime) return 0;\n\n // A scenario that threw is not in `stats`, and every count below is missing\n // whatever it held — so it takes a row of its own rather than appearing only\n // at the bottom of the report.\n for (const failure of runtime.failures) {\n stats.push({\n scenario: failure.scenario,\n callable: 0,\n disabled: 0,\n hidden: 0,\n rejected: 0,\n failed: true,\n failure: failure.message,\n });\n }\n stats.sort((a, b) => runtime.scenarios.indexOf(a.scenario) - runtime.scenarios.indexOf(b.scenario));\n const mounted = stats.filter((entry) => !entry.failed).length;\n\n const parts: ReportPart[] = [\n {\n kind: \"blocks\",\n blocks: [\n {\n title: \"SURFACE SUMMARY\",\n rows: surfaceSummaryRows({\n depth: options.depth,\n ...(coverage ? { coverage } : {}),\n scenarios: stats,\n failures: runtime.failures.length,\n reach,\n }),\n },\n ],\n },\n ];\n\n // One row per scenario, so a config with more than one can be compared in a\n // single place rather than by scrolling through its tables.\n if (stats.length > 1) {\n const table = scenarioTable(stats);\n parts.push({\n kind: \"table\",\n title: `SCENARIOS (${stats.length})`,\n headers: table.headers,\n rows: table.rows,\n });\n }\n\n if (runtime.failures.length > 0) {\n parts.push({\n kind: \"findings\",\n stream: \"err\",\n sections: [\n failureSection(runtime.failures),\n ...(inventory ? [noVerdictSection(runtime.failures)] : []),\n ],\n });\n }\n\n // Over one scenario, \"never callable\" repeats that scenario's own table row\n // for row. It is a finding only once there is more than one scenario for a\n // capability to have been callable in.\n const sections: FindingSection[] = [\n ...(coverage ? coverageSections(coverage) : []),\n ...(dark.length > 0 && mounted > 1 ? [neverCallableSection(dark)] : []),\n ];\n if (sections.length > 0) parts.push({ kind: \"findings\", sections });\n\n // Details. The catalog first, because the scenario tables below are what it\n // is the denominator of.\n const catalog = scopeInventory(inventory, effectiveScope);\n if (catalog) {\n parts.push({\n kind: \"blocks\",\n blocks: [\n {\n title: \"STATIC CATALOG\",\n rows: catalogRows(catalog, {\n ...(domainCount() === undefined ? {} : { domainCapabilities: domainCount()! }),\n mounted: true,\n }),\n },\n ],\n });\n }\n\n for (const view of views) {\n parts.push({ kind: \"surface\", view, ...(detail ? { detail: true } : {}) });\n }\n\n await present.emit(...parts);\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.failures.length > 0 ? 2 : 0;\n}\n"],"mappings":"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","names":["catalog"]}