@agent-surface/cli 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -6
- package/dist/bin.js +22 -13
- package/dist/bin.js.map +1 -1
- package/dist/check-O7ENP4XP.js +259 -0
- package/dist/check-O7ENP4XP.js.map +1 -0
- package/dist/chunk-G7Z6PP6C.js +532 -0
- package/dist/chunk-G7Z6PP6C.js.map +1 -0
- package/dist/chunk-JR4GESLI.js +61 -0
- package/dist/chunk-JR4GESLI.js.map +1 -0
- package/dist/chunk-LQUL4OIA.js +1441 -0
- package/dist/chunk-LQUL4OIA.js.map +1 -0
- package/dist/chunk-YUT4FVQQ.js +220 -0
- package/dist/chunk-YUT4FVQQ.js.map +1 -0
- package/dist/{init-LFQ5R3G7.js → init-64GIHMKV.js} +64 -41
- package/dist/init-64GIHMKV.js.map +1 -0
- package/dist/{ink-P23VKP4H.js → ink-ICJWXGZC.js} +153 -25
- package/dist/ink-ICJWXGZC.js.map +1 -0
- package/dist/inspect-6OZ6ZBKZ.js +226 -0
- package/dist/inspect-6OZ6ZBKZ.js.map +1 -0
- package/dist/snapshot-GTDFQTN2.js +130 -0
- package/dist/snapshot-GTDFQTN2.js.map +1 -0
- package/package.json +4 -4
- package/dist/check-POE5VRSE.js +0 -183
- package/dist/check-POE5VRSE.js.map +0 -1
- package/dist/chunk-DYDSJM7R.js +0 -170
- package/dist/chunk-DYDSJM7R.js.map +0 -1
- package/dist/chunk-J2NN3J5N.js +0 -541
- package/dist/chunk-J2NN3J5N.js.map +0 -1
- package/dist/chunk-Q5WOLWEW.js +0 -984
- package/dist/chunk-Q5WOLWEW.js.map +0 -1
- package/dist/chunk-QIVOZAWX.js +0 -169
- package/dist/chunk-QIVOZAWX.js.map +0 -1
- package/dist/init-LFQ5R3G7.js.map +0 -1
- package/dist/ink-P23VKP4H.js.map +0 -1
- package/dist/inspect-XMDZBSK2.js +0 -119
- package/dist/inspect-XMDZBSK2.js.map +0 -1
- package/dist/snapshot-FLXE5UC6.js +0 -61
- package/dist/snapshot-FLXE5UC6.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/analysis.ts","../src/render/present.tsx"],"sourcesContent":["/**\n * The two halves of the surface, and the join between them.\n *\n * A presentation surface has two sources of truth, and every command needs\n * some mix of both:\n *\n * - the **catalog** — what this codebase authors. Static: `type` is a string\n * literal, capability names are object keys, so `view:devices.table.sort` is\n * fully determined by source text ([`extract.ts`](./extract.ts)).\n * - the **projection** — what a mounted scenario actually surfaces, after\n * availability, policy and binding have had their say ([`collect.ts`](./collect.ts)).\n *\n * Splitting those across separate commands is what let a green `check` sit on\n * top of a route no scenario visits. So the split lives here, behind a `depth`\n * dial, and the commands compose the same three steps in whatever order their\n * output needs: `inspect` streams and closes with the verdict, `check` collects\n * and leads with it.\n */\nimport { dirname } from \"node:path\";\nimport { matchesScope } from \"@agent-surface/core/explain\";\nimport { baselineDirFor } from \"./baseline.js\";\nimport { UsageError, type Depth } from \"./contract.js\";\nimport type { CollectResult } from \"./collect.js\";\nimport {\n allowlistPathFor,\n buildCoverageReport,\n readAllowlist,\n unreadAllowlistPathFor,\n type CoverageReport,\n} from \"./coverage.js\";\nimport {\n authoredIds,\n extractCapabilities,\n readLiteralConfigScope,\n unresolved,\n type CapabilityInventory,\n} from \"./extract.js\";\nimport { createSurfaceRunner } from \"./load.js\";\n\nexport type { Depth } from \"./contract.js\";\nexport { UsageError } from \"./contract.js\";\n\nexport interface AnalysisOptions {\n configPath: string;\n depth: Depth;\n scenario?: string;\n scope?: string[];\n tsconfig?: string;\n baselineDir?: string;\n}\n\n/**\n * The static half. `undefined` at `--depth runtime`, which is the caller\n * saying it does not want this computed rather than it having failed.\n */\nexport function readInventory(options: AnalysisOptions): CapabilityInventory | undefined {\n if (options.depth === \"runtime\") return undefined;\n return extractCapabilities({\n root: dirname(options.configPath),\n ...(options.tsconfig ? { tsconfig: options.tsconfig } : {}),\n });\n}\n\nexport function staticConfigScope(options: AnalysisOptions): string[] | undefined {\n return options.scope ?? readLiteralConfigScope(options.configPath);\n}\n\n/** A scenario the config declares whose mount threw. Named, never swallowed. */\nexport interface ScenarioFailure {\n scenario: string;\n message: string;\n}\n\n/**\n * Everything knowable once the config has loaded and before the first mount:\n * which scenarios will run, under which scope, against which manifest.\n *\n * Split out so a command can *say* what it is about to measure. The mounts are\n * the slow half — on a real app, seconds of them — and a report that opens with\n * its qualifiers only after they finish spends that time showing nothing and\n * then asks the reader to re-read the numbers above.\n */\nexport interface RuntimePlan {\n /** Scenarios selected for this run, in config order. */\n scenarios: string[];\n /** Every scenario declared by the config, even when one was selected. */\n declaredScenarios: string[];\n baselineDir: string;\n /** CLI scope wins; otherwise the config scope is effective everywhere. */\n scope?: string[];\n /** Authoritative domain capability ids from the configured oRPC manifest. */\n domainCapabilities: string[];\n domainManifestConfigured: boolean;\n}\n\nexport interface RuntimeAnalysis extends RuntimePlan {\n /** The ones that mounted. */\n results: CollectResult[];\n failures: ScenarioFailure[];\n}\n\nexport interface MountHooks {\n /** Called once, after the config loads and before the first mount. */\n onPlan?: (plan: RuntimePlan) => void | Promise<void>;\n /** Called as each scenario finishes, so a command can print as it goes. */\n onEach?: (result: CollectResult) => void | Promise<void>;\n}\n\n/**\n * The runtime half. `undefined` at `--depth static`.\n *\n * `onEach` is awaited as each scenario finishes, so a command can print as it\n * goes instead of after the last mount — a config with ten scenarios is a long\n * time to look at nothing.\n *\n * A scenario that throws is recorded and the run continues. Before this was\n * one command, `capabilities` was the only thing that still worked on an app\n * that would not mount; merging the commands would have thrown that away if a\n * single bad scenario could abort the run.\n */\nexport async function mountScenarios(\n options: AnalysisOptions,\n hooks: MountHooks = {},\n): Promise<RuntimeAnalysis | undefined> {\n if (options.depth === \"static\") return undefined;\n\n const runner = await createSurfaceRunner(options.configPath);\n try {\n if (options.scenario && !runner.scenarioNames.includes(options.scenario)) {\n throw new UsageError(\n `unknown scenario \"${options.scenario}\" — this config defines ` +\n runner.scenarioNames.map((name) => `\"${name}\"`).join(\", \"),\n );\n }\n const scenarios = options.scenario ? [options.scenario] : runner.scenarioNames;\n const effectiveScope = options.scope ?? runner.config.scope;\n const results: CollectResult[] = [];\n const failures: ScenarioFailure[] = [];\n const plan: RuntimePlan = {\n scenarios,\n declaredScenarios: runner.scenarioNames,\n baselineDir: baselineDirFor(\n options.configPath,\n options.baselineDir ?? runner.config.baselineDir,\n ),\n ...(effectiveScope ? { scope: effectiveScope } : {}),\n domainCapabilities: Object.keys(runner.config.manifest?.tools ?? {})\n .map((path) => `domain:${path}`)\n .sort(),\n domainManifestConfigured: runner.config.manifest !== undefined,\n };\n await hooks.onPlan?.(plan);\n\n for (const scenario of scenarios) {\n let result: CollectResult;\n try {\n result = await runner.collect({\n scenario,\n ...(options.scope ? { scope: options.scope } : {}),\n });\n } catch (error) {\n failures.push({\n scenario,\n message: error instanceof Error ? error.message : String(error),\n });\n continue;\n }\n results.push(result);\n await hooks.onEach?.(result);\n }\n\n return { ...plan, results, failures };\n } finally {\n await runner.close();\n }\n}\n\n/**\n * The component type a capability id belongs to: `view:devices.table.sort` →\n * `devices.table`. Capability names are object keys and cannot contain a dot,\n * so the last one is always the boundary; component types can and do.\n */\nfunction componentTypeOf(capabilityId: string): string {\n const withoutPlane = capabilityId.replace(/^(view|domain):/, \"\");\n const dot = withoutPlane.lastIndexOf(\".\");\n return dot === -1 ? withoutPlane : withoutPlane.slice(0, dot);\n}\n\nexport function scopeInventory(\n inventory: CapabilityInventory | undefined,\n scope: string[] | undefined,\n): CapabilityInventory | undefined {\n if (!inventory || !scope) return inventory;\n return {\n ...inventory,\n capabilities: inventory.capabilities.filter(\n (capability) =>\n capability.resolution === \"unresolved\" ||\n matchesScope(componentTypeOf(capability.capabilityId), scope),\n ),\n };\n}\n\nexport function scopeCapabilityIds(ids: string[], scope: string[] | undefined): string[] {\n return scope ? ids.filter((id) => matchesScope(componentTypeOf(id), scope)) : ids;\n}\n\n/**\n * Authored minus reached (`AS-COVER-004…005`).\n *\n * Two ways this returns `undefined`, and neither is \"no gaps\":\n *\n * - a half was not computed, because the depth did not ask for it;\n * - **a scenario failed to mount.** That scenario reached nothing, so every\n * capability it would have surfaced would be reported as one no scenario\n * reaches. A coverage verdict computed over a partial run is precisely the\n * misleading check this package refuses to emit, so there is no verdict\n * until every scenario mounted. The renderer says which of the two it was.\n */\nexport function joinCoverage(\n inventory: CapabilityInventory | undefined,\n runtime: RuntimeAnalysis | undefined,\n options: AnalysisOptions,\n): CoverageReport | undefined {\n if (!inventory || !runtime) return undefined;\n if (runtime.failures.length > 0) return undefined;\n\n // A scope filters the mount, so it has to filter the catalog by the same\n // predicate — core's own, not a second copy of it. Without this, `--scope\n // devices` reported every `app.navigation` capability as unreached, with the\n // words \"no scenario mounts it\" over two that both scenarios mount.\n const effectiveScope = options.scope ?? runtime.scope;\n const inScope = (capabilityId: string): boolean =>\n matchesScope(componentTypeOf(capabilityId), effectiveScope);\n\n const origins = new Map<string, { file: string; line: number }>();\n for (const capability of inventory.capabilities) {\n if (!origins.has(capability.capabilityId)) {\n origins.set(capability.capabilityId, capability.origin);\n }\n }\n\n const authored = new Set([...authoredIds(inventory)].filter(inScope));\n for (const capabilityId of runtime.domainCapabilities) {\n if (inScope(capabilityId)) authored.add(capabilityId);\n if (!origins.has(capabilityId)) {\n origins.set(capabilityId, { file: \"oRPC manifest\", line: 0 });\n }\n }\n const reachedIds = new Set<string>();\n for (const result of runtime.results) {\n for (const capability of result.explanation.capabilities) {\n reachedIds.add(capability.capabilityId);\n }\n }\n\n // The allowlist is a statement about the whole catalog, and a scoped run has\n // only looked at part of it. Judging an out-of-scope entry either way would\n // be wrong in both directions — it is not an unreached capability this run\n // waved through, and it is not a stale entry either, because nothing here\n // reached it.\n const allowlistPath = allowlistPathFor(runtime.baselineDir);\n const wholeAllowlist = readAllowlist(allowlistPath);\n const allowlist = Object.fromEntries(\n Object.entries(wholeAllowlist).filter(([id]) => inScope(id)),\n );\n\n // Not scope-filtered, deliberately: an unread call site has no capability id,\n // so there is no component type to test a scope prefix against. A scoped run\n // simply reports the same unread sites as an unscoped one.\n const unreadAllowlistPath = unreadAllowlistPathFor(runtime.baselineDir);\n\n return buildCoverageReport({\n unreadAllowlist: readAllowlist(unreadAllowlistPath, \"file#reason#site\"),\n unreadAllowlistPath,\n domainAuthoritative: runtime.domainManifestConfigured,\n authored,\n origins,\n reachedIds,\n scenarios: runtime.scenarios,\n ...(effectiveScope ? { scope: effectiveScope } : {}),\n unresolved: unresolved(inventory),\n allowlist,\n allowlistOutOfScope: Object.keys(wholeAllowlist).length - Object.keys(allowlist).length,\n allowlistPath,\n });\n}\n","/**\n * The presenter: one report, one look, whichever command produced it.\n *\n * Every human-facing command builds [`ReportPart`s](./summary.ts) and hands them\n * here. Nothing else in this package decides between the terminal UI and plain\n * text, waits on a spinner, or writes a blank line — which is the point. That\n * choice used to be made at each call site, so `check` and `snapshot` never drew\n * a terminal UI at all, `inspect` printed its static catalog and its mount\n * failures as raw text in the middle of a rendered report, and the same block\n * carried a different text column in each of them.\n *\n * Three invariants it exists to keep:\n *\n * - **One renderer per stream, decided once.** Ink when that stream is a\n * terminal, plain text otherwise (`AS-CLI-003`), for every part of the run.\n * - **One blank line between parts**, on both paths. Ink ends each painted frame\n * with a newline and plain text has to be asked for one, so the callers stop\n * sprinkling `\\n` and getting it wrong in one of the two.\n * - **One label grid.** The text column belongs to the report, not to the block,\n * and it can only ever widen — so `Config`, `Capabilities` and `Coverage` line\n * up whether they were printed a second or a minute apart.\n */\nimport { isPlain, loadInk, paint, transient, write, type OutputFlags } from \"../output.js\";\nimport { renderPartPlain } from \"./plain.js\";\nimport { LABEL_WIDTH, reportGrid, type ReportPart, type ReportStream } from \"./summary.js\";\n\nexport interface Presenter {\n /** Draws parts in order, one blank line between them. */\n emit(...parts: ReportPart[]): Promise<void>;\n /** Names the slow thing currently happening. A no-op in plain mode. */\n wait(label: string): Promise<void>;\n /** Clears any spinner. Called for you before anything is drawn. */\n settle(): void;\n}\n\ntype InkModule = NonNullable<Awaited<ReturnType<typeof loadInk>>>;\n\nclass ReportPresenter implements Presenter {\n /** `null` on a stream rendering plain text — piped, CI, `--plain`, React 18. */\n readonly #out: InkModule | null;\n readonly #err: InkModule | null;\n /** Sticky, and only ever wider: a grid that shrank mid-report is two grids. */\n #labelWidth = LABEL_WIDTH;\n #wrote = false;\n /**\n * Whether the last part was written as plain text. Ink's own frame supplies\n * the blank line after it; plain text has to be asked for one before the next\n * part. Tracking which drew last is what keeps the spacing identical when a\n * drawn report sends a finding to a redirected stderr.\n */\n #plainLast = false;\n #stop: (() => void) | undefined;\n\n constructor(out: InkModule | null, err: InkModule | null) {\n this.#out = out;\n this.#err = err;\n }\n\n async emit(...parts: ReportPart[]): Promise<void> {\n for (const part of parts) await this.#draw(part);\n }\n\n async wait(label: string): Promise<void> {\n this.settle();\n // Nothing transient is ever written in plain mode: `AS-CLI-003` wants\n // byte-stable output, and a spinner is neither.\n const ink = this.#out;\n if (ink) this.#stop = await transient(<ink.Loading label={label} />);\n }\n\n settle(): void {\n this.#stop?.();\n this.#stop = undefined;\n }\n\n async #draw(part: ReportPart): Promise<void> {\n // A spinner is a live frame: anything written under it lands in the region\n // Ink is about to erase.\n this.settle();\n const stream: ReportStream = part.stream ?? \"out\";\n if (part.kind === \"blocks\") {\n this.#labelWidth = Math.max(this.#labelWidth, reportGrid(part.blocks, LABEL_WIDTH).label);\n }\n const separate = this.#wrote && this.#plainLast;\n const ink = stream === \"err\" ? this.#err : this.#out;\n\n if (ink) {\n if (separate) write(\"\", stream);\n await paint(<ink.Part part={part} labelWidth={this.#labelWidth} />, stream);\n } else {\n const text = renderPartPlain(part, this.#labelWidth);\n if (text.length === 0) return;\n write(separate ? `\\n${text}` : text, stream);\n }\n this.#plainLast = ink === null;\n this.#wrote = true;\n }\n}\n\n/**\n * The presenter for this run.\n *\n * `--json` resolves to the plain path and its spinner to nothing, so a command\n * emitting data rather than a report simply has no parts to emit — it never has\n * to ask which renderer it is talking to.\n */\nexport async function createPresenter(flags: OutputFlags): Promise<Presenter> {\n const drawn = !isPlain(flags, \"out\") || !isPlain(flags, \"err\");\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 = drawn ? await loadInk() : null;\n return new ReportPresenter(\n ink && !isPlain(flags, \"out\") ? ink : null,\n ink && !isPlain(flags, \"err\") ? ink : null,\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
findTsconfig,
|
|
5
|
-
unresolved
|
|
6
|
-
} from "./chunk-Q5WOLWEW.js";
|
|
2
|
+
createPresenter
|
|
3
|
+
} from "./chunk-YUT4FVQQ.js";
|
|
7
4
|
import {
|
|
8
5
|
UsageError,
|
|
9
6
|
isPlain,
|
|
10
7
|
loadInk,
|
|
11
|
-
write,
|
|
12
8
|
writeError
|
|
13
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-G7Z6PP6C.js";
|
|
10
|
+
import {
|
|
11
|
+
READING_SOURCE,
|
|
12
|
+
authoredIds,
|
|
13
|
+
catalogDetailParts,
|
|
14
|
+
catalogRows,
|
|
15
|
+
displayPath,
|
|
16
|
+
extractCapabilities,
|
|
17
|
+
findTsconfig
|
|
18
|
+
} from "./chunk-LQUL4OIA.js";
|
|
14
19
|
|
|
15
20
|
// src/commands/init.tsx
|
|
16
21
|
import { existsSync, writeFileSync } from "fs";
|
|
17
|
-
import { join
|
|
22
|
+
import { join } from "path";
|
|
18
23
|
import { jsx } from "react/jsx-runtime";
|
|
19
24
|
var CONFIG_NAME = "agent-surface.config.tsx";
|
|
20
25
|
var ENTRY_CANDIDATES = [
|
|
@@ -53,7 +58,7 @@ async function runInit(options) {
|
|
|
53
58
|
const configPath = join(options.cwd, CONFIG_NAME);
|
|
54
59
|
if (existsSync(configPath)) {
|
|
55
60
|
throw new UsageError(
|
|
56
|
-
`${
|
|
61
|
+
`${displayPath(configPath)} already exists \u2014 edit it, or delete it and re-run`
|
|
57
62
|
);
|
|
58
63
|
}
|
|
59
64
|
const tsconfig = options.tsconfig ?? findTsconfig(options.cwd);
|
|
@@ -62,48 +67,66 @@ async function runInit(options) {
|
|
|
62
67
|
`no tsconfig.json found from ${options.cwd} \u2014 agent-surface reads your TypeScript program to find registration call sites, and cannot do that without one`
|
|
63
68
|
);
|
|
64
69
|
}
|
|
70
|
+
const present = await createPresenter(options);
|
|
71
|
+
await present.wait(READING_SOURCE);
|
|
65
72
|
const inventory = extractCapabilities({ root: options.cwd, tsconfig });
|
|
66
73
|
const ids = authoredIds(inventory);
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
const entry = ENTRY_CANDIDATES.find((candidate) => existsSync(join(options.cwd, candidate)));
|
|
75
|
+
const parts = [
|
|
76
|
+
{
|
|
77
|
+
kind: "blocks",
|
|
78
|
+
blocks: [
|
|
79
|
+
{
|
|
80
|
+
title: "SURFACE INIT",
|
|
81
|
+
rows: [
|
|
82
|
+
{ label: "Tsconfig", text: displayPath(tsconfig) },
|
|
83
|
+
{ label: "Config", text: `${displayPath(configPath)} \u2014 to be written` }
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{ title: "STATIC CATALOG", rows: catalogRows(inventory) }
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
...ids.size > 0 ? catalogDetailParts(inventory) : []
|
|
90
|
+
];
|
|
76
91
|
if (ids.size === 0) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
parts.push({
|
|
93
|
+
kind: "note",
|
|
94
|
+
lines: [
|
|
95
|
+
"Nothing is annotated yet \u2014 that is the default, and it is the safe one: a capability",
|
|
96
|
+
"exists only where someone wrote one. Start with `useAgentComponent` in a component",
|
|
97
|
+
"that owns state worth acting on, then re-run this."
|
|
98
|
+
]
|
|
99
|
+
});
|
|
84
100
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
101
|
+
parts.push({
|
|
102
|
+
kind: "note",
|
|
103
|
+
title: "SCAFFOLD",
|
|
104
|
+
lines: [
|
|
105
|
+
` ${displayPath(configPath)}`,
|
|
106
|
+
entry ? ` imports ./${entry}, which you will still have to wire into a mount()` : " no app entry found, so the import line is a placeholder you will have to point somewhere"
|
|
107
|
+
]
|
|
108
|
+
});
|
|
109
|
+
await present.emit(...parts);
|
|
91
110
|
if (!options.yes) {
|
|
92
111
|
const answered = await ask(options, `Write ${CONFIG_NAME}?`);
|
|
93
112
|
if (!answered) {
|
|
94
|
-
|
|
95
|
-
write("Nothing written.");
|
|
113
|
+
await present.emit({ kind: "note", lines: ["Nothing written."] });
|
|
96
114
|
return 0;
|
|
97
115
|
}
|
|
98
116
|
}
|
|
99
117
|
writeFileSync(configPath, scaffold(entry), "utf8");
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
await present.emit(
|
|
119
|
+
{ kind: "note", lines: [`wrote ${displayPath(configPath)}`] },
|
|
120
|
+
{
|
|
121
|
+
kind: "steps",
|
|
122
|
+
title: "NEXT STEPS",
|
|
123
|
+
steps: [
|
|
124
|
+
"fill in mount() \u2014 it should call your existing composition root",
|
|
125
|
+
"`agent-surface inspect` to see what an agent can reach",
|
|
126
|
+
"`agent-surface snapshot` to commit the baseline, then `check` in CI"
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
);
|
|
107
130
|
return 0;
|
|
108
131
|
}
|
|
109
132
|
async function ask(options, question) {
|
|
@@ -138,4 +161,4 @@ async function ask(options, question) {
|
|
|
138
161
|
export {
|
|
139
162
|
runInit
|
|
140
163
|
};
|
|
141
|
-
//# sourceMappingURL=init-
|
|
164
|
+
//# sourceMappingURL=init-64GIHMKV.js.map
|
|
@@ -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":"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","names":[]}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
STATUS_WIDTH,
|
|
3
|
+
flatRows,
|
|
4
|
+
reportGrid,
|
|
5
|
+
riskClause
|
|
6
|
+
} from "./chunk-LQUL4OIA.js";
|
|
4
7
|
|
|
5
8
|
// src/render/ink.tsx
|
|
6
9
|
import { Box, Static, Text, useInput } from "ink";
|
|
@@ -11,6 +14,13 @@ var OUTCOME = {
|
|
|
11
14
|
disable: { mark: "\u25D0", color: "yellow", state: "disabled" },
|
|
12
15
|
hide: { mark: "\u25CB", color: "red", state: "hidden" }
|
|
13
16
|
};
|
|
17
|
+
var STATUS_COLOR = {
|
|
18
|
+
PASS: "green",
|
|
19
|
+
WARN: "yellow",
|
|
20
|
+
FAIL: "red",
|
|
21
|
+
ERROR: "red"
|
|
22
|
+
};
|
|
23
|
+
var TONE_COLOR = { good: "green", warn: "yellow", bad: "red" };
|
|
14
24
|
var NONE = "\u2014";
|
|
15
25
|
function widthsFor(headers, rows) {
|
|
16
26
|
return headers.map(
|
|
@@ -38,9 +48,102 @@ function Confirm({
|
|
|
38
48
|
function Loading({ label }) {
|
|
39
49
|
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
40
50
|
/* @__PURE__ */ jsx(Text, { color: "cyan", children: /* @__PURE__ */ jsx(Spinner, { type: "dots" }) }),
|
|
41
|
-
` ${label}`
|
|
51
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${label}\u2026` })
|
|
52
|
+
] });
|
|
53
|
+
}
|
|
54
|
+
function Row({ row, width, statuses }) {
|
|
55
|
+
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
56
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: pad(row.label, width) }),
|
|
57
|
+
statuses ? /* @__PURE__ */ jsx(Text, { bold: true, color: row.status ? STATUS_COLOR[row.status] : void 0, children: pad(row.status ?? "", STATUS_WIDTH) }) : null,
|
|
58
|
+
/* @__PURE__ */ jsx(Text, { color: row.tone ? TONE_COLOR[row.tone] : void 0, wrap: "wrap", children: row.text })
|
|
59
|
+
] });
|
|
60
|
+
}
|
|
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);
|
|
67
|
+
return /* @__PURE__ */ jsx(
|
|
68
|
+
Static,
|
|
69
|
+
{
|
|
70
|
+
items: drawn.map((block, index) => ({ key: block.title ?? `block-${index}`, block, index })),
|
|
71
|
+
children: ({ key, block, index }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [
|
|
72
|
+
block.title ? /* @__PURE__ */ jsx(Text, { bold: true, children: block.title }) : null,
|
|
73
|
+
block.rows.map((row) => /* @__PURE__ */ jsx(Row, { row, width: grid.label, statuses: grid.statuses }, row.label))
|
|
74
|
+
] }, key)
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
function Grid({ headers, rows }) {
|
|
79
|
+
const widths = widthsFor(
|
|
80
|
+
headers,
|
|
81
|
+
rows.map((row) => row.cells)
|
|
82
|
+
);
|
|
83
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
84
|
+
/* @__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)) }),
|
|
85
|
+
rows.map((row, index) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
86
|
+
/* @__PURE__ */ jsx(Box, { children: row.cells.map((cell, column) => /* @__PURE__ */ jsx(Text, { bold: column === 0, children: column === headers.length - 1 ? cell : `${pad(cell, widths[column])} ` }, `${column}`)) }),
|
|
87
|
+
row.note ? /* @__PURE__ */ jsx(Box, { paddingLeft: 4, children: /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: `\u2937 ${row.note}` }) }) : null
|
|
88
|
+
] }, `${row.cells[0]}-${index}`))
|
|
42
89
|
] });
|
|
43
90
|
}
|
|
91
|
+
function Table({
|
|
92
|
+
title,
|
|
93
|
+
lead,
|
|
94
|
+
headers,
|
|
95
|
+
rows
|
|
96
|
+
}) {
|
|
97
|
+
return /* @__PURE__ */ jsx(Static, { items: [{ key: title }], children: (block) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
98
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: title }),
|
|
99
|
+
lead ? /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: lead }) : null,
|
|
100
|
+
/* @__PURE__ */ jsx(Grid, { headers, rows })
|
|
101
|
+
] }, block.key) });
|
|
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
|
+
}
|
|
122
|
+
function Findings({ sections }) {
|
|
123
|
+
return /* @__PURE__ */ jsx(
|
|
124
|
+
Static,
|
|
125
|
+
{
|
|
126
|
+
items: sections.map((section, index) => ({ key: `${section.title}-${index}`, section, index })),
|
|
127
|
+
children: ({ key, section, index }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [
|
|
128
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
129
|
+
/* @__PURE__ */ jsx(
|
|
130
|
+
Text,
|
|
131
|
+
{
|
|
132
|
+
backgroundColor: section.tone === "notice" ? "yellow" : "red",
|
|
133
|
+
color: "black",
|
|
134
|
+
bold: true,
|
|
135
|
+
children: ` ${section.title} `
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${section.gloss}${section.count > 0 ? ` ${section.count}` : ""}` })
|
|
139
|
+
] }),
|
|
140
|
+
section.headers && section.rows ? /* @__PURE__ */ jsx(Grid, { headers: section.headers, rows: section.rows }) : null,
|
|
141
|
+
(section.lines ?? []).map((line, index2) => /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsx(Text, { children: line }) }, `${index2}`)),
|
|
142
|
+
section.hint ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "cyan", wrap: "wrap", children: `\u2192 ${section.hint}` }) }) : null
|
|
143
|
+
] }, key)
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
}
|
|
44
147
|
function PolicyLine({
|
|
45
148
|
policy
|
|
46
149
|
}) {
|
|
@@ -86,6 +189,7 @@ function Group({ group }) {
|
|
|
86
189
|
] });
|
|
87
190
|
}
|
|
88
191
|
function Header({ view }) {
|
|
192
|
+
const risk = riskClause(flatRows(view));
|
|
89
193
|
return /* @__PURE__ */ jsxs(Box, { children: [
|
|
90
194
|
/* @__PURE__ */ jsx(Text, { bold: true, children: view.scenario }),
|
|
91
195
|
view.route ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${view.route}` }) : null,
|
|
@@ -99,6 +203,10 @@ function Header({ view }) {
|
|
|
99
203
|
view.rejections.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
100
204
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ", " }),
|
|
101
205
|
/* @__PURE__ */ jsx(Text, { color: "magenta", children: `${view.rejections.length} registration${view.rejections.length === 1 ? "" : "s"} rejected` })
|
|
206
|
+
] }) : null,
|
|
207
|
+
risk ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
208
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \xB7 " }),
|
|
209
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", children: risk })
|
|
102
210
|
] }) : null
|
|
103
211
|
] });
|
|
104
212
|
}
|
|
@@ -173,6 +281,40 @@ function CapabilityTable({ rows }) {
|
|
|
173
281
|
))
|
|
174
282
|
] });
|
|
175
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
|
+
}
|
|
176
318
|
function Surface({
|
|
177
319
|
view,
|
|
178
320
|
detail
|
|
@@ -189,29 +331,15 @@ function Surface({
|
|
|
189
331
|
rows.length === 0 ? /* @__PURE__ */ jsx(Empty, { view }) : null
|
|
190
332
|
] }, block.key) });
|
|
191
333
|
}
|
|
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) });
|
|
210
|
-
}
|
|
211
334
|
export {
|
|
212
335
|
Confirm,
|
|
213
|
-
|
|
336
|
+
Findings,
|
|
214
337
|
Loading,
|
|
215
|
-
|
|
338
|
+
Note,
|
|
339
|
+
Part,
|
|
340
|
+
Report,
|
|
341
|
+
Steps,
|
|
342
|
+
Surface,
|
|
343
|
+
Table
|
|
216
344
|
};
|
|
217
|
-
//# sourceMappingURL=ink-
|
|
345
|
+
//# sourceMappingURL=ink-ICJWXGZC.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"]}
|