/** * W3e: context-cost inventory for the Claude host binding. * * Per the Project Framework Binding v1 plan, every binding carries a * context-cost inventory "with a defined evidence source per adapter * (host-reported projections such as `claude plugin details` where * available; AIH estimates labeled as estimates elsewhere)". The Framework * Card (W7) surfaces this as "context-cost estimate at session start * (labeled estimate, with its evidence source)" — this module produces the * {@link ContextCostReport} that data comes from, via three entry points: * * - {@link contextCostFromPluginDetails} — HOST-REPORTED, JSON payload. * Kept for forward-compat with a future host version; `claude plugin * details` does NOT actually accept `--json` today (see the text sibling * below) — this parses an already-fetched payload of that SHAPE, should * one ever be produced. Tolerant of absent sections (they count as zero) * but fails closed on a payload with no recognizable shape at all, and * NEVER invents a token number the host did not report. * - {@link contextCostFromPluginDetailsText} — HOST-REPORTED, TEXT (the * ACTUAL 2.1.214 shape). Parses the raw stdout `claude plugin details` * prints (the W3b `pluginDetails` wrapper in `./plugins.ts` invokes the * CLI; this module never shells out itself): a component inventory * (`Skills (N)`, etc.) plus an `Always-on: ~N tok` line. Same * tolerance/fail-closed/no-fabrication rules as the JSON sibling. * - {@link estimateContextCostFromTree} — AIH-ESTIMATE. Walks a framework * tree on disk (a scanned checkout or an installed surface dir) and * derives a labeled, deterministic estimate from file counts and byte * sizes — no tokenizer, no network, no magic numbers beyond the * documented bytes/4 divisor. * * All three paths return the SAME {@link ContextCostReport} shape so a * caller (or the Framework Card renderer) never has to branch on which * adapter produced it — only on `source`/`estimate` to label it correctly. */ export type ContextCostEvidenceSource = "host-reported" | "aih-estimate"; export interface ContextCostReport { source: ContextCostEvidenceSource; /** Non-authoritative human label, e.g. "claude plugin details" or "aih static tree estimate". */ evidence: string; /** Projected session-start token cost; undefined when the evidence source cannot provide one. */ projectedTokens?: number; /** Counted surfaces feeding the projection. */ counts: { skills: number; agents: number; commands: number; rules: number; hooks: number; mcpServers: number; }; /** Total bytes of the counted surface files (estimate basis). */ totalBytes: number; /** True when numbers are AIH estimates rather than host-reported. */ estimate: boolean; } /** * Host-reported context cost: parse a `claude plugin details --json` payload * (already fetched by the caller via the W3b `pluginDetails` wrapper — this * function never invokes the CLI itself). The exact 2.1.214 payload field * paths are pinned during W4's real-VM runs; parsing here stays intentionally * tolerant (every section independently optional) so that run only needs to * adjust field paths, not this function's control flow. * * Tolerant-but-typed: every `components.*` section and `projectedTokens` may * be absent (absence counts as zero / `undefined`), but the payload as a * whole must carry at least one recognizable field (`components` or * `projectedTokens`) — a payload with neither is refused rather than silently * reported as an all-zero cost. A present field with the wrong shape (e.g. a * non-array `components.skills`, or a non-numeric `projectedTokens.total`) * fails closed the same way: this function never fabricates a token number or * silently coerces a malformed field into zero. * * `counts.rules` is always `0` here — a Claude plugin's `details` payload has * no "rules" concept (that is an AIH/framework-tree surface); only * {@link estimateContextCostFromTree} ever counts rules. */ export declare function contextCostFromPluginDetails(payload: unknown): ContextCostReport; /** * Host-reported context cost: parse the raw TEXT `claude plugin details` * prints (empirically corrected — this CLI has NO `--json` flag; the W3b * `pluginDetails` wrapper in `./plugins.ts` invokes it and hands the stdout * here unmodified). Tolerant per-category (an absent header counts as `0`, * matching {@link contextCostFromPluginDetails}'s JSON-payload tolerance * model), but the text AS A WHOLE must carry at least one recognizable * signal — a component header OR the `Always-on:` line — or it fails closed * rather than silently reporting an all-zero-but-successful cost for * unrelated/garbage text. NEVER fabricates a token number: an absent * `Always-on:` line leaves `projectedTokens` `undefined`. */ export declare function contextCostFromPluginDetailsText(text: string): ContextCostReport; /** * AIH-estimate context cost: walk `treePath` (a scanned framework checkout or * an installed surface dir) and derive a deterministic, labeled estimate. * Fails closed ({@link ClaudeHostWriteError}) when `treePath` is not a * directory; every SURFACE within it is independently optional (a missing * `agents/`, `commands/`, `rules/`, `skills/`, `hooks/hooks.json`, or * `.mcp.json`/`mcp.json` counts as zero, not an error) — but a surface file * that EXISTS and does not parse/shape as expected fails closed, because a * write-path-style silent default here would silently miscount the one * estimate this function is the source of truth for. * * Heuristics (deterministic; no tokenizer, no network, no magic beyond the * documented bytes/4 divisor): * - skills: `skills//SKILL.md` (nested layout) UNION top-level * `/SKILL.md` (the tree itself IS a skills collection, e.g. an * installed `.claude/skills/` dir) — one counted skill per matching dir. * - agents: immediate `agents/*.md` files (one level, not recursive). * - commands: immediate `commands/*.md` files (one level, not recursive). * - rules: every `.md` file anywhere under `rules/` (recursive — nested rule * dirs count as files, matching a `rules/**\/*.md` glob). * - hooks: `hooks/hooks.json`, summing hook-entry ARRAY LENGTHS across every * top-level event key (a `{"PreToolUse": [...], "PostToolUse": [...]}` * shape) — absent file -> 0; present-but-unparsable or a non-array event * value -> fail closed. * - mcpServers: `.mcp.json` (checked first) or `mcp.json`, counting the keys * of the top-level `mcpServers` map — absent file -> 0; present-but- * unparsable, or `mcpServers` present-but-not-an-object -> fail closed. * * `totalBytes` is the byte sum of every counted file (every counted * SKILL.md/agent/command/rule file, plus the whole `hooks.json`/mcp file when * present) — the estimate basis `projectedTokens` derives from via the * standard rough chars-per-token divisor (`Math.round(totalBytes / 4)`). */ export declare function estimateContextCostFromTree(treePath: string): ContextCostReport;