import type { IPrompter } from "../cli/prompts.js";
import { DetectorRegistry } from "../detection/index.js";
import type { ILogger } from "../logging/index.js";
import { MigrationRegistry } from "../migrations/index.js";
import { StrategyRegistry } from "../strategies/index.js";
import type { IBackupService } from "../transaction/index.js";
import type { LisaConfig, LisaResult } from "./config.js";
import type { IGitService } from "./git-service.js";
/**
 * Dependencies for Lisa operations
 */
export interface LisaDependencies {
    readonly logger: ILogger;
    readonly prompter: IPrompter;
    readonly backupService: IBackupService;
    readonly detectorRegistry: DetectorRegistry;
    readonly strategyRegistry: StrategyRegistry;
    readonly gitService: IGitService;
    readonly migrationRegistry: MigrationRegistry;
}
/**
 * Main Lisa orchestrator
 */
export declare class Lisa {
    private readonly config;
    private readonly deps;
    private counters;
    /**
     * Managed files this apply found out of date but could not update.
     *
     * Named so the summary can list them: a count alone tells an operator that
     * something did not land without telling them what, which is most of the way
     * back to the silence this exists to end.
     */
    private readonly stalePaths;
    /**
     * Lisa-owned artifacts this apply deliberately did not replace, each with the
     * reason spelled out for whoever reads the output.
     *
     * Full sentences rather than paths, because this is the one outcome an
     * operator cannot act on from the filename: "we left yours alone because it
     * defends something ours does not" is the entire message.
     */
    private readonly hostAheadNotes;
    private detectedTypes;
    private ignorePatterns;
    /**
     * Paths marked for deletion by any detected project type's deletions.json.
     * Used to skip creating files that would just be deleted moments later (e.g.,
     * CDK inherits jest.* files from typescript but its own deletions.json removes
     * them — without this gate, Lisa creates then immediately deletes the files,
     * causing ENOENT races for any concurrent linter/file-watcher).
     */
    private pendingDeletions;
    /**
     * Maps each relative path shipped by any type's `create-only/` tree to the
     * single type that "owns" it (most-specific wins). Computed once before
     * strategies run; consulted while processing `create-only` to suppress
     * parent-type sources for paths that a child type also ships.
     *
     * Motivating case: CDK projects detect as both `typescript` and `cdk`.
     * Both ship `.github/workflows/ci.yml` under their `create-only/` trees.
     * Without this map, typescript's bun-mode ci.yml runs first and creates
     * the file; cdk's npm-mode + determine_environment ci.yml then sees the
     * file already exists and is silently skipped — producing a CDK project
     * wired to use bun, which fails immediately in CI because CDK projects
     * have `engines.bun = "please-use-npm"` and only a `package-lock.json`.
     */
    private createOnlyOwnership;
    /**
     * Maps each relative path shipped by any type's `copy-overwrite/` tree to the
     * single type that "owns" it (most-specific wins). Computed once before
     * strategies run; consulted while processing `copy-overwrite` to suppress
     * parent-type sources for paths that a child type also ships.
     *
     * Why this exists — crash safety: `copy-overwrite` always overwrites, so the
     * per-type outer loop in `processConfigurations` naturally lets the child win
     * by writing the parent's version first and then overwriting it. But that
     * leaves a window in which the destination holds the parent (typescript)
     * version of an overlapping path (e.g. `tsconfig.json`, `eslint.config.ts`)
     * before the child (expo/cdk/nestjs) phase overwrites it. If the process is
     * killed in that window — which is exactly what bun's lifecycle-script
     * handling did during postinstall — the project is left with TypeScript
     * configs clobbering the child stack's (Expo getting the typescript
     * `include` glob in tsconfig.json instead of its own).
     * That was the bug fixed by removing the postinstall apply in #318.
     *
     * Resolving ownership up front and writing each path exactly once (by its
     * most-specific type) eliminates the intermediate write entirely: a kill at
     * any point leaves either the pre-existing file or the correct final file,
     * never the parent-clobbered intermediate. This is the symmetric counterpart
     * to `createOnlyOwnership` (which exists for the opposite reason — create-only
     * skips existing files, so without help the parent would win).
     */
    private copyOverwriteOwnership;
    /** Destination selected by the host project's projectRulesFile setting. */
    private projectLearningsFile;
    /** Whether the canonical all/create-only learnings source is registered. */
    private projectLearningsTemplateRegistered;
    /**
     * True when a populated legacy ledger exists and the new `.lisa/` destination
     * does not yet — the create-only strategy must NOT seed an empty ledger over
     * it, or the pending relocation would collide (both-exist no-clobber) and
     * strand the real entries at the raw-injected legacy path.
     */
    private suppressLearningsSeedForRelocation;
    private readonly separator;
    private readonly lisaignoreSuffix;
    /**
     * True when the destination is the Lisa source repo itself. A self-apply
     * skips deletions, template application, and postinstall injection while
     * still applying package.lisa.json dependency governance. See {@link isLisaSourceRepo}.
     */
    private selfApply;
    /**
     * Initialize Lisa orchestrator
     * @param config - Configuration for the apply operation
     * @param deps - Injected service dependencies
     */
    constructor(config: LisaConfig, deps: LisaDependencies);
    /**
     * Postinstall applies are intentionally narrow: they refresh governance
     * templates that must track dependency updates, but they must not regenerate
     * project-scoped agent surfaces. Agent trees are large, committed outputs and
     * are updated through an explicit `lisa apply` or cross-pollinate run. A
     * self-apply against the Lisa source repo also skips emit — those agent
     * surfaces are built from `plugins/src`, not regenerated by apply.
     * @returns True when the current apply should avoid agent-surface writes.
     */
    private shouldSkipAgentEmitDuringPostinstall;
    /**
     * Initialize services
     */
    private initServices;
    /**
     * Detect and confirm project types
     */
    private detectTypes;
    /**
     * Detect whether this apply targets the Lisa source repo itself and, if so,
     * announce the narrowed behavior. A self-apply skips deletions, template
     * application, and agent-surface emit, but still applies package.lisa.json
     * dependency governance so Lisa's own deps track the floors it forces
     * downstream. See {@link isLisaSourceRepo}.
     */
    private detectSelfApply;
    /**
     * Process all configurations
     */
    private processConfigurations;
    /**
     * Pre-compute the set of paths marked for deletion across "all" and every
     * detected project type. This runs before strategy processing so the copy
     * strategies can skip creating files that are about to be deleted anyway.
     *
     * Motivating case: CDK inherits typescript/create-only, which ships
     * `jest.config.local.ts` + `jest.thresholds.json`. CDK's own `deletions.json`
     * then removes them. Without this gate, Lisa would create them and delete
     * them in the same apply, which causes ENOENT errors for any concurrent
     * file-watcher/linter that observed the briefly-existing files.
     *
     * The `keep` list in a deletions.json (e.g., expo keeps
     * `zap-baseline-expo.yml`) is honored — paths in `keep` are not treated as
     * pending-deletions, so they will still be created by upstream strategies.
     * @returns Promise that resolves once pending deletions have been collected
     */
    private loadPendingDeletions;
    /**
     * Resolve the host-owned learnings destination before template ownership is
     * computed. The canonical source stays at its stable package path while its
     * destination follows the configured project-rules directory.
     */
    private loadProjectLearningsFile;
    /**
     * Pre-compute, for every relative path shipped by any type's `create-only/`
     * directory, which single type "owns" it. The owner is the most-specific
     * detected type — i.e., the last entry in [all, ...detectedTypes] (already
     * canonically ordered parent-before-child by `expandAndOrderTypes`).
     *
     * Why this is needed: the `create-only` strategy intentionally does nothing
     * when the destination file already exists. Combined with the per-type
     * outer loop in `processConfigurations`, that means the *first* type to
     * ship a `create-only/<path>` creates the file and every later type's
     * version is silently skipped. For paths that overlap between a parent
     * stack (typescript) and a child stack (cdk/expo/nestjs/npm-package), this
     * lets the parent win — the exact opposite of the rest of Lisa's
     * "child overrides parent" semantics. See `copy-overwrite` for comparison:
     * because it always overwrites, the per-type ordering naturally lets
     * children win. `create-only` needs explicit help.
     *
     * The fix: the orchestrator pre-computes this ownership map and, while
     * processing `create-only`, skips any source whose owning type isn't the
     * current one. The result is that exactly one source wins per path — the
     * most-specific detected type.
     * @returns Promise that resolves once ownership has been resolved
     */
    private loadCreateOnlyOwnership;
    /**
     * Pre-compute, for every relative path shipped by any type's `copy-overwrite/`
     * directory, which single type "owns" it — the most-specific detected type
     * (last entry in [all, ...detectedTypes], already ordered parent-before-child).
     *
     * Consumed by `shouldProcessFile` to skip non-owning sources so each path is
     * written exactly once, by its winning stack. See the `copyOverwriteOwnership`
     * field doc for the crash-safety rationale (eliminating the parent-then-child
     * overwrite window that left half-applied configs when the process was killed
     * mid-apply during postinstall — the bug behind #318).
     * @returns Promise that resolves once ownership has been resolved
     */
    private loadCopyOverwriteOwnership;
    /**
     * Read the set of paths a single type's deletions.json will delete,
     * filtered by the type's `keep` list. Returns an empty array when the file
     * is missing or malformed — a bad file cannot block strategy processing;
     * any validation errors are surfaced later by processDeletions itself.
     * @param type - Project type (e.g., "all", "cdk", "typescript")
     * @returns Array of relative paths marked for deletion
     */
    private readPendingDeletionsForType;
    /**
     * Process deletions from deletions.json files
     */
    private processDeletions;
    /**
     * Process a single deletions.json file
     * @param deletionsPath - Path to the deletions.json file
     */
    private processDeletionsFile;
    /**
     * Process a single deletion path
     * @param relativePath - Relative path to delete
     */
    private processSingleDeletion;
    /**
     * Invoke pre-strategy migration hooks so migrations can snapshot project state
     * before strategies overwrite or delete managed files.
     */
    private runMigrationsBeforeStrategies;
    /**
     * Run all applicable migrations against the destination project
     */
    private processMigrations;
    /**
     * Update migration counters from aggregated migration results
     * @param results - Migration results to aggregate into counters
     */
    private updateMigrationCounters;
    /**
     * Register plugins from merged settings.json with Claude Code at project scope
     */
    private registerPlugins;
    /**
     * Install each plugin and refresh the Lisa marketplace cache.
     *
     * Version-gated (perf): `claude plugin marketplace update` is a network git
     * pull of the Lisa repo and every `claude plugin install` spawns the Claude
     * CLI (seconds each); re-running all of it on every apply made a single
     * `bun add` cost minutes across the repeated apply passes. A full sync
     * (marketplace refresh + install every plugin) still runs whenever the
     * installed Lisa version differs from the `.claude/.lisa-plugins-synced`
     * marker; between version changes only plugins missing for this project are
     * installed, so the self-heal property (a removed plugin comes back on the
     * next install) survives at the cost of one `claude plugin list` call.
     * @param execAsync - Promisified exec function for running shell commands
     * @param plugins - Plugin identifiers from enabledPlugins (e.g. "lisa@lisa")
     */
    private installPluginsAndUpdateMarketplace;
    /**
     * Decide whether this apply needs a full plugin sync and which plugins to
     * install.
     *
     * Linked worktrees inherit the primary checkout's sync state: the marker is
     * gitignored (it records this machine's `~/.claude` plugin state), so a
     * fresh agent worktree is always marker-less and would otherwise pay the
     * full sync — marketplace pulls plus a Claude CLI spawn per plugin — on
     * every ticket. When the primary checkout is already synced for this Lisa
     * version, the worktree has nothing left to do: the marketplace cache is
     * machine-global and per-worktree plugin registration is handled by the
     * coding agent's own startup (Claude Code auto-installs the committed
     * `enabledPlugins` for a new projectPath; Codex uses the project-local
     * `.codex-plugin` pointer instead of Claude project-scope registrations).
     * The #320 defense (refresh marketplace whenever installs happened) holds:
     * the skip path performs no installs.
     * @param execAsync - Promisified exec function for running shell commands
     * @param plugins - Plugin identifiers from enabledPlugins
     * @returns Sync plan: fullSync (version changed), plugins to install, the
     *   current Lisa version, and whether destDir is a linked worktree
     */
    private computePluginSyncPlan;
    /**
     * Refresh the cached Lisa marketplace via the Claude CLI.
     * @param execAsync - Promisified exec function for running shell commands
     * @param announce - Whether to log the outcome (silent for the pre-install
     *   best-effort refresh)
     * @returns Promise that resolves when the refresh attempt completes
     */
    private updateLisaMarketplace;
    /**
     * Install a single plugin at project scope via the Claude CLI.
     * @param execAsync - Promisified exec function for running shell commands
     * @param plugin - Plugin identifier (already validated)
     * @returns Promise that resolves when the install attempt completes
     */
    private installPlugin;
    /**
     * Exec options shared by all Claude CLI plugin commands.
     * @returns Options object with the project cwd and POSIX shell
     */
    private pluginExecOptions;
    /**
     * Absolute path of the plugin sync marker recording the last fully-synced
     * Lisa version for this project. Shared with install-claude-plugins.sh.
     * @returns Marker path under the project's .claude directory
     */
    private pluginSyncMarkerPath;
    /**
     * Absolute path of the host project's package.json.
     * @returns package.json path under the destination directory
     */
    private hostPackageJsonPath;
    /**
     * Read the plugin ids already installed for this project via
     * `claude plugin list --json`.
     * @param execAsync - Promisified exec function for running shell commands
     * @returns Set of installed plugin ids, or null when the list cannot be
     *   obtained (older CLI, parse failure) so callers fall back to installing
     *   everything — the pre-existing behavior
     */
    private readInstalledPluginIds;
    /**
     * Record the Lisa version whose plugin set was last fully synced.
     *
     * Writes through `node:fs/promises`. `writeFile` is one of the `graceful-fs`
     * passthroughs Node's ESM loader does not expose on the `fs-extra` namespace,
     * so `fse.writeFile(...)` was `undefined` in the shipped `dist/` and threw a
     * `TypeError` into the catch below (#2487) — the marker was never written, and
     * the version gate it feeds never once took effect.
     *
     * Failure stays non-fatal, but is no longer silent. The catch now reports the
     * real error: a recovery path that says nothing cannot be distinguished from
     * one that never ran, which is precisely how this went unnoticed. The wording
     * matches the sibling best-effort plugin steps above.
     * @param version - Lisa package version to record
     * @returns Promise that resolves when the marker is written (best-effort)
     */
    private writePluginSyncMarker;
    /**
     * Finalize operation
     * @returns Promise that resolves when finalization is complete
     */
    private finalize;
    /**
     * Create success result
     * @returns Success result with operation counters and detected types
     */
    private getSuccessResult;
    /**
     * Handle apply error and rollback
     * @param error Error that occurred during apply
     * @returns Error result
     */
    private handleApplyError;
    /**
     * Apply Lisa configurations to the destination project
     * @returns Result of the apply operation
     */
    apply(): Promise<LisaResult>;
    /**
     * Sync lockfiles to package.json in-process when the postinstall trampoline
     * will not run.
     *
     * The trampoline only fires when Lisa is invoked as a package-manager
     * lifecycle script (postinstall, prepare, etc.). When users invoke Lisa
     * manually after `npm install -D @codyswann/lisa@latest` — the recommended
     * flow on plain-npm projects, where Lisa's postinstall is not auto-run —
     * the trampoline never schedules and any package.json changes Lisa just
     * made (e.g., adding `oxlint-tsgolint` via package.lisa.json merge) leave
     * package-lock.json out of sync. The next `npm ci` in CI then fails with
     * "lockfile out of sync".
     *
     * This method closes that gap by running the same lockfile-regeneration
     * commands the trampoline child would have run, but synchronously in the
     * current process. We skip when the trampoline IS scheduled (it will
     * regenerate on its own after the parent PM exits) and when running as the
     * trampoline child itself (the trampoline has its own regen step at the end).
     * @param prePackageJsonHash - Hash of package.json captured before apply ran (null if file did not exist)
     */
    private reconcileLockfilesInProcess;
    /**
     * Emit Codex-targeted artifacts (agents, hooks, settings) when the host
     * project's harness includes Codex (`codex` or `fleet`). No-op for `claude`
     * (default).
     *
     * Lisa writes the complete Codex surface and a filtered repository
     * marketplace into the host. No user-wide plugin activation participates.
     * Ownership is tracked via `.codex/.lisa-managed.json` so updates remove
     * stale stack artifacts without touching host customizations.
     */
    private processCodexEmit;
    /**
     * Emit Claude-Code-targeted artifacts when the harness includes Claude.
     *
     * Claude's primary distribution is the GitHub marketplace; the per-project
     * artifacts Lisa writes are the canonical `AGENTS.md` and a thin `CLAUDE.md`
     * that `@AGENTS.md`-imports it (Claude Code doesn't read AGENTS.md natively).
     * Both are create-only and host-owned from creation onward. Skipped in dry-run
     * mode and on harness modes that don't include Claude.
     */
    private processClaudeEmit;
    /**
     * Emit Antigravity-targeted artifacts when the harness includes agy.
     *
     * Runtime probes of agy 1.0.3 (ticket-1054) established how agy consumes each
     * surface, so each is delivered the way agy actually reads it:
     *   1. `agy plugin install` Lisa's variant from `plugins/lisa-agy/` (when agy
     *      is on PATH). This installs the variant into
     *      `~/.gemini/config/plugins/<variant>/`.
     *   2. Hooks → PLUGIN-BUNDLED. agy loads a plugin's hooks from a `hooks.json`
     *      at the installed plugin ROOT, so the hooks config + agy-protocol script
     *      are emitted into the variant at BUILD time by
     *      generate-agy-plugin-artifacts.mjs and ride along with the plugin
     *      install above — nothing is written here. Only `block-no-verify`
     *      (PreToolUse) maps; SessionStart hooks aren't supported by agy.
     *   3. MCP → USER-GLOBAL. agy ignores plugin-bundled MCP and only auto-loads
     *      `~/.gemini/config/mcp_config.json`, so `installAgyMcpConfig` writes
     *      there (servers collected from the built plugin `.mcp.json` files via
     *      `collectLisaMcpServers`, translated to agy's `serverUrl` shape,
     *      tagged-merge preserving host entries). Cross-project caveat: the most
     *      recent `lisa apply` carrying MCP wins globally.
     *   4. Instruction file → the canonical create-only `AGENTS.md` (the same file
     *      every other agent reads), plus a bounded project-learnings bridge
     *      reconciled by `processInstructionFilesMigration`. Lisa does not bake
     *      eager rule bodies into AGENTS.md: a giant generated file is not worth
     *      carrying just to polyfill agy's headless (`-p`) mode, where SessionStart
     *      hooks don't fire. In interactive mode it still receives Lisa's plugin
     *      (skills/agents/MCP) above.
     */
    private processAgyEmit;
    /**
     * Wire the official SonarQube MCP into each harness agent via the vendor's
     * `sonar integrate <agent>`.
     *
     * Hard-gated inside {@link installSonarIntegrations}: a no-op unless the project
     * opted in (`verification.sonar.enabled`) AND the `sonar` CLI is on PATH, so it
     * never touches a project that only wants the CI SonarCloud gate. Kiro and
     * Gemini are never wired — they are not Lisa-supported agents. Only the
     * apply-time emit agents with a vendor integrate path are wired here (claude,
     * codex, copilot, antigravity). Cursor (consumed natively, no apply-time emit)
     * and OpenCode (no vendor integrate path) are wired by `/lisa:setup:sonar`.
     */
    private processSonarIntegration;
    /**
     * Per-agent stack variant directory names for the detected project types
     * that have a built variant. Filters by on-disk existence so a detected type
     * without a stack plugin (e.g. `npm-package`) is naturally skipped.
     * @param pluginRoot - Absolute path to the `plugins/` directory.
     * @param agent - Per-agent variant suffix (`agy` | `copilot` | `cursor`).
     * @returns Variant directory names like `lisa-typescript-agy`.
     */
    private perAgentStackVariants;
    /**
     * Emit GitHub-Copilot-targeted artifacts when the harness includes copilot.
     *
     * Three per-project actions:
     *   1. `copilot plugin install lisa@CodySwannGT/lisa` (with local-path
     *      fallback when the marketplace path fails — currently does, pending
     *      the marketplace.json pluginRoot fix gated on Wave 2 spec step 8).
     *   2. Ensure the canonical `AGENTS.md` exists (Copilot reads it natively).
     *   3. Create-only write of `.github/copilot-instructions.md`.
     */
    private processCopilotEmit;
    /**
     * Emit OpenCode-targeted artifacts when the harness includes OpenCode.
     *
     * OpenCode reads the open Agent Skills format, native agents
     * (`.opencode/agents/`), native commands (`.opencode/commands/`), and
     * `AGENTS.md` natively (opencode.ai/docs/skills, /docs/agents, /docs/commands,
     * /docs/rules), so Lisa needs no transformed plugin variant. It writes:
     *   - skills (bundled + command-derived) into `.opencode/skills/lisa/`,
     *   - agents (Markdown, `lisa-` prefixed) into `.opencode/agents/`,
     *   - commands (Markdown, `lisa-` prefixed) into `.opencode/commands/`,
     * and ensures the canonical `AGENTS.md` exists. Ownership of skills/agents/
     * commands is tracked via `.opencode/.lisa-managed.json` so updates clean up
     * stale artifacts without touching host customizations. Skipped in dry-run
     * mode and on harness modes that don't include OpenCode.
     */
    private processOpencodeEmit;
    /**
     * Relocate a legacy `.claude/rules/PROJECT_LEARNINGS.md` ledger to the new
     * cold `.lisa/PROJECT_LEARNINGS.md` BEFORE the create-only strategy would
     * seed an empty ledger at the new path — otherwise both would exist and the
     * real entries would be stranded at the legacy location.
     *
     * Runs even on the postinstall/bootstrap skip path: that guard exists to
     * suppress agent-surface EMISSIONS, but moving machine-managed state under
     * `.lisa/` is not an agent emission and is safe headless. Skipping it there
     * was the exact defect that stranded populated legacy ledgers on the fleet
     * upgrade path (create-only still seeds an empty `.lisa/` ledger, so every
     * later run then hits the both-exist no-clobber warning). Only dry-run is
     * exempt. Idempotent; logs one line on a move and one warning when both exist.
     */
    private processLearningsRelocation;
    /**
     * Reconcile the project's agent instruction files onto Lisa's canonical
     * pattern after the per-harness emits run.
     *
     * The per-agent emits write `AGENTS.md` / `CLAUDE.md` create-only, so they
     * can't repair files that already exist from older Lisa versions. This step
     * runs the same non-destructive migration `lisa doctor` uses — stripping the
     * legacy agy baked-rules block from an existing `AGENTS.md` and adding the
     * `@AGENTS.md` import to an existing `CLAUDE.md`. Without it, updating an
     * existing project would create a canonical `AGENTS.md` that its stale
     * `CLAUDE.md` never imports.
     *
     * Harness-aware: a `CLAUDE.md` pointer is only *created* when the harness
     * includes Claude, so a codex/cursor/copilot/agy-only project never gets a
     * stray `CLAUDE.md`. An existing host-authored `CLAUDE.md` still gets the
     * import regardless. Skipped in dry-run mode.
     */
    private processInstructionFilesMigration;
    /**
     * Schedule a reconciliation re-run when Lisa is invoked as a package-manager
     * lifecycle script. Works around the fact that `bun add` (and similar
     * package-manager mutations) cache package.json in memory at the start of
     * the command and rewrite it at the end, clobbering any changes made by
     * postinstall scripts.
     *
     * The trampoline is always fully detached so the package manager can exit
     * normally; the child then detects the exit and re-runs Lisa. See
     * utils/postinstall-trampoline.ts for details.
     *
     * Skipped when this apply did not mutate package.json: the trampoline exists
     * solely to redo package.json changes that the parent package manager's
     * end-of-command rewrite clobbers (#383). With no mutation there is nothing
     * to clobber — the rewrite restores identical content — and scheduling a
     * detached re-apply (plus its lockfile regen) is pure waste. File-based
     * template writes are not clobbered and need no reconciliation.
     * @param prePackageJsonHash - Hash of package.json captured before apply mutated anything (null if the file did not exist)
     * @returns Promise that resolves immediately after the detached child is spawned
     */
    private schedulePostinstallReconciliation;
    /**
     * Validate compatibility without applying changes
     * @returns Result of the validate operation
     */
    validate(): Promise<LisaResult>;
    /**
     * Process all files for a given project type
     * @param type Project type to process
     */
    private processProjectType;
    /**
     * Process all files in a directory with the given strategy
     * @param srcDir Source directory
     * @param strategy Strategy to apply
     * @param strategy.name Strategy name
     * @param strategy.apply Apply function
     * @param currentType Project type (e.g. "all", "typescript", "cdk") supplying these files
     */
    private processDirectory;
    /**
     * Map the stable canonical learnings source to the project's configured
     * sibling path. No other template source is redirected.
     * @param sourceRelativePath - Path relative to the strategy source directory
     * @param strategyName - Strategy delivering the source file
     * @param currentType - Project type that owns the source directory
     * @returns Destination path relative to the host project
     */
    private resolveTemplateDestination;
    /**
     * Decide whether a candidate source file should be passed to its strategy.
     * Centralizes three independent skip rules that all need to short-circuit
     * before a strategy ever sees the file:
     *
     * 1. `.lisaignore` patterns from the destination project
     * 2. Pending deletions from any detected type's `deletions.json`
     *    (avoids the create-then-delete ENOENT race)
     * 3. Create-only ownership conflicts (parent stack ships a path that a
     *    child stack also ships under create-only — child wins)
     * 4. Cross-strategy ownership conflicts (a child stack ships a create-only
     *    path that a parent stack ships under copy-overwrite — child wins before
     *    the parent can overwrite a host-owned or soon-to-be-created child file)
     * 5. Copy-overwrite ownership conflicts (parent and child both ship a path
     *    under copy-overwrite — only the most-specific stack writes it, so there
     *    is no parent-then-child overwrite window; see copyOverwriteOwnership)
     * @param relativePath - Path relative to the strategy's source directory
     * @param strategyName - Name of the strategy that would apply the file
     * @param currentType - Project type currently being processed
     * @returns True if the strategy should apply the file, false to skip
     */
    private shouldProcessFile;
    /**
     * Emit a "would skip"/"skipped" log line in a single place so all the skip
     * branches in `shouldProcessFile` stay symmetric.
     * @param dryReason - Reason text for dry-run mode
     * @param relativePath - Path being skipped
     * @param applyMessage - Message to log when not in dry-run mode
     */
    private logSkip;
    /**
     * Handle overwrite prompt for conflicting files
     * @param relativePath Relative path of the file
     * @param sourcePath Source file path
     * @param destPath Destination file path
     * @returns True if user approves overwrite
     */
    private handleOverwritePrompt;
    /**
     * Show diff between two files
     * @param sourcePath Source file path
     * @param destPath Destination file path
     */
    private showDiff;
    /**
     * Update counters based on operation result
     * @param result Operation result to count
     */
    private updateCounters;
    /**
     * Log a message using appropriate logger method
     * @param dryMsg Message for dry run mode
     * @param successMsg Message for success mode
     */
    private logMessage;
    /**
     * Log operation result
     * @param result Result to log
     */
    private logResult;
    /**
     * Log an optional operation note.
     * @param result Operation result that may carry an operator-readable note
     */
    private logNote;
    /**
     * Validate destination directory exists and is a directory
     */
    private validateDestination;
    /**
     * Check for uncommitted git changes and prompt user if dirty
     */
    private validateGitState;
    /**
     * Print header banner
     */
    private printHeader;
    /**
     * Print summary header with mode
     */
    private printSummaryHeader;
    /**
     * Print a single stat line with color
     * @param label - Stat label
     * @param value - Stat value
     * @param color - Color function from picocolors
     * @param suffix - Optional suffix after the count
     */
    private printStatLine;
    /**
     * Print summary statistics for validate mode
     */
    private printValidateStats;
    /**
     * Print summary statistics for dry-run mode
     */
    private printDryRunStats;
    /**
     * Print summary statistics for apply mode
     */
    private printApplyStats;
    /**
     * Name the out-of-date managed files and say how to update them.
     *
     * A bare count is not enough to act on. An operator taking an upgrade needs
     * to see WHICH managed files the release changed and did not deliver —
     * particularly the enforcement guards, where the gap is a security control
     * that stayed on its old version while the release notes said otherwise.
     */
    private printStaleDetail;
    /**
     * Report Lisa-owned artifacts this apply chose not to replace.
     *
     * Reported as good news, not as a warning about the project: keeping a guard
     * that is stronger than Lisa's is the correct outcome, and phrasing it as a
     * problem would push an operator to "fix" it by discarding their own
     * hardening — the exact loss the check exists to prevent.
     */
    private printHostAheadDetail;
    /**
     * Print summary statistics
     */
    private printSummaryStats;
    /**
     * Print project types
     */
    private printProjectTypes;
    /**
     * Print validation result
     */
    private printValidationResult;
    /**
     * Print summary
     */
    private printSummary;
    /**
     * Read a file's contents, returning null if the file does not exist or cannot be read.
     * Used for optional file checks that should not interrupt the main flow on failure.
     * @param filePath - Absolute path to the file to read
     * @returns File contents as a string, or null if unavailable
     */
    private tryReadFile;
    /**
     * Print migration notices when the project's ci.yml or deploy.yml still reference
     * local quality/release workflows instead of the canonical Lisa repo versions.
     * Printed after the update summary so users see it as a post-update action item.
     * @param projectDir - The destination project directory to inspect
     */
    private printMigrationNotices;
}
//# sourceMappingURL=lisa.d.ts.map