/** * Pi user-level plugins doctor (TOOLING-002L / PB-010 / pi-plugin-doctor). * Pure classify + format; I/O helpers injectable for tests. */ export type PluginTier = "required" | "recommended_a" | "recommended_b" | "forbidden"; export interface ManifestPlugin { id: string; package: string; version?: string; tier?: PluginTier | string; severity?: "conflict" | "forbidden" | string; reason?: string; } export interface PiPluginsManifest { version: number; scope: string; source_protocol: string; install_template: string; required: ManifestPlugin[]; recommended_a: ManifestPlugin[]; recommended_b: ManifestPlugin[]; forbidden: ManifestPlugin[]; } export interface InstalledPackage { /** bare or scoped name without version */ name: string; version?: string; raw?: string; } export type DetectSource = "pi_list" | "settings" | "none" | "injected"; export interface DoctorClassifyInput { manifest: PiPluginsManifest; installed: InstalledPackage[]; piCliAvailable: boolean; detectSource: DetectSource; } export interface DoctorFinding { package: string; version?: string; tier: string; kind: "missing_required" | "missing_recommended" | "conflict" | "forbidden_present" | "ok_required"; installCmd?: string; reason?: string; } export interface DoctorReport { piCliAvailable: boolean; detectSource: DetectSource; installedNames: string[]; missingRequired: DoctorFinding[]; missingRecommendedA: DoctorFinding[]; missingRecommendedB: DoctorFinding[]; conflicts: DoctorFinding[]; forbiddenPresent: DoctorFinding[]; okRequired: DoctorFinding[]; /** true when required all present and no conflicts */ ok: boolean; /** CLI doctor exit should be non-zero when !ok or pi missing for strict mode */ exitCode: number; } export declare const DEFAULT_MANIFEST: PiPluginsManifest; /** Normalize package name for set membership (lowercase, strip version suffix). */ export declare function normalizePackageName(raw: string): string; export declare function installCommandFor(plugin: ManifestPlugin, template?: string): string; /** * Loose parse of `pi list` / package listing text. * Accepts lines like: * - @scope/pkg@1.2.0 * - npm:@scope/pkg@1.2.0 * - pi-powerbar 0.1.0 * - "name": "@scope/pkg" * Skips URLs, filesystem paths, and section headers ("User packages:"). */ export declare function parseInstalledFromText(text: string): InstalledPackage[]; export declare function parseInstalledFromSettings(settingsJson: unknown): InstalledPackage[]; /** * Classify installed packages against manifest. */ export declare function classifyPiPlugins(input: DoctorClassifyInput): DoctorReport; /** * Forbidden/conflict package detection — exact normalized-name match. */ export declare function isForbiddenPresent(set: Set, forbiddenPackage: string): boolean; export declare function formatDoctorReport(report: DoctorReport, opts?: { header?: string; }): string; export declare function loadManifestFromSpec(): Promise; export declare function normalizeManifest(data: Partial | null | undefined): PiPluginsManifest; export declare function detectInstalledPackages(opts?: { homeDir?: string; runPiList?: () => Promise<{ ok: boolean; text: string; }>; }): Promise<{ installed: InstalledPackage[]; piCliAvailable: boolean; detectSource: DetectSource; }>; export declare function runPiPluginsDoctor(opts?: { homeDir?: string; manifest?: PiPluginsManifest; installed?: InstalledPackage[]; runPiList?: () => Promise<{ ok: boolean; text: string; }>; }): Promise<{ report: DoctorReport; text: string; }>; /** Official Pi agent package (global CLI `pi`). Not auto-installed by AIWS. */ export declare const PI_CODING_AGENT_PACKAGE = "@mariozechner/pi-coding-agent"; /** Copy-paste install hints when `pi` is missing (detect only; never run by default). */ export declare function formatPiCliInstallHints(): string; /** * Detect whether Pi CLI is runnable. Inject `probe` for tests. * Default: `pi --version` (or empty argv that succeeds on some builds). */ export declare function checkPiCliAvailable(opts?: { probe?: () => Promise<{ ok: boolean; }>; }): Promise; export interface InstallRequiredResult { attempted: string[]; results: Array<{ cmd: string; code: number; stderr: string; package?: string; }>; /** True when install was not attempted because Pi CLI is missing. */ piCliMissing: boolean; /** Manifest packages that were missing when the install run started. */ missing: string[]; /** Manifest packages already present and therefore not installed. */ alreadyInstalled: string[]; message?: string; } /** Default per-package install timeout (ms). Heavy packages like pi-mcp-adapter can hang npm. */ export declare const DEFAULT_PI_INSTALL_TIMEOUT_MS = 180000; /** Maximum amount of command output persisted in an install result/evidence file. */ export declare const MAX_PI_INSTALL_STDERR_LENGTH = 2000; export declare function redactPiPluginInstallOutput(value: string, maxLength?: number): string; /** * Packages AIWS may install. The allowlist is deliberately derived only from * manifest `required`, `recommended_a`, and `recommended_b` tiers; forbidden * entries and arbitrary installed packages never enter this list. */ export declare function listInstallablePiPlugins(manifest: PiPluginsManifest): ManifestPlugin[]; /** Return only allowlisted manifest packages absent from the detected install set. */ export declare function listMissingInstallablePiPlugins(manifest: PiPluginsManifest, installed?: InstalledPackage[]): ManifestPlugin[]; /** * Install only missing AIWS-declared Pi plugins (required + recommended A/B, * user scope). The operation is sequential and best-effort: one package * failure is recorded and does not prevent later packages from being tried. * * `installed` should normally come from `runPiPluginsDoctor`; when omitted we * perform the same local detection so direct callers still get missing-only * semantics. No package is installed when the allowlist is already satisfied. */ export declare function installRequiredPiPlugins(opts?: { manifest?: PiPluginsManifest; installed?: InstalledPackage[]; homeDir?: string; runInstall?: (args: string[]) => Promise<{ code: number; stdout: string; stderr: string; }>; /** Inject for tests; default uses checkPiCliAvailable. */ checkPi?: () => Promise; /** Per-package timeout for default runCommand path; ignored when runInstall is injected. */ installTimeoutMs?: number; }): Promise; /** @deprecated alias — preserves the historical explicit-install API. */ export declare const installDeclaredPiPlugins: typeof installRequiredPiPlugins;