import type { WorkOrder } from "./prompts.js"; /** One leftover of the example app found by the bootstrap sweep. */ export interface Leftover { /** Repo-relative path of the file carrying the leftover. */ file: string; /** What the leftover is and why it does not belong in a fresh project. */ description: string; } /** * The bootstrap role: file operations plus the three judgment steps of * turning the template into an empty project. The deterministic methods * take no WorkOrder (they are executors over the BootstrapRules); the * model-performing methods receive their instructions as order.prompt, * like every other role. */ export interface Bootstrapper { /** Copy the template tree into the target, skipping the excludes at any depth. */ copyTemplate(examplePath: string, targetDir: string, excludes: readonly string[]): Promise; /** * Remove a directory's SOURCE content but keep the directory, its * package scaffolding (package.json, tsconfig, runner configs), and * any file matching a `keep` pattern (infrastructure living among the * example app's files) — so the architecture keeps its shape while * the app disappears. */ emptyDirectory(targetDir: string, relativeDir: string, keep?: readonly string[]): Promise; /** Remove a path entirely, scaffolding included. */ removePath(targetDir: string, relativePath: string): Promise; /** List every .feature file in the tree, repo-relative. */ listFeatureFiles(targetDir: string, featuresDir: string): Promise; /** * Delete the paths a feature owns BY CONVENTION (derived from its * name — no judgment). Paths that do not exist are skipped: a feature * has an acceptance test only for the clients it targets. */ removeConventionalPaths(targetDir: string, paths: readonly string[]): Promise; /** * Remove what is left of a BATCH of features' vertical slices after * their conventional paths are gone: the screens or pages that carry * their names differently, their DSL methods and selector constants, * their fake and real backend operations, their contract entries, and * any domain, repository or SQL code that exists ONLY for them. Code * shared with a KEPT feature stays — the kept features' suites must * still pass. * * A BATCH rather than one feature at a time, because the slices meet * in the same shared files (the DSL, the selectors, the route indexes, * the resolver, the specs, both clients' backends): removing five * features' methods from a file already in context costs barely more * than removing one, and "is this exclusive?" gets EASIER to answer * the more of the removed set a session can see at once. */ removeFeatureRemainders(order: WorkOrder, targetDir: string, batch: { names: readonly string[]; keptFeatures: readonly string[]; }): Promise; /** * Rewrite the project's IDENTITY for the new name: package names, * titles, the global configuration's app fields and module * registrations (pruning entries that point at stripped modules), and * the shared OpenAPI spec reduced to a valid empty skeleton. */ applyProjectIdentity(order: WorkOrder, targetDir: string, projectName: string, identityFiles: readonly string[]): Promise; /** * One reconciliation fix: the typecheck is red because shared wiring * (Application, UseCaseResolver, route indexes, fake backends, DSL * exports) still references stripped code — remove the dangling * reference the output names. Never re-create stripped code to satisfy * a reference; the reference goes, not the emptiness. */ fixDanglingReference(order: WorkOrder, targetDir: string, typecheckOutput: string): Promise; /** * Fresh-eyes sweep over the bootstrapped tree: every remaining trace * of the example app (its vocabulary, dead registrations, orphaned * catalogs and selectors, config entries for stripped modules). Empty * means the project is clean. `alreadyHandled` carries every leftover * fixed in earlier rounds, so a re-sweep reports only NEW problems — * without it, fresh eyes over a large tree keep noticing different * things and the loop never converges. */ sweepForLeftovers(order: WorkOrder, targetDir: string, alreadyHandled: readonly Leftover[]): Promise; /** * Remove EVERY leftover of one sweep round in a single session — they * share one tree and often one cause, and per-leftover sessions pay * the whole context price once per item. */ fixLeftovers(order: WorkOrder, targetDir: string, leftovers: readonly Leftover[]): Promise; }