import { type Cli } from "./clis.js"; import type { PlanContext } from "./plan.js"; import type { Prompter } from "./prompt.js"; /** * Best-effort presence detection for each AI CLI: a home-relative config dir, or * a binary on PATH (probed through the Runner seam, so tests stay hermetic). * Signals are conservative — present is high-signal, absent just means "not found * here", never an error. The config dirs / binaries come from {@link entry} (the * single CLI registry), so they can't drift from the rest of the per-CLI facts. */ export interface CliPresence { cli: Cli; present: boolean; /** How it was detected, when present. */ via?: "config" | "binary"; /** The matching config dir or binary name. */ detail?: string; } /** The user's home directory — from the injected env first (testable), then the OS. */ export declare function homeDir(ctx: PlanContext): string; /** Detect one CLI: config dir wins (cheap, deterministic), else a PATH probe. */ export declare function detectOne(ctx: PlanContext, cli: Cli): Promise; /** Detect every supported CLI (presence + how), in canonical order. */ export declare function detectClis(ctx: PlanContext): Promise; /** * A CLI's two install signals checked INDEPENDENTLY (unlike {@link detectOne}, * which short-circuits on the first hit). A config dir alone is a weak signal — it * survives an uninstall — so an honest inventory needs to know whether the binary * is ALSO on PATH ("runnable") vs only a (possibly stale) config dir remaining. */ export interface CliInstall { cli: Cli; /** A home config dir exists (weak: a leftover dir survives an uninstall). */ config: boolean; /** A registry binary resolves on PATH (strong: the tool is actually runnable). */ binary: boolean; /** The matching config dir (`~/…`), when `config`. */ configDetail?: string; /** The matching binary name, when `binary`. */ binaryDetail?: string; } /** * Per-CLI install signals with config AND PATH checked separately, so a caller can * tell a runnable install (binary on PATH) from a config dir that may just be a * leftover. Async — one PATH probe per binary through the Runner seam. */ export declare function detectInstall(ctx: PlanContext): Promise; /** * Config-dir-only presence (synchronous, no PATH probe), in canonical order. For * read-only inventories like `aih report`, where spawning a `which`/`where` per * binary isn't worth it — reuses the same {@link SIGNALS} config dirs. */ export declare function detectClisByConfig(ctx: PlanContext): CliPresence[]; /** The CLIs detected as present, in canonical order. */ export declare function presentClis(presences: CliPresence[]): Cli[]; export interface TargetResolution { /** The CLIs to act on. */ clis: Cli[]; /** True when `--detect` found nothing and the result fell back to `claude`. */ detectFellBack: boolean; /** * True when NOTHING selected the targets — no injected `ctx.targets`, no * `--cli`/`--all-tools`/`--detect`, and no marker targets — so the result is the * bare `claude` default. Callers use it to warn before narrowing past canon the * repo already has; an expressed selection is never second-guessed. */ bareDefault: boolean; } /** * Show the auto-detected CLIs and let the user confirm or edit the list before the * harness installs anything. Bare Enter accepts the detected set; typing a * comma-separated list replaces it (add/remove tools). Reuses {@link resolveClis} * for parsing + validation, so unknown names are dropped with the same rules. * Returns the final list (possibly empty when nothing was detected and the user * skipped — the caller then falls back to `claude`). */ export declare function confirmDetectedClis(prompter: Prompter, detected: Cli[], configOnly?: Cli[]): Promise; export declare function resolveTargets(ctx: PlanContext): Promise; /** * The root bootloader files present in the repo that `clis` will NOT regenerate. * * Reported as FILES, not tool names: `AGENTS.md` is the bootloader for codex, * antigravity, opencode, zed AND kimi, so naming tools would invent an intent the * repo never expressed. Paths come from the same CLI registry that drives * generation, so this can't drift from what a run actually writes. */ export declare function unmanagedBootloaders(root: string, clis: readonly Cli[]): string[]; /** * The notice emitted when a bare run's `claude` default leaves bootloaders the repo * already has unregenerated — and therefore outside the `--verify` drift gate, which * only probes the resolved targets. Silent narrowing is the one sharp edge of the * deterministic default, so it is stated rather than inferred. */ export declare function bareDefaultNarrowingNotice(paths: readonly string[]): string; /** Back-compat thin wrapper for callers that only need the CLI list. */ export declare function resolveTargetClis(ctx: PlanContext): Promise; /** * Whether a leaf phase should emit `cli`-specific files. Under `aih init` the * orchestrator pre-resolves the target set into {@link PlanContext.targets}, so a * phase emits a tool's files only when that tool is targeted — e.g. on a Kiro-only * `aih init --detect` neither `.cursor/*` (cursor) nor `.claude/*` (claude) is * written. Run standalone (no `ctx.targets`), the leaf keeps its single-tool * identity and always emits: `aih profile` is the Cursor profiler, `aih * secrets`/`aih sandbox` the Claude guards. */ export declare function isTargeted(ctx: PlanContext, cli: Cli): boolean; /** The notice emitted when `--detect` found no AI CLIs and defaulted to claude. */ export declare function detectFallbackNotice(): string;