import { type GraphIR, type IRNode } from "../../graph-ir.js"; import type { CommandContext } from "../registry.js"; /** * `chant search [--live --env ]` — answer an estate question with * a COMPACT result instead of the whole graph. The point (measured on aws-bench, * #1139): a small model shouldn't ingest a multi-thousand-token IR dump to answer * "which instances are in public subnets" — it should query and get a few rows. * * Query grammar (space-separated terms, all must match — AND): * bare word case-insensitive substring over id, kind, and attrs * kind: node kind contains (e.g. kind:EC2::Instance) * tag:= a Tags entry with Key=key and Value containing val * attr:= attribute equals/contains * ->kind:X / ->attr:.. this node has an edge TO a node matching the right side * <-kind:X / <-attr:.. this node has an edge FROM a node matching the right side * * The edge operators are the point of "edge-aware" search (#1139): a small * model shouldn't hand-join instance→subnet→public across many results — one * query does the traversal. `kind:Instance ->attr:MapPublicIpOnLaunch=true` * = instances that reference a public subnet. * * Output: one line per match — ` ` — with only the * physical id and any attributes named in `attr:`/`--show`. Tens of tokens, not * thousands. */ export declare function runSearch(ctx: CommandContext): Promise; /** Where an answer's facts came from, for the provenance line (#1266). */ type AnswerSource = { kind: "declared"; } | { kind: "live"; } | { kind: "snapshot"; commit: string; timestamp: string; }; /** * Name the `--show` columns nothing carries (#1279). * * A requested column that no matched resource has simply did not appear, so the * result looked like a resource with no such value rather than a name that was * never going to match. Combined with case sensitivity that made `--show * Region` an invisible no-op on an estate where every resource carries * `region`. */ declare function showMiss(matches: IRNode[], show: string[]): void; /** * Say when the answer spans more than one region (#1279). * * A result is a list of resources with no shape to it, and region is the one * dimension of this estate that is invisible in a row unless asked for. Asked * to list instances "in all regions", an agent printed six correct ids with no * region against any of them — a complete answer to a question about regions * that never mentions one, and it was judged wrong. * * Stated only when the matched set actually spans several and the caller has * not already asked: a fact about the result, in the same family as the * provenance line. It names the regions and no resource, so it cannot stand in * for the answer — it says the answer has a dimension, not what to say about it. */ declare function regionSpread(terms: Term[], matches: IRNode[], show: string[]): void; /** * Point out that `--ambient` is relevant to the kind just queried (#1278). * * A resource nothing declares and nothing references is invisible to every * other observation path, so an answer about "my security groups" can be * complete for the declared estate and still not be the answer the question * wanted. The caller cannot know that from the result — it looks like the whole * set. An agent asked which groups were unused queried the three declared ones, * never learned three more existed, and spent twenty-five turns trying to * reconcile the shortfall from the graph. * * Says only that the flag applies to this kind, which is knowable without a * scan. It reports no count and names no resource, so it cannot stand in for * the answer. */ declare function ambientHint(matches: IRNode[], ambientKinds: string[], asked: boolean, replay?: { recordedAmbient: boolean; }): void; /** * Say what backed this answer (#1266). * * Two things went wrong without it. A `--live` read that failed entirely * returned the declared graph, exit 0, with no physical ids and nothing to say * so — indistinguishable from a working live answer (#1263). And the derived * surface below named folds like `internetFacing` whether or not the * observation could support them, which is worse than saying nothing. * * It is also the most direct thing the tool can say to a caller deciding * whether to re-check with a raw provider sweep: the API has already been read, * and this many resources were bound to what it returned. A sweep repeats work * already done. That is a fact about the query, printed for every query, and it * encodes no expected answer. */ declare function provenance(matches: IRNode[], source: AnswerSource, recorded?: string, liveFailures?: string[]): void; /** * Name the facts chant computed for the kinds in this result that the query did not use. * * A provider API can only return what it stores; chant additionally folds multi-hop topology * onto a node, and a caller has no way to know that surface exists. Reporting it turns a * one-shot query into a conversation with the graph — ask something, learn what else is * knowable about the same resources, refine. * * The names come from {@link GraphIR.derivedAttrs}, recorded by whichever enrichment pass * produced them. Nothing here knows what any attribute means or which question it answers; * add a pass and its facts appear, remove one and they stop. */ declare function derivedSurface(terms: Term[], matches: IRNode[], ir: GraphIR, backed?: boolean): void; /** * `--explain` footer (#1139): a compact, model-DERIVED summary that gives a * small model a reason to trust the result instead of re-deriving it with a * lossy CLI sweep. It reports the universe count ("4 of 6 Instances") — chant's * structural edge, since the typed graph knows the denominator a live sweep * doesn't — and, for the near-miss set, WHY each was excluded (which query term * it fails). Everything here is a property of the query over the graph, not of * any expected answer, so it stays a fair, question-agnostic capability. */ declare function explain(terms: Term[], matches: IRNode[], ir: GraphIR, byId: Map, query: string): void; /** * On a miss, name the attributes the queried kind actually carries. A graph knows * its own schema, so a caller who guessed an attribute name — or did not know a * derived one existed — can see what is queryable instead of falling back to a * lossy CLI sweep. Read off the nodes present, so it stays a property of the * graph rather than of any expected answer: whatever the estate holds is what * this lists, and it says nothing about which attribute answers a question. */ declare function availableAttrs(terms: Term[], ir: GraphIR): void; declare function describeTerm(t: Term): string; export interface Term { kind: "word" | "kind" | "tag" | "attr" | "edge"; /** `!term` — the node must NOT satisfy this (#1280). */ negated?: boolean; a: string; b?: string; /** For edge terms: the direction and the sub-predicate matched at the far end. */ dir?: "out" | "in"; sub?: Term; /** * For kind terms: the kinds this term actually means, resolved against the * graph (see {@link resolveKindTerms}). Absent when nothing resolved it, in * which case the substring rule applies unchanged. */ kinds?: Set; } /** * Decide which kinds a `kind:` term means, given what is in the graph. * * `kind:` is documented as a substring, and substrings of a CloudFormation type * cross kind boundaries: `kind:EC2::VPC` also matches * `AWS::EC2::VPCGatewayAttachment`, and `kind:EC2::Subnet` also matches * `AWS::EC2::SubnetRouteTableAssociation`. An estate with 6 VPCs answers 9, and * the caller has no reason to suspect the number they were given. * * That cost real answers. Asked how many VPCs have no instances, agents named * the right VPCs and reported "9 VPCs in the estate" beside them; the grader * failed the answer for contradicting the estate, which it did. * * So: a term that lines up with `::` boundaries means those kinds and only * those. `EC2::VPC` is the last two segments of `AWS::EC2::VPC` and is not any * run of segments in `AWS::EC2::VPCGatewayAttachment`, because * `VPCGatewayAttachment` is not `VPC`. * * Substring is kept as the fallback, so `kind:Gateway` still finds both * `InternetGateway` and `VPCGatewayAttachment` — a genuine substring search * that no segment rule would serve. Most specific wins; nothing else changes. */ export declare function resolveKindTerms(terms: Term[], nodes: IRNode[]): void; declare function parseQuery(query: string): Term[]; declare function matchTerm(n: IRNode, t: Term, ir?: GraphIR, byId?: Map): boolean; declare function formatRow(n: IRNode, show: string[]): string; /** Internals exposed for unit tests. */ export declare const __searchInternals: { parseQuery: typeof parseQuery; matchTerm: typeof matchTerm; formatRow: typeof formatRow; explain: typeof explain; describeTerm: typeof describeTerm; derivedSurface: typeof derivedSurface; availableAttrs: typeof availableAttrs; ambientHint: typeof ambientHint; regionSpread: typeof regionSpread; showMiss: typeof showMiss; provenance: typeof provenance; }; export {}; //# sourceMappingURL=search.d.ts.map