import type { Prompter } from "./confirm.js"; import type { ScaffoldInputs } from "./index.js"; import type { ScaffoldFile } from "./scaffolders/common.js"; import { type StandardDecision, type StandardKey } from "./standards.js"; import { type ReadTemplateOptions } from "./templates.js"; export interface WriteScaffoldOptions { /** When true, print a file list to stdout and skip disk writes. */ dryRun?: boolean; /** * In dry-run mode, also dump the full content of every file (the pre-v0.2.1 * default). When false (the new default), dry-run prints only the file list * with byte sizes — the 2026-06-03 dogfood finding #5, where a full-content * dump buried the standards summary + next-actions in hundreds of lines. */ showContents?: boolean; /** When true (default), refuse to write into a non-empty target directory. */ refuseNonEmpty?: boolean; /** Forwarded to {@link resolveAgentsTemplate}; tests pin this at a sandbox. */ templatesPath?: string; /** Overrides for the Templates resolver (onWarn). Tests. */ templatesOverrides?: Omit; /** When true, run the interactive (a) inline / (b) promote dialog per standard. */ promoteStandards?: boolean; /** Forwarded to {@link resolveStandards} + {@link scaffoldByType}; date pin. */ date?: string; /** Required when `promoteStandards: true`; ignored otherwise. */ prompter?: Prompter; /** When true, layer the Pro SEO baseline extension on top of nextjs/cf-pages scaffolds. v0.3.0 paid feature. */ withProSeo?: boolean; /** Where dry-run output + promotion-dialog narration are written. */ output?: NodeJS.WritableStream; } export type PromotionChoice = "inline" | "promote"; export interface PromotionDecision { key: StandardKey; title: string; choice: PromotionChoice; } export interface WriteScaffoldResult { /** The composed file list. In dry-run mode these are the files that WOULD have been written. */ filesWritten: ScaffoldFile[]; /** Decisions from the standards resolver. */ decisions: StandardDecision[]; /** Per-standard promote/inline choices from the dialog; empty when --promote-standards is off. */ promotions: PromotionDecision[]; /** Resolved absolute target path. */ targetPath: string; /** Mirrors `opts.dryRun`. */ dryRun: boolean; /** * What the audience gate did — `undefined` for `audience="internal"`, where * the gate is a byte-for-byte no-op and there is nothing to report. * * 🔴 **This exists because the warning had no receiver on the path that * matters.** The dangling-reference finding was written to `output` only, * and `previewScaffold()` — the entry point a web UI uses, and the one * `project-creator` runs for every outside-customer scaffold — pipes `output` * into a discarding sink by design, so it can return files as data instead of * printing. The result was a report nobody could receive: the gate correctly * refused to auto-edit the canonical body, said so, and said it into * `/dev/null`. Returning it as DATA is what makes "report, never auto-edit" * true for a programmatic consumer rather than only for a terminal. */ audienceGate?: AudienceGateReport; } /** * The audience gate's findings, returned as data so a programmatic consumer can * surface them. Mirrors the fields of `AudienceGateResult` that a caller can * act on; the gated content itself is already in `filesWritten`. */ export interface AudienceGateReport { /** `## ` headings actually removed, in document order. */ readonly dropped: readonly string[]; /** Bytes removed from `CLAUDE.md`. */ readonly bytesDropped: number; /** * Surviving lines that still point at a dropped section. Non-empty means the * emitted `CLAUDE.md` is internally incoherent in a way a human should see — * NOT that the scaffold failed. A caller that ignores this ships a customer a * file referring to sections it does not contain. */ readonly danglingReferences: readonly { readonly heading: string; readonly line: number; }[]; /** * Surviving lines that INSTRUCT the reader into a private Z2W resource — a * worse class than a dangling reference, because the reader is being told to * go do something in a library they cannot open. See * `AudienceGateResult.privateReferences` in src/audience.ts for the full note. * * Reported, never auto-edited: the sentence lives inside the canonical AGENTS * body, and the durable fix is upstream in `cursor-project-templates`. */ readonly privateReferences: readonly { readonly resource: string; readonly line: number; readonly text: string; }[]; } export declare class WriterError extends Error { constructor(message: string); } /** * Compose the Step 4 / Step 5 / Templates layers and write the result. * * Pure filesystem effects are concentrated here so the earlier steps stay * testable in isolation. See module-level comment for the full pipeline. */ export declare function writeScaffold(inputs: ScaffoldInputs, targetPath: string, opts?: WriteScaffoldOptions): Promise; /** * Parse a JSON string into a {@link ScaffoldInputs}. Lightweight validation — * checks every required field is present with the right primitive type. The * downstream layers (`scaffoldByType`, `resolveStandards`, `writeScaffold`) * surface clearer errors for unknown enum values, so we don't re-validate * those here. */ export declare function parseScaffoldInputs(json: string): ScaffoldInputs; /** * Read the `--input ` value. `-` reads from stdin; anything else is a * file path. Returns the parsed inputs. */ export declare function loadScaffoldInputs(source: string, stdin?: NodeJS.ReadableStream): Promise;