/** * Read-only adapter-hook wiring inspection — the inverse of `harn init`'s * writer (commands/init.ts `wireHooks`). Compares what `init` would wire * (ADAPTER_SPECS) against what's actually present in a project's adapter * settings file, so `harn doctor` and the SessionStart nudge can tell an agent * when a harnery upgrade changed the hook set but the project hasn't been * re-wired yet. * * The shared types + matcher live here (not in init.ts) so the writer, the * doctor check, and the session-start renderer all agree on what "wired" means * — there's exactly one definition of the `agent-hook ` match. */ import { type AdapterId, type AdapterSpec, type HookEntryShape, type HookEvent } from "./events.js"; /** Claude Code + Codex entry: `{ hooks: [{ type, command }] }`. */ export interface ClaudeHookGroup { matcher?: string; hooks: { type: string; command: string; }[]; } /** Cursor entry: a flat `{ command }`. */ export interface CursorHookGroup { command: string; type?: string; matcher?: string; } export type HookGroup = ClaudeHookGroup | CursorHookGroup; export interface SettingsFile { version?: number; description?: string; hooks?: Record; [k: string]: unknown; } /** Build a hook entry in the adapter's shape. */ export declare function makeEntry(shape: HookEntryShape, command: string): HookGroup; /** Pull every command string out of a hook entry, regardless of shape. */ export declare function groupCommands(group: HookGroup): string[]; /** * Whether a hook command string wires the given agent-hook subcommand. The * token boundary keeps `stop` from matching `stop-failure`, while accepting a * minimal command that ends immediately after the subcommand. */ export declare function commandWiresSubcommand(command: string, subcommand: string): boolean; /** Whether a command invokes Harnery's agent-hook binary at all. */ export declare function isAgentHookCommand(command: string): boolean; /** The canonical command installed for one adapter event. */ export declare function hookCommand(spec: AdapterSpec, agentHookPath: string, subcommand: string, adapter: AdapterId): string; /** * Pick a committed hook target that survives cloning the consumer elsewhere. * Projects that contain Harnery can use its repo-relative launcher. Standalone * consumers use the package's installed `agent-hook` executable from PATH * instead of recording a relative path back into one developer's package tree. */ export declare function agentHookPathForProject(projectRoot: string, packageRoot: string): string; export interface WiringDiff { /** Spec events not wired in the settings file. */ missing: HookEvent[]; /** Spec events already wired. */ present: HookEvent[]; /** * agent-hook subcommands wired in the file that are NOT in the current spec * (e.g. an event renamed/removed by an upgrade). Re-init removes these, so * they are surfaced separately from missing hooks. */ orphans: string[]; /** Spec events wired more than once under their correct event key. */ duplicates: HookEvent[]; /** Spec events wired under an event key other than the canonical one. */ misplaced: HookEvent[]; /** Spec events whose correct-key command differs from init's canonical command. */ stale: HookEvent[]; /** Settings fields rejected by a adapter with a strict top-level schema. */ invalidTopLevelKeys: string[]; /** Hook event names rejected by a adapter with a strict event schema. */ invalidEventKeys: string[]; } /** * Pure diff of one settings object against one adapter spec. Read-only inverse * of `wireHooks`; no fs, so it's unit-testable. */ export declare function diffWiring(settings: SettingsFile, spec: AdapterSpec, expected?: { agentHookPath: string; adapter: AdapterId; }): WiringDiff; export interface AdapterWiringStatus { adapter: AdapterId; /** Settings file path, relative to the project root. */ settingsFile: string; missing: HookEvent[]; orphans: string[]; duplicates: HookEvent[]; misplaced: HookEvent[]; stale: HookEvent[]; invalidTopLevelKeys: string[]; invalidEventKeys: string[]; parseError?: string; } /** * Inspect every adapter whose settings file exists under `projectRoot` and * return only those with *drift*. Read-only; never writes. * * Drift is reported only for a adapter the project has **already opted into** — * i.e. at least one harnery hook is already wired. A settings file with zero * harnery hooks just means this adapter isn't harnery-wired here (a bare * `.claude/settings.json` is a generic Claude Code file); that's `harn init`'s * job to surface on first run, not drift to nag about every session. A adapter * with no settings file at all, or an unparseable one, is skipped. */ export declare function loadAdapterWiring(projectRoot: string): AdapterWiringStatus[]; export interface AdapterWiringSummary { /** Adapters carrying at least one harnery hook. */ wired: AdapterId[]; /** * Adapters carrying none: no settings file at all, or a settings file with * none of ours in it. An unparseable file lands in neither list, since * `loadAdapterWiring` already reports that as drift. */ unwired: AdapterId[]; } /** * Classify every known adapter as wired or unwired, without judging whether an * unwired one is a problem. * * `loadAdapterWiring` stays deliberately silent about an adapter with zero * harnery hooks, because a bare settings file is not an opt-out signal and * nagging about it would false-warn every session. That silence is right in * isolation and wrong once you also know the adapter's CLI is installed: then * an agent can start a session here and register nothing. Splitting the fact * from the judgement lets a caller holding both (doctor) tell "not used here" * from "never wired" without changing what counts as drift. */ export declare function summarizeAdapterWiring(projectRoot: string): AdapterWiringSummary; /** * Resolve the harnery package version for context in nudges/checks. Walks up * from this module to the package root (works under Bun from `src/` and Node * from `dist/`). Returns "" if unresolved — callers omit it from the message. */ export declare function harneryVersion(): string; /** Resolve the package root from either src/ (Bun) or dist/ (Node). */ export declare function harneryPackageRoot(): string | null; //# sourceMappingURL=wiring.d.ts.map