import { type CloudStepOptions } from "./cloud-init.js"; import type { InitPolishSeam } from "./init-judgment.js"; import { type McpPosture } from "./init-mcp.js"; import { type DevCredential } from "../harnesses/inference/resolve.js"; import { type HostFramework } from "./framework.js"; import { type AuthAnswer } from "./init-auth.js"; import { type InstallRunner } from "./provider-deps.js"; import { INIT_USE_CASES, type InitUseCase } from "./install-record.js"; import { type PrettyOutput, type SelectOption } from "./pretty.js"; import { type Output } from "./shared.js"; export { INIT_USE_CASES, type InitUseCase }; /** How the run ENDS, in every mode: what it wired, what it detected, the guard posture it left behind, and the ONE page that carries the instructions. */ interface InitFacts { /** The install's files, root-relative: what init wrote, plus what an earlier init already put there (it is idempotent, so a re-run leaves the same set in place). Every entry exists on disk. */ wrote: string[]; detected: { framework: string; auth: string; packageManager: string; port: number; }; guardPosture: string; /** MCP arm with a Cloud key: the one line saying what that key settled for both environments. Absent everywhere else — a run with no key cannot promise a deployment anything. */ signIn?: string; continueUrl: string; /** Slots the extraction was unsure of and KEPT as extracted. Nothing blocks on them: init asks nothing once the question phase is over. */ keptUncertain: string[]; /** Loosening proposals the judgment pass held as PENDING. Never applied — a loosening needs a human — and never asked about mid-run, so the count is a fact the run reports and `vendo sync --review` is where it is answered. */ pendingLoosenings: number; } /** How an agent-mode run ENDS: the same facts, as data. Its twin is `InitQuestions` — one status field tells them apart, and both exit 0, so the coding agent branches on the shape and never on a code. */ export interface InitReceipt extends InitFacts { status: "written"; root: string; useCase: InitUseCase; /** MCP re-run only: the service key is in `.env.local`, but `serviceAuth` is not in the composition — init never rewrites a file it did not author, so that one line is the developer's, at the continue URL. */ serviceAuthUnwired?: true; /** Agent mode grades like every other run — `graded` means the pass ran here and the grades are on disk. `delegated` is the one fallback left: no judgment engine resolved on this machine, so the catalog is ungraded and the checklist is REQUIRED work for the caller, not a suggestion. */ judgment: { status: "graded"; file: string; } | { status: "delegated"; checklist: string[]; }; } export interface InitOptions { targetDir: string; agent?: boolean; yes?: boolean; force?: boolean; /** Agent-install-dx value flags: each one answers exactly one wizard question, so a non-interactive run never needs the prompt it replaces. */ /** --auth: the auth answer — wires like the equivalent interactive pick. */ auth?: AuthAnswer; /** --framework: detection override; required non-interactively when detection comes back "unknown" (there is no safe default to guess). "unknown" is excluded: an override that answers nothing would silently bypass the non-interactive framework guard. */ framework?: Exclude | "custom"; /** --cloud-key: answer the cloud-login offer with an existing key — landed in .env.local exactly where the mint would put it. */ cloudKey?: string; /** --byo: answer the cloud-login offer with "no — bring my own key". */ byo?: boolean; /** --use-case: answer the first question without asking. Unattended runs take "embedded" — today's behaviour, so no existing script changes. */ useCase?: InitUseCase; /** --base-url: answer "where does this app run in dev?" without asking — written to .env.local as VENDO_BASE_URL. A DEPLOYED URL does not belong here: production reads the variable from the hosting platform's own env, and a public URL in .env.local would repoint local dev's discovery, callbacks and credential forwarding at the deployed origin. */ baseUrl?: string; /** --posture: which authorization server fronts the door (MCP use case only). No longer a question — a Cloud key answers both environments at once — so this is the escape hatch for a host that wants a Cloud-fronted-only door and no sign-in key on its dev machine. */ posture?: McpPosture; /** --service-key: the dev sign-in key, which a local door wires by default. `false` is the explicit opt-out (no flag spells it; a programmatic caller can). MCP use case only. */ serviceKey?: boolean; /** --ai / --no-ai (`--ai-polish` is the legacy spelling of `--ai`): `true` runs the judgment pass with no prompt, `false` forces it off, and `undefined` asks in an interactive run and skips otherwise. No answer is ever persisted — every interactive run asks again. */ ai?: boolean; /** --engine: pin the AI-polish rung family (claude | codex | npx). */ engine?: string; /** --theme slot=value answers for the uncertain-slot review. */ themeAnswers?: Record; output?: Output; telemetry?: { home?: string; env?: Record; posthogKey?: string; fetchImpl?: typeof fetch; }; env?: Record; /** Test seam: credential detection for the key step. */ resolveCredential?: (options: { env: Record; }) => Promise; /** Test seam: the provider-dependency install subprocess (provider-deps.ts). */ installProvider?: InstallRunner; /** Test seam: the `@vendoai/vendo` install subprocess (#1153). */ installVendo?: InstallRunner; /** Test seam: the zod-floor bump install subprocess. */ installZod?: InstallRunner; /** Test seam (ENG-339): cloud-in-init step overrides. */ cloud?: Partial>; /** Test seam: judgment step overrides (harnesses, consent). */ extract?: InitPolishSeam; /** Test seam: "How do your users sign in?" — asked on every interactive run that creates the composition. Receives the choice list (value/label/hint) and the index the package.json scan pre-selects, and resolves the chosen value. */ selectAuth?: (question: string, options: SelectOption[], defaultIndex?: number) => Promise; /** Test seam: interactivity override for the auth question (default: TTY), mirroring the judgment step's `interactive`. */ interactive?: boolean; /** Test seam: the use-case question, and the MCP sign-in select that hangs off it. Mirrors the auth picker's shape. */ selectUseCase?: (question: string, options: SelectOption[]) => Promise; /** Test seam: the free-text asks (the dev base URL). "" is "nobody was asked" — the prompt itself turns a bare Enter into the prefilled default, so a seam that answers "" stands for a run that never got to ask. */ askText?: (question: string, hint?: string, defaultValue?: string) => Promise; } /** The detection read-back, printed before the first question. Nothing here is newly computed — framework, router style, language, package manager and auth family are all detected today and none of them is ever shown, so the first thing the user sees is a question about a package they were never told we found. Print, never re-detect. */ export declare function stackLines(root: string, framework: Exclude | "custom"): Promise; /** "Where does this app run in dev?" — prefilled with the port the host's own * `dev` script names, so Enter is the whole interaction, and the answer lands * in .env.local as VENDO_BASE_URL. Own-agent-loop tools, backend processes and * the MCP door never see a wire request, so without it the first tool call * meets "Cannot execute … set VENDO_BASE_URL" instead of working. * * A run that cannot ASK writes NOTHING: the prefill is only an answer when a * person accepts it, and a guessed origin is worse than an absent one — unset * in dev still learns the request's own origin, and production fails loud. * Production is told at deploy time, never asked here: a public URL in * .env.local would repoint local dev's discovery, callbacks and credential * forwarding at the deployed origin. * * Returns the answer, or null when the run could not ask. */ export declare function captureDevBaseUrl(input: { root: string; options: InitOptions; output: Output; pretty: PrettyOutput | null; }): Promise; /** The footer's stats. It never claims more than the run achieved: the catalog it read, the brand it captured, and that the wiring landed. */ export declare function runStats(input: { toolCount: number; brandCaptured: boolean; }): string; export declare function runInit(input: InitOptions): Promise;