import type { LoadedConfig } from "../config/load.js"; import type { Catalogue } from "../routing/catalogue.js"; import type { Router } from "../routing/dispatch.js"; import { dirtyWorktreeWarning, receiptGuard } from "./receipt-guard.js"; import { mergeGuard } from "./merge-guard.js"; import type { ExtensionSurface, InvocationContext, ModelRef, RoutingControls } from "../routing/types.js"; import type { HintStore } from "../routing/state.js"; /** * The Pi-free half of the extension entry (SPEC "Command surface (api)"). * * Given something that can register commands and a surface shaped like * `ExtensionAPI`, this wires one command per bundled public skill plus the * settings console, and returns the router that owns model lifecycle. Every * behaviour in AC3–AC12 is reachable from here, so `index.ts` stays a thin * adapter over Pi's real objects and nothing has to be tested against a live * session. `M` is the session model type — Pi's `Model` in production, a plain * `{provider, id}` in tests. */ export interface CommandRegistrar { registerCommand(name: string, options: { description?: string; handler: (args: string, ctx: InvocationContext) => void | Promise; }): void; } /** What the settings command presents (SPEC S4): the console, over the same files. */ export type SettingsHandler = (input: { catalogue: Catalogue; ctx: InvocationContext; /** Lets the console show and release a latch the operator can otherwise wait on. */ routing: RoutingControls; }) => unknown; export interface ExtensionDeps { registrar: CommandRegistrar; /** Pi's API is session-bound, so the router resolves the surface per call. */ surface: (ctx: InvocationContext) => ExtensionSurface; /** Directory holding the bundled `skills/` tree. */ skillsDir: string; /** Global agent directory, from Pi's `getAgentDir()`. */ agentDir: string; hint: HintStore; settings: SettingsHandler; /** Overridable so a test can hand the router an in-memory configuration. */ loadConfig?: (ctx: InvocationContext) => LoadedConfig; /** P4: profile demotion state store (optional — defaults to file-backed). */ profileState?: import("../routing/state.js").ProfileStateStore; /** * Feature 67 D5/AC9: the Serena readiness nudge. Consulted at the first * workflow-command dispatch, so a session that never starts through * `session_start` still hears it once. */ serenaReadiness?: import("../routing/serena-readiness.js").SerenaReadinessGate; } export interface ExtensionHandle { router: Router; catalogue: Catalogue; /** * The Pi-free predicates the adapter (`index.ts`) binds to Pi's lifecycle * events: the inline-receipt block guard, the merge guard (owner-only * merges, issue #278), and the dirty-worktree notice. They travel with the * handle so the entry stays a thin translation layer and the decisions are * unit-tested with no session (issues #182, #278). */ guards: { receiptGuard: typeof receiptGuard; mergeGuard: typeof mergeGuard; dirtyWorktreeWarning: typeof dirtyWorktreeWarning; }; } export declare function createExtension(deps: ExtensionDeps): ExtensionHandle;