import { type Telemetry } from "#telemetry"; export declare const CLI_VERSION = "0.62.1"; export interface Output { log(message: string): void; error(message: string): void; } export declare const consoleOutput: Output; export declare function askYesNo(question: string, defaultYes?: boolean): Promise; /** True when this process was started by a package-manager lifecycle script (`predev`, `prebuild`, any `npm run …`). Such a run is NOT interactive, even on a TTY: npm inherits the terminal, so a command that stops to ask would block what the human thinks is a dev-server start — and a reflexive Enter on a default-yes prompt would spend money. A run the human did not invoke never gets a question. `npx` is the one event name that is not a script: `npx`/`npm exec` runs its target as a synthetic script literally named `npx`, so the docs' own `npx vendo init` arrived here looking like a hook and ran mute. A human typing `npx vendo …` is a human; real hooks have their own names. */ export declare function invokedByPackageScript(env?: Record): boolean; export declare function exists(path: string): Promise; /** A leading UTF-8 BOM, stripped before any read is parsed or compared: Notepad and PowerShell's Set-Content both write one, and npm and Node's own require() both tolerate it — a host file must never be less readable to vendo than to npm (FINDINGS, linkwarden field test 2026-08-08: a BOM'd package.json crashed init with a raw SyntaxError). */ export declare function stripBom(text: string): string; export declare function readOptional(path: string): Promise; export declare function writeText(path: string, content: string): Promise; /** The injectable telemetry deps every CLI command's options carry (init/doctor already ride this exact shape). */ export interface TelemetryOptions { home?: string; env?: Record; posthogKey?: string; fetchImpl?: typeof fetch; /** The command's TARGET project dir: projectIdHash/packageManager derive from it (not the shell cwd — `vendo sync ../app` must attribute to ../app), and it is where the .env.local cloud-key read looks. Defaults to process.cwd(). */ cwd?: string; } /** * The value of one NAME=value line in the project's dotenv. The SYNC half of * the one env reader — telemetry client creation cannot await — reading the * same files in the same precedence as sync-flow.ts's `readEnvFiles`: `.env` * then `.env.local`, local wins. Matches dotenv semantics for hand-authored * entries: surrounding quotes are stripped, and unquoted values lose their * ` #…` inline comment. Non-throwing: a missing or unreadable file is null. */ export declare function envFileValueSync(root: string, name: string): string | null; /** THE dotenv parser, for both halves of the CLI's env reader (this file's * envFileValueSync and sync-flow.ts's readEnvFiles). Minimal KEY=VALUE: * `export ` prefix, `#` comment lines skipped, value grammar below. */ export declare function parseDotEnv(text: string): Record; /** One value grammar for the parser above: matching surrounding quotes are * stripped; unquoted values lose their ` #…` inline comment. */ export declare function normalizeDotEnvValue(value: string): string; export declare function toolingTelemetry(options?: TelemetryOptions & { log?: (message: string) => void; }): Telemetry; export declare function errorClass(error: unknown): string; /** The closed `command_run.command` enum (TELEMETRY.md). init keeps its own richer events; "theme" is reserved — no `vendo theme` entrypoint exists yet. "login" is the top-level claim ceremony; init's embedded run of the same ceremony stays "cloud-init". */ export type CommandName = "login" | "extract" | "theme" | "sync" | "cloud-init" | "mcp" | "knowledge"; /** Cloud-lane project identity (projectName + repoHost) for commands that have a target project dir. Anonymous-lane sends strip both keys. */ export declare function cloudProjectProps(root: string | undefined): Promise>; /** * Run a CLI command body with one `command_run` telemetry row: ok is the * exit code (0 = true), a throw records the error class and rethrows, and a * body can name the step it failed at via the mutable `failure` argument. * The body also receives the telemetry client for extra events. Telemetry * NEVER changes command behavior or exit codes — the client never throws, * and this wrapper's own prop assembly is guarded too. */ export declare function withCommandRun(input: { command: CommandName; telemetry?: TelemetryOptions; /** Host project dir for the cloud lane's projectName/repoHost; omitted for commands without a target project (mcp). */ root?: string; }, body: (failure: { failedStep?: string; }, telemetry: Telemetry) => Promise): Promise; /** Windows' `start` is a cmd built-in, not an executable — execFile can only * reach it through `cmd /c start "" ` (the empty string is the window * title, so a URL is never mistaken for one). */ export declare function browserOpenCommand(platform: NodeJS.Platform, url: string): { command: string; args: string[]; }; /** Lockfile-derived package manager for `run dev` (doctor's probe starter). */ export declare function detectPackageManager(root: string): Promise<"pnpm" | "yarn" | "bun" | "npm">; /** Where init scaffolds app/api/vendo/[...vendo] and (for a fresh scaffold) the app-router layout wrap. Next hard-fails ("pages and app directories should be under the same folder") when app/ and pages/ sit at different bases, so a host whose pages router already lives under src/ must get its NEW app/ segment there too, mirroring detectRouter's src/pages signal — even before any src/app exists to detect directly. This still hands a pure-Pages host an App-Router route segment by design (valid in Next as long as both share one base); whether pages-native hosts deserve a pages/api scaffold instead is a separate, unaddressed question. */ export declare function appDirectory(root: string): Promise; /** The file whose client root the paste belongs in, and the child expression it wraps there. The app router's ROOT layout is whichever layout sits shallowest — an i18n or route-group host (`app/[locale]/`, `app/(shop)/`) has no app/layout.tsx at all, so a literal app/layout.tsx probe named a file that does not exist and told the user to create a SECOND root layout, which is how you break such a host rather than mount in it. Shallowest wins, lexicographic on a tie (walk() sorts, and sort is stable). A pages-only host has no layout to wrap — its client root is pages/_app.tsx, and the paste mounts there unchanged. (Where the API route segment gets scaffolded is a separate, deliberate choice — see appDirectory.) Keyed on the layout FILE, not on a router probe: the scaffold creates app/ mid-run, and the answer must be the same before and after it. The conventional app/layout.tsx survives only as the last resort — a host with no layout and no pages/ has no client root yet, and that is where Next wants the one it must create. Shared with doctor on purpose: init tells the user which file to paste into and doctor grades whether they did. Two copies of this rule meant doctor failed every pages-only host forever, naming a file init never mentioned. */ export declare function clientRoot(root: string): Promise<{ file: string; children: string; }>;