import { BoundaryBinding, BehavioralSummary, Finding, RunFinding, ConfidenceInfo } from '@suss/behavioral-ir'; import { CheckAllResult, SuppressionRule } from '@suss/checker'; import { CheckIntentResult } from '@suss/checker-intent'; import { Project } from 'ts-morph'; import { StubCallEvidence } from '@suss/adapter-typescript'; import { z } from 'zod'; import { Relation } from '@suss/ir-core'; /** * What a unit does at each boundary a summary mentions. * * How somebody spells a boundary, and whether what they wrote picks one * out, is `boundarySpelling.ts` in `@suss/ir-core`, which the intent * checker reads too. */ interface TouchedBoundary { label: string; binding: BoundaryBinding; relation: Relation; /** The call as the source writes it, when the effect recorded one. */ callee: string | undefined; /** What the effect says past the label; see `interactionDetail`. */ detail: string | undefined; transitionId: string | undefined; } /** * One thing somebody can point at, and what it turns out to be. * * Four spellings, resolved in this order: a summary id, which is * anything with `::` in it; a file and a line, `src/dao.ts:43`; a file; * and a boundary, `aws.dynamodb:editions#by-publication`. A file wins over * a boundary, so a path is never read as a boundary whose words happen * to line up. * * A spelling that matches nothing leaves the caller a sentence to * print. An empty report reads as agreement, and this is not that. */ type TargetKind = "summary" | "file" | "line" | "boundary"; /** A unit the target picked out, and what it does at one boundary. */ interface TargetTouch { summary: BehavioralSummary; touched: TouchedBoundary; /** * The calls between the unit somebody asked about and this one, when * the touch was found by following calls rather than in the asked * unit's own body. */ through?: string[]; } interface ResolvedTarget { kind: TargetKind; /** What the caller typed. */ spelledAs: string; /** What it turned out to be, as a report prints it. */ detail: string; /** The units the target picked out. */ summaries: BehavioralSummary[]; /** Transitions covering the line, when the target gave one. */ transitionIds: string[]; /** What those units do at the boundaries they touch. */ touches: TargetTouch[]; } type TargetResolution = { matched: true; target: ResolvedTarget; } | { matched: false; spelledAs: string; message: string; }; /** * The call facts a summary set states, and the reach questions asked * over them as rules. * * A summary says "this calls that" three ways: an invocation effect the * run resolved, a wrapper the framework runs on the way in, and a * caller-kind unit's own binding to the export it calls. All three are * one-hop facts here, `calls` joins them, and a reach * question in either direction is the fixpoint over `calls` with the * shortest call path kept as the tag on each derived fact. * * The node is the function, keyed by where it is, since one function * is several summaries when it is bound to several exports. A call * into any of them is a call into the function. */ /** What `functionOf` returns. */ type FunctionKey = string; /** * Where a call came from: the caller's body, only the caller's binding * to the export it imports, or a function the caller passed to * something else that calls it back, or a wrapper the framework runs in * front of the caller. A written call can be proved from source; a * bound one has no call expression to find; a passed one runs * through a parameter one hop further in; a wrapping one is nowhere in * the caller's own body. */ type CallRecord = "written" | "bound" | "passed" | "wraps"; interface CallHop { callee: string; /** Null when the call is to an export nothing here provides. */ to: FunctionKey | null; recorded: CallRecord; } /** The calls from one function to another, in the order they are made. */ type CallPath = readonly CallHop[]; interface CallFacts { /** Every summary of each function, in the order the run wrote them. */ units: ReadonlyMap; /** * Every call from one function to another, for a caller doing its own * walk. A reach question per unit would run a fixpoint per unit, * which a project with a thousand routes cannot afford. */ edges(): CallEdge[]; /** Who calls a function directly, one entry per caller function. */ callersOf(target: ReachTarget): DirectCall[]; /** Every function that ends up calling into the target, with the shortest path. */ reaching(target: ReachTarget): Map; /** Every function these ones end up calling, with the shortest path. */ reachedFrom(start: Iterable): Map; } /** What a reach question ends at, as the facts spell it. */ interface ReachTarget { /** The functions themselves, which nothing reaches by being one of them. */ functions: ReadonlyArray; /** The package exports they provide, which a caller can be bound to. */ keys: ReadonlyArray; /** Functions at the target already, when it is a boundary and not a function. */ at?: ReadonlyArray; } /** One call, as a walk over the graph reads it. */ interface CallEdge { from: FunctionKey; to: FunctionKey; /** The call as the caller writes it, which is how a chain prints. */ callee: string; } interface DirectCall { caller: FunctionKey; /** The call as the caller writes it, or the export's label when only the binding records it. */ callee: string; } declare function readCallFacts(summaries: ReadonlyArray): CallFacts; /** Where a function is, which is the one thing all of its summaries share. */ declare function functionOf(summary: BehavioralSummary): FunctionKey; /** * Summaries read into memory once, with the call facts built over them * kept beside them. * * Reading a summary directory is most of what a question costs, and * building the call facts is most of the rest. A command that runs * once pays both once. A server answering many questions over one * directory should pay them once per directory, so a caller that keeps * one of these hands it to every question until the directory changes. * * The call facts are built on first use, since a question about what a * boundary declares never needs them. */ interface LoadedSummaries { readonly summaries: BehavioralSummary[]; readonly callFacts: CallFacts; } declare function loadedSummaries(summaries: BehavioralSummary[]): LoadedSummaries; /** * The two why questions: why a unit reaches a boundary, and why a * value resolves to what it does. * * The summaries on disk say which unit calls which and where a * boundary is touched. The witness proof over the resolution rules * says why a callee comes down to the function it does, computed when * the question is asked by re-reading source through whichever * language's adapter reads that file. When a session cannot make * sense of the source, the answer says so in a caveat. */ type WhyShape = "whyReaches" | "whyResolves"; /** * `suss ask`: one question, answered from the summaries already on * disk, and for a why question from the source as well. * * Ten shapes, and no parser behind them. A question that is not one * of the ten gets the ten printed back rather than a guess at what * it meant: a wrong answer about a store is worse than no answer. * * An answer says what it is missing. Nothing on disk declares what most * stores serve until somebody reads the deploy template in, and a list * of fields assembled from call sites would look like the same thing * while meaning something weaker. */ /** The questions that ask who does one thing at a named boundary. */ type Direction = "reads" | "writes" | "invokes"; type QuestionShape = "declares" | Direction | "calls" | "reaches" | "reachedBy" | "provides" | WhyShape; interface AskOptions { question: string; dir?: string; file?: string; /** * Summaries the caller already read, used instead of `dir` or `file`. * A server that answers many questions over one directory reads it * once and passes the same value each time. */ loaded?: LoadedSummaries; json?: boolean; /** Print every item, rather than the first few and a count. */ all?: boolean; output?: string; /** Where the source is, for a why question. Defaults to the cwd. */ project?: string; } interface AnswerItem { text: string; data: Record; } interface Answer { shape: QuestionShape; subject: string; /** The answer in one sentence. */ headline: string; items: AnswerItem[]; /** What this run would need to say more. */ needs: string[]; /** What could make the answer wrong, said plainly. */ caveats: string[]; /** False when the subject is not in these summaries at all. */ found: boolean; /** Structure only the JSON form prints: chains, hops, costs. */ detail?: Record; } declare function ask(options: AskOptions): number; /** * The same run as `ask`, with the answer handed back rather than only * written out. * * A caller inside the same process wants the answer as data. Reading it * back off stdout is the only other way, and a long-lived caller asking * many questions should not have to. */ declare function answerQuestion(options: AskOptions): { exitCode: number; answer: AnswerJson | null; }; /** * A why question opens a language's parser, which loads asynchronously, * so a caller awaits this before asking. Every other question reads * summaries alone, and nothing is warmed for it. */ declare function preloadForQuestion(raw: string): Promise; /** * An answer as the shape `--json` writes and a caller in the same * process reads. A why question adds its chain under `detail`, so the * type stays open past the fields every shape has. */ interface AnswerJson { question: string; shape: QuestionShape; subject: string; found: boolean; headline: string; items: unknown[]; needs: string[]; caveats: string[]; [extra: string]: unknown; } /** * Look up the summary-level confidence for a `Finding` side. The * checker stamps `side.summary` as `${file}::${name}`, which matches * the key we build here. Informational only: the checker does not * use confidence to decide anything; the human-output renderer * surfaces it so reviewers can weigh findings themselves. */ type ConfidenceLookup = Map; type FailOn = "error" | "warning" | "info" | "none"; interface CheckOptions { providerFile: string; consumerFile: string; json?: boolean; output?: string; failOn?: FailOn; /** Override path to a .sussignore file. */ sussignore?: string; /** Skip loading any .sussignore, even if one would be auto-discovered. */ noSuppressions?: boolean; /** Print every finding and every list, not the collapsed report. */ all?: boolean; /** * Let a run that compared nothing exit 0. Without it, the run fails. * * A run that pairs no boundary produces no findings and reads as a * pass, which is the same answer it gives when both sides agree. The * two mean different things: one says the code is consistent, the * other says suss could not see enough of it to say. `extract` takes * the same option for the same reason. A two-file `check` has no * pairing count to gate on, so it refuses this option instead of * reading it. */ allowEmpty?: boolean; } interface CheckDirOptions { dir: string; json?: boolean; output?: string; failOn?: FailOn; sussignore?: string; noSuppressions?: boolean; all?: boolean; /** Let a run that compared nothing exit 0. See CheckOptions. */ allowEmpty?: boolean; /** * Exit non-zero when more boundaries went unpaired than this allows: * a count ("25") or a share of all boundaries ("50%"). A run that * pairs three boundaries out of hundreds otherwise reads the same as * one that paired everything. */ failOnUnpaired?: string; /** * Exit non-zero when a file in the directory could not be read as * summaries. Skipping one silently turns a truncated or malformed * file into a pass. */ failOnUnreadable?: boolean; /** * Directory of team-authored intent specs (`*.intent` / `*.prd`). * When set, each boundary intent is paired against the code summaries * from `dir`, adding intent-coverage findings to the result. */ intent?: string; } interface CheckResult { findings: Finding[]; /** Problems with the run itself, present only when there were any. */ run?: RunFinding[]; /** * Intent pass result (findings + checked / unchecked accounting), * present only when --intent was supplied. */ intent?: CheckIntentResult; hasErrors: boolean; } declare function check(options: CheckOptions): CheckResult; /** Everything one pass over a directory of summaries produced. */ interface CheckedDirectory { summaries: BehavioralSummary[]; /** Which file each summary came from. */ sourceFile: Map; /** Files in the directory that could not be read as summaries. */ skipped: string[]; /** The checker's own result, with suppressions already applied. */ result: CheckAllResult; suppressions: SuppressionRule[]; confidence: ConfidenceLookup; } /** * Read a directory of summaries and run every pass over it. * * `suss check --dir` and `suss check --at` both go through here, so a * scoped run is the full run with a filter over it rather than a second * way of checking that could answer differently. */ declare function checkDirectory(options: { dir: string; sussignore?: string; noSuppressions?: boolean; }): CheckedDirectory; declare function checkDir(options: CheckDirOptions): CheckResult & { result: CheckAllResult; }; /** * `suss check --at`: the findings for one thing. * * The run is the same run. `checkDirectory` reads the folder and every * pass looks at every summary, exactly as a full check does, and what * changes is how much of the result gets printed. So a scoped answer * cannot disagree with the full one, and there is no second checker to * keep in step. * * A target with a gap on it says so. "No findings here" means less when * part of the unit could not be read, and a reader who is not told that * will take the quiet for agreement. */ interface CheckAtOptions { dir: string; /** The file, file and line, boundary, or summary id to report on. */ at: string; json?: boolean; output?: string; failOn?: FailOn; sussignore?: string; noSuppressions?: boolean; } interface CheckAtResult { findings: Finding[]; /** False when the target picked out nothing at all. */ matched: boolean; hasErrors: boolean; } declare function checkAt(options: CheckAtOptions): CheckAtResult; type ContractSource = "openapi" | "cloudformation" | "terraform" | "serverless" | "storybook" | "appsync" | "prisma" | "graphql" | "graphql-documents" | "wrangler"; interface ContractOptions { from: ContractSource; spec: string; output?: string; /** * The directory each deployable unit's code is in, by instance name. * A Terraform configuration never says which directory a container's * image was built from, so `check` has nothing to pair the unit's * code against until somebody says where it is. */ codeScopes?: Record; } declare function contract(options: ContractOptions): Promise; interface CorroborateOptions { /** Verdict-producing executions to aim for per transition. */ runs?: number; /** Sampling attempts per transition before giving up. */ attempts?: number; } /** * Corroborate one summary in place: stamps * `transition.confidence.corroboration` on every response transition * with a literal status. Returns true when the summary was in scope. */ declare function corroborateSummary(summary: BehavioralSummary, project: Project, options?: CorroborateOptions): Promise; interface CorroborateCommandOptions { /** Path to the tsconfig covering the code to read. Optional. */ tsconfig?: string; /** Directory to read when no tsconfig is given. Defaults to cwd. */ dir?: string; frameworks: string[]; /** Write the annotated summaries here instead of discarding them. */ output?: string; /** Verdict-producing executions to aim for per claim. */ runs?: number; /** Sampling attempts per claim before giving up. */ attempts?: number; } interface CorroborateResult { summaries: BehavioralSummary[]; inScope: number; refuted: number; } /** * Extract the project, corroborate every in-scope summary against the * same source, print the report to stdout, and optionally write the * annotated summaries. Returns counts so the CLI can pick an exit * code (refuted claims fail the run: they are findings). */ declare function corroborate(options: CorroborateCommandOptions): Promise; /** * Dependency stubs: checked-in declarations about packages the repo's * code cannot state, read from `suss/stubs/` at the project root. * * v1 is a projection, per design/proposals/dependency-stubs.md: each * statement routes into the pack option that consumes the same fact * today, before the pack factories run, so no pack or adapter * changes. The merged options flow through the same digest pack * config does, so an edited stub invalidates the extraction cache the * same way. YAML and JSON both parse, one schema. */ declare const StubFileSchema: z.ZodObject<{ package: z.ZodString; authored: z.ZodOptional; from: z.ZodOptional; statements: z.ZodArray; export: z.ZodString; composes: z.ZodObject<{ module: z.ZodString; name: z.ZodString; }, z.core.$strip>; }, z.core.$strip>, z.ZodObject<{ kind: z.ZodLiteral<"extends-base">; class: z.ZodString; extends: z.ZodString; }, z.core.$strip>, z.ZodObject<{ kind: z.ZodLiteral<"re-exports">; of: z.ZodString; }, z.core.$strip>, z.ZodObject<{ kind: z.ZodLiteral<"performs-call">; export: z.ZodOptional; system: z.ZodString; spec: z.ZodRecord; }, z.core.$strip>], "kind">>; }, z.core.$strip>; type StubFile = z.infer; /** Pack name to option key to items appended under it. */ type StubOverlay = Map>; declare function loadStubs(root: string): StubFile[]; declare function stubOverlayOf(stubs: StubFile[]): StubOverlay; /** * Which language a directory is written in, and which language a pack * reads. * * suss has one adapter per language behind a single interface, so the * only thing standing between a Python project and `suss extract` is * knowing that the directory is a Python project. A person can say so * with --lang, and most of the time nobody should have to: a directory * usually declares its language in the files it keeps at the top, and * failing that in the source files it is made of. Recognition is * deliberately shallow, since anything deeper would be guessing about * a tree the walk has not read. */ type Language = "typescript" | "python" | "ruby"; declare const LANGUAGES: readonly Language[]; interface ExtractOptions { /** * Path to the tsconfig covering the code to read. Without one, the * nearest tsconfig or jsconfig above the working directory is used, * and the directory itself when there is none. */ tsconfig?: string; /** Directory to read when no tsconfig is given. Defaults to cwd. */ dir?: string; /** Leave it out and suss works it out from what is in the directory. */ lang?: Language; frameworks: string[]; files?: string[]; output?: string; /** * What to do with gaps. `permissive` (default) and `strict` record the * same gaps; `strict` also exits non-zero when any were recorded. * `silent` skips gap detection and records none. */ gaps?: "strict" | "permissive" | "silent"; /** Print the per-phase wall-clock breakdown to stderr. */ timing?: boolean; /** * Print where datalog evaluation spent the run. Off by default, since * collecting it costs a timestamp per rule attempt. */ datalogProfile?: boolean; /** Skip the on-disk extraction cache for this run. */ noCache?: boolean; /** * Print the extraction funnel even when the run produced summaries. * A run that produced nothing prints it either way. */ explain?: boolean; /** * Let a run that produced no summaries exit 0. Without it, the run * fails. Set this when an empty run is expected. */ allowEmpty?: boolean; /** Exit non-zero when a pack threw while it was reading. */ failOnPackError?: boolean; } declare function extract(options: ExtractOptions): Promise; interface InspectOptions { file: string; /** * Spell out the types a summary refers to rather than naming them. * * Naming is the default because a boundary answering with a `User` * is what a reader wants to see, and printing every field of every * named type is how one summary came to be a megabyte. Somebody * chasing a particular shape asks for it. */ types?: boolean; } interface DirOptions { dir: string; types?: boolean; } interface DiffOptions { before: string; after: string; /** * Write the diff as JSON rather than for a person. `inspect` says no * to `--json` everywhere else, because the summaries it reads are * already JSON and printing them again helps nobody. A diff is the * one thing here that no file contains: it is worked out from two of * them, so something reading it has nowhere else to go. */ json?: boolean; /** * The files the change touched, project-relative. A unit in one of * them prints as a line saying how much moved, since the reader has * that file's diff in front of them; a unit in any other file prints * in full. Without the list every file is read as untouched. */ changedFiles?: readonly string[]; /** * How many characters the report may come to. Whole files are * written until the next one does not fit, and the rest are counted * at the end. Without it the report says everything. */ budget?: number; /** * How many calls to print between a boundary and something it * reaches before the middle of the chain collapses into a count. * `"full"` prints every hop, `0` prints none. */ chain?: number | "full"; } declare function inspect(options: InspectOptions): void; declare function inspectDiff(options: DiffOptions): void; declare function readSummariesFromDir(dir: string): BehavioralSummary[]; declare function inspectDir(options: DirOptions): void; /** * `suss infer intent`: turn what suss read out of the code into * starting boundary intent docs for somebody to curate. One document * per boundary, each saying `source: "inferred"`, which is what tells * the checker to downgrade findings against it until a person fills in * the purpose and the audience and moves it to `"inferred, curated"`. * * The mapping mostly moves fields across. What the code cannot supply * is left out rather than filled in with a placeholder, and a boundary * no document could be written for is named in the report. * * The transform lives in the CLI because it reads a BehavioralSummary * and writes an intent doc, and neither IR package depends on the other. */ interface IntentDraftOptions { /** Summaries file to read, the output of `suss extract`. */ from: string; /** Where the documents go. Default: `intent/`. */ out?: string; /** Same destination, but it refuses to write over existing intent docs. */ into?: string; } interface DraftedIntent { /** File name within the destination directory. */ file: string; /** The document's `name`, which PRD scenarios link through. */ name: string; /** The boundary key it was drafted for. */ boundary: string; outcomes: number; yaml: string; } /** A boundary the summaries describe that no document could be written for. */ interface UndraftedBoundary { boundary: string; reason: string; } interface IntentDraftResult { drafted: DraftedIntent[]; undrafted: UndraftedBoundary[]; } /** `from` is the summaries file, written into each document's header. */ declare function intentDraftResult(summaries: BehavioralSummary[], from: string): IntentDraftResult; declare function intentDraft(options: IntentDraftOptions): number; /** * `suss intent outcomes`: list the outcome ids a PRD scenario can link * to, one line each. * * A scenario's `link` is `.`, and both halves * are written inside a boundary intent document. Without a listing the * only way to find them is to open every YAML file in the folder, so a * link written by hand or by a model is a guess, and a wrong guess * comes back later as `danglingScenarioLink`. * * An id in an uncurated draft is listed apart from the rest. Renaming * the outcome ids is the first thing curation does, so a link to one of * those breaks as soon as somebody picks up the draft. */ interface IntentOutcomeRow { /** What a PRD scenario writes in its `link`. */ link: string; /** The boundary document's own `name`. */ intent: string; /** The boundary it is about, spelled the way reports spell it. */ boundary: string; /** The outcome's `id`, the part after the dot in `link`. */ outcomeId: string; /** How the outcome ends and what it turns on, in one line. */ description: string; /** Absolute path of the document that declares it. */ file: string; /** The line in that file the id is written on. */ line: number; } interface IntentOutcomeListing { /** Outcomes of the curated documents, in file order. */ outcomes: IntentOutcomeRow[]; /** Outcomes of inferred drafts, whose ids curation still renames. */ drafts: IntentOutcomeRow[]; /** One message per file in the folder that could not be read. */ unreadable: string[]; } interface IntentOutcomesOptions { /** The folder of intent documents to read. */ from: string; /** Keep only the boundaries whose label contains this text. */ boundary?: string; } /** * Every outcome the folder declares, curated ones apart from drafts. * * A PRD in the folder is skipped: it links to outcomes rather than * declaring any. */ declare function intentOutcomes(options: IntentOutcomesOptions): IntentOutcomeListing; interface IntentOutcomesCommandOptions { /** The folder of intent documents to read. */ from: string; /** Write the rows as JSON, for something other than a person. */ json?: boolean; } /** * A folder with no settled id exits non-zero, because a PRD author who * ran this to find a link has nothing to write. */ declare function intentOutcomesCommand(options: IntentOutcomesCommandOptions): number; /** * projectFile.ts: what `suss init` worked out about a project, written * down so a later command can read it. * * `init` finds the packs a project needs and the artifacts it declares, * prints them, and forgets. Somebody who then forgets one of the * commands gets an empty comparison, because a boundary whose other * side lives in an unread artifact pairs with nothing and the run has * no way to know the artifact was there. * * The file is committed. It says what this project contains, which is * the same for everybody working on it. */ /** The file, at the project root, beside `.sussignore.json`. */ declare const PROJECT_FILE = "suss.json"; /** Source that `suss extract` reads, one entry per language. */ interface ExtractEntry { kind: "extract"; language: string; /** The tsconfig to read from, when the language has one. */ project?: string; /** The `-f` names. */ packs: string[]; } /** An artifact `suss contract` reads, one entry per file. */ interface ContractEntry { kind: "contract"; /** The `--from` name. */ from: string; /** Where the artifact is, relative to the project root. */ file: string; } interface ProjectFile { version: 1; read: Array; } /** Null when the project has no file, or one nothing can read. */ declare function readProjectFile(root: string): ProjectFile | null; /** * projectRead.ts: read a project without being told which packs to use. * * `suss.json` says which packs and artifacts a project has. When the * file is missing, the detection `init` runs picks them from the * dependency manifests and the files on disk, and the command says * so, so the person can write the file down with `init`. The CLI and * the MCP server both read a project this way, so a bare * `suss inspect` and an agent's question describe the same code. */ type ReadEntry = ExtractEntry | ContractEntry; /** What a project says to read, and whether a `suss.json` said it. */ interface DeclaredReads { readonly reads: readonly ReadEntry[]; /** True when `suss.json` was read; false when detection picked them. */ readonly declared: boolean; } /** What reading a project into a directory produced. */ interface ProjectReadReport { readonly summaryDir: string; /** One command line per entry that ran. */ readonly ran: string[]; /** One line per entry that threw, with what it said. */ readonly failed: string[]; readonly declared: boolean; } /** * What `suss.json` says to read, or what `init` would pick when there * is no file. Both come back empty for a project nothing matches. */ declare function declaredReads(root: string): Promise; /** The entry as the command a person would type. */ declare function commandFor(entry: ReadEntry): string; /** * The line a command prints before it reads a project it was not told * about, so the person knows where the packs came from. */ declare function whereReadsCameFrom(root: string, declared: boolean): string; /** * Run every entry into the directory, one file each. An entry that * throws takes down its own file and nothing else, so a project with * one unreadable spec still describes the code suss could read. */ declare function readProjectInto(root: string, summaryDir: string, reads: DeclaredReads): Promise; declare const USAGE: string; /** Returns the exit code rather than calling process.exit, so tests can run it. */ declare function runCli(args: string[]): Promise; /** * `suss infer stub `: turn the project's observed use of a * package suss cannot read into a stub skeleton for an author to fill * in. TypeScript evidence is every call site attributed to the * package, with the argument shapes seen there. Python evidence is * every import of the package or a submodule of it, since a Python * decorator pattern matches a wrapper module exactly. Ruby evidence is * every `require` of the package and every class whose superclass is * spelled from it, since graphql-ruby's `baseClassNames` matches a * superclass by its literal written name. In every case the semantic * blanks are what the author supplies from the package's own source. * The output lands in `suss/stubs/`, where the loader already reads. */ interface StubDraftOptions { package: string; tsconfig?: string; dir?: string; /** Write here instead of `suss/stubs/`; `-` prints to stdout. */ output?: string; } declare function draftYaml(packageName: string, evidence: StubCallEvidence[]): string; interface StubDraft { yaml: string; /** Where the draft belongs, under the resolved source root. */ target: string; } interface StubDraftResult { language: Language; drafts: StubDraft[]; /** Exports covered for TypeScript, imported modules for Python, distinct superclasses for Ruby. */ exports: number; sites: number; } /** Null when the project has no evidence for the package: no calls into it, no imports of it, no class extending it. */ declare function stubDraftResult(options: Pick): Promise; declare function stubDraft(options: StubDraftOptions): Promise; export { type Answer, type AnswerJson, type AskOptions, type CallFacts, type CheckAtOptions, type CheckAtResult, type CheckDirOptions, type CheckOptions, type CheckResult, type ContractEntry, type ContractOptions, type ContractSource, type CorroborateCommandOptions, type CorroborateOptions, type CorroborateResult, type DeclaredReads, type DiffOptions, type DirOptions, type DraftedIntent, type ExtractEntry, type ExtractOptions, type FailOn, type FunctionKey, type InspectOptions, type IntentDraftOptions, type IntentDraftResult, type IntentOutcomeListing, type IntentOutcomeRow, type IntentOutcomesCommandOptions, type IntentOutcomesOptions, LANGUAGES, type Language, type LoadedSummaries, PROJECT_FILE, type ProjectFile, type ProjectReadReport, type QuestionShape, type ReadEntry, type ResolvedTarget, type StubDraftOptions, type StubDraftResult, type StubFile, type TargetKind, type TargetResolution, USAGE, type UndraftedBoundary, answerQuestion, ask, check, checkAt, checkDir, checkDirectory, commandFor, contract, corroborate, corroborateSummary, declaredReads, draftYaml, extract, functionOf, inspect, inspectDiff, inspectDir, intentDraft, intentDraftResult, intentOutcomes, intentOutcomesCommand, loadStubs, loadedSummaries, preloadForQuestion, readCallFacts, readProjectFile, readProjectInto, readSummariesFromDir, runCli, stubDraft, stubDraftResult, stubOverlayOf, whereReadsCameFrom };