@cleocode/caamp 2026.5.29 → 2026.5.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/logger.ts","../src/core/registry/detection.ts","../src/core/skills/installer.ts","../src/core/advanced/orchestration.ts","../src/core/paths/agents.ts","../src/core/config/caamp-config.ts","../src/core/harness/pi.ts","../src/core/harness/scope.ts","../src/core/harness/index.ts","../src/core/formats/utils.ts","../src/core/formats/json.ts","../src/core/formats/toml.ts","../src/core/formats/yaml.ts","../src/core/formats/index.ts","../src/core/mcp/reader.ts","../src/core/mcp/installer.ts","../src/core/mcp/remover.ts","../src/core/sources/parser.ts","../src/core/skills/audit/scanner.ts","../src/core/skills/audit/rules.ts","../src/core/skills/lock.ts","../src/core/lock-utils.ts","../src/core/network/fetch.ts","../src/core/marketplace/skillsmp.ts","../src/core/marketplace/skillssh.ts","../src/core/marketplace/client.ts","../src/core/skills/recommendation.ts","../src/core/skills/recommendation-api.ts","../src/core/skills/library-loader.ts","../src/core/skills/catalog.ts","../src/core/skills/discovery.ts","../src/core/skills/validator.ts"],"sourcesContent":["/**\n * Simple logger with verbose/quiet mode support.\n *\n * - verbose: enables debug output to stderr\n * - quiet: suppresses info and warn output (errors always shown)\n */\n\nlet verboseMode = false;\nlet quietMode = false;\nlet humanMode = false;\n\n/**\n * Enable or disable verbose (debug) logging mode.\n *\n * When enabled, debug messages are written to stderr.\n *\n * @param v - `true` to enable verbose mode, `false` to disable\n *\n * @remarks\n * Typically called once during CLI initialization based on the `--verbose` flag.\n *\n * @example\n * ```typescript\n * setVerbose(true);\n * ```\n *\n * @public\n */\nexport function setVerbose(v: boolean): void {\n verboseMode = v;\n}\n\n/**\n * Enable or disable quiet mode.\n *\n * When enabled, info and warning messages are suppressed. Errors are always shown.\n *\n * @param q - `true` to enable quiet mode, `false` to disable\n *\n * @remarks\n * Typically called once during CLI initialization based on the `--quiet` flag.\n *\n * @example\n * ```typescript\n * setQuiet(true);\n * ```\n *\n * @public\n */\nexport function setQuiet(q: boolean): void {\n quietMode = q;\n}\n\n/**\n * Log a debug message to stderr when verbose mode is enabled.\n *\n * @remarks\n * Messages are prefixed with `[debug]` and written to stderr. No output is\n * produced when verbose mode is disabled.\n *\n * @param args - Values to log (forwarded to `console.error`)\n *\n * @example\n * ```typescript\n * debug(\"Loading config from\", filePath);\n * ```\n *\n * @public\n */\nexport function debug(...args: unknown[]): void {\n if (verboseMode) console.error('[debug]', ...args);\n}\n\n/**\n * Log an informational message to stdout.\n *\n * @remarks\n * Output is suppressed when quiet mode is enabled.\n *\n * @param args - Values to log (forwarded to `console.log`)\n *\n * @example\n * ```typescript\n * info(\"Installed 3 skills\");\n * ```\n *\n * @public\n */\nexport function info(...args: unknown[]): void {\n if (!quietMode) console.log(...args);\n}\n\n/**\n * Log a warning message to stderr.\n *\n * @remarks\n * Output is suppressed when quiet mode is enabled.\n *\n * @param args - Values to log (forwarded to `console.warn`)\n *\n * @example\n * ```typescript\n * warn(\"Deprecated option used\");\n * ```\n *\n * @public\n */\nexport function warn(...args: unknown[]): void {\n if (!quietMode) console.warn(...args);\n}\n\n/**\n * Log an error message to stderr.\n *\n * @remarks\n * Errors are always displayed regardless of quiet mode.\n *\n * @param args - Values to log (forwarded to `console.error`)\n *\n * @example\n * ```typescript\n * error(\"Failed to install skill:\", err.message);\n * ```\n *\n * @public\n */\nexport function error(...args: unknown[]): void {\n console.error(...args);\n}\n\n/**\n * Check if verbose (debug) logging is currently enabled.\n *\n * @remarks\n * Useful for conditionally performing expensive formatting only when verbose\n * output is requested.\n *\n * @returns `true` if verbose mode is active\n *\n * @example\n * ```typescript\n * if (isVerbose()) {\n * console.error(\"Extra debug info\");\n * }\n * ```\n *\n * @public\n */\nexport function isVerbose(): boolean {\n return verboseMode;\n}\n\n/**\n * Check if quiet mode is currently enabled.\n *\n * @remarks\n * Commands use this to skip non-essential output when the user requested quiet\n * operation.\n *\n * @returns `true` if quiet mode is active\n *\n * @example\n * ```typescript\n * if (!isQuiet()) {\n * console.log(\"Status message\");\n * }\n * ```\n *\n * @public\n */\nexport function isQuiet(): boolean {\n return quietMode;\n}\n\n/**\n * Enable or disable human-readable output mode.\n *\n * When enabled, commands output human-readable format instead of JSON.\n *\n * @param h - `true` to enable human mode, `false` to disable\n *\n * @remarks\n * Typically called once during CLI initialization based on the `--human` flag.\n *\n * @example\n * ```typescript\n * setHuman(true);\n * ```\n *\n * @public\n */\nexport function setHuman(h: boolean): void {\n humanMode = h;\n}\n\n/**\n * Check if human-readable output mode is currently enabled.\n *\n * @remarks\n * Commands use this to decide between structured JSON output and\n * human-friendly formatted output.\n *\n * @returns `true` if human mode is active\n *\n * @example\n * ```typescript\n * if (isHuman()) {\n * console.log(\"Human readable output\");\n * } else {\n * console.log(JSON.stringify(data));\n * }\n * ```\n *\n * @public\n */\nexport function isHuman(): boolean {\n return humanMode;\n}\n","/**\n * Provider auto-detection engine\n *\n * Detects which AI coding agents are installed on the system\n * by checking binaries, directories, app bundles, and flatpak.\n */\n\nimport { execFileSync } from 'node:child_process';\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Provider } from '../../types.js';\nimport { debug } from '../logger.js';\nimport { getPlatformLocations, resolveProviderProjectPath } from '../paths/standard.js';\nimport { getAllProviders } from './providers.js';\n\n/**\n * Result of detecting whether a provider is installed on the system.\n *\n * @example\n * ```typescript\n * const provider = getProvider(\"claude-code\")!;\n * const result = detectProvider(provider);\n * if (result.installed) {\n * console.log(`Found via: ${result.methods.join(\", \")}`);\n * }\n * ```\n *\n * @public\n */\nexport interface DetectionResult {\n /** The provider that was checked. */\n provider: Provider;\n /** Whether the provider was detected as installed. */\n installed: boolean;\n /** Detection methods that matched (e.g. `[\"binary\", \"directory\"]`). */\n methods: string[];\n /** Whether the provider has project-level config in the current directory. */\n projectDetected: boolean;\n}\n\n/**\n * Options for controlling the detection result cache.\n *\n * @public\n */\nexport interface DetectionCacheOptions {\n /** Whether to bypass the cache and force a fresh detection scan. @defaultValue false */\n forceRefresh?: boolean;\n /** Time-to-live for cached results in milliseconds. @defaultValue 30000 */\n ttlMs?: number;\n}\n\ninterface DetectionCacheState {\n createdAt: number;\n signature: string;\n results: DetectionResult[];\n}\n\nconst DEFAULT_DETECTION_CACHE_TTL_MS = 30_000;\nlet detectionCache: DetectionCacheState | null = null;\n\nfunction checkBinary(binary: string): boolean {\n try {\n const cmd = process.platform === 'win32' ? 'where' : 'which';\n execFileSync(cmd, [binary], { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction checkDirectory(dir: string): boolean {\n return existsSync(dir);\n}\n\nfunction checkAppBundle(appName: string): boolean {\n if (process.platform !== 'darwin') return false;\n const applications = getPlatformLocations().applications;\n return applications.some((base) => existsSync(join(base, appName)));\n}\n\nfunction checkFlatpak(flatpakId: string): boolean {\n if (process.platform !== 'linux') return false;\n try {\n execFileSync('flatpak', ['info', flatpakId], { stdio: 'pipe' });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Detect if a single provider is installed on the system.\n *\n * Checks each detection method configured for the provider (binary, directory,\n * appBundle, flatpak) and returns which methods matched.\n *\n * @remarks\n * Detection methods are defined per-provider in `providers/registry.json`.\n * Each method is checked in order: `binary` uses `which`/`where` to find\n * executables, `directory` checks for config directories, `appBundle` looks\n * in macOS Applications folders, and `flatpak` queries flatpak on Linux.\n * The `projectDetected` field is always `false` here; use\n * {@link detectProjectProviders} for project-level detection.\n *\n * @param provider - The provider to detect\n * @returns Detection result with installation status and matched methods\n *\n * @example\n * ```typescript\n * const provider = getProvider(\"claude-code\")!;\n * const result = detectProvider(provider);\n * if (result.installed) {\n * console.log(`Claude Code found via: ${result.methods.join(\", \")}`);\n * }\n * ```\n *\n * @public\n */\nexport function detectProvider(provider: Provider): DetectionResult {\n const matchedMethods: string[] = [];\n const detection = provider.detection;\n\n debug(`detecting provider ${provider.id} via methods: ${detection.methods.join(', ')}`);\n\n for (const method of detection.methods) {\n switch (method) {\n case 'binary':\n if (detection.binary && checkBinary(detection.binary)) {\n debug(` ${provider.id}: binary \"${detection.binary}\" found`);\n matchedMethods.push('binary');\n }\n break;\n case 'directory':\n if (detection.directories) {\n for (const dir of detection.directories) {\n if (checkDirectory(dir)) {\n matchedMethods.push('directory');\n break;\n }\n }\n }\n break;\n case 'appBundle':\n if (detection.appBundle && checkAppBundle(detection.appBundle)) {\n matchedMethods.push('appBundle');\n }\n break;\n case 'flatpak':\n if (detection.flatpakId && checkFlatpak(detection.flatpakId)) {\n matchedMethods.push('flatpak');\n }\n break;\n }\n }\n\n return {\n provider,\n installed: matchedMethods.length > 0,\n methods: matchedMethods,\n projectDetected: false,\n };\n}\n\nfunction providerSignature(provider: Provider): string {\n return JSON.stringify({\n id: provider.id,\n methods: provider.detection.methods,\n binary: provider.detection.binary,\n directories: provider.detection.directories,\n appBundle: provider.detection.appBundle,\n flatpakId: provider.detection.flatpakId,\n });\n}\n\nfunction buildProvidersSignature(providers: Provider[]): string {\n if (!providers || !Array.isArray(providers)) return '';\n return providers.map(providerSignature).join('|');\n}\n\nfunction cloneDetectionResults(results: DetectionResult[]): DetectionResult[] {\n return results.map((result) => ({\n provider: result.provider,\n installed: result.installed,\n methods: [...result.methods],\n projectDetected: result.projectDetected,\n }));\n}\n\nfunction getCachedResults(\n signature: string,\n options: DetectionCacheOptions,\n): DetectionResult[] | null {\n if (!detectionCache || options.forceRefresh) return null;\n if (detectionCache.signature !== signature) return null;\n\n const ttlMs = options.ttlMs ?? DEFAULT_DETECTION_CACHE_TTL_MS;\n if (ttlMs <= 0) return null;\n if (Date.now() - detectionCache.createdAt > ttlMs) return null;\n\n return cloneDetectionResults(detectionCache.results);\n}\n\nfunction setCachedResults(signature: string, results: DetectionResult[]): void {\n detectionCache = {\n createdAt: Date.now(),\n signature,\n results: cloneDetectionResults(results),\n };\n}\n\n/**\n * Detect if a provider has project-level config in the given directory.\n *\n * @remarks\n * Checks whether the provider's `pathProject` config file exists within the\n * given project directory. Returns `false` if the provider has no project-level\n * path defined.\n *\n * @param provider - Provider to check for project-level config\n * @param projectDir - Absolute path to the project directory\n * @returns `true` if the provider has a config file in the project directory\n *\n * @example\n * ```typescript\n * const provider = getProvider(\"claude-code\")!;\n * const hasProjectConfig = detectProjectProvider(provider, \"/home/user/my-project\");\n * ```\n *\n * @public\n */\nexport function detectProjectProvider(provider: Provider, projectDir: string): boolean {\n if (!provider.pathProject) return false;\n return existsSync(resolveProviderProjectPath(provider, projectDir));\n}\n\n/**\n * Detect all registered providers and return their installation status.\n *\n * Runs detection for every provider in the registry.\n *\n * @remarks\n * Results are cached in memory with a configurable TTL (default 30 seconds).\n * The cache key is a signature of all provider detection configurations, so\n * it auto-invalidates if the registry changes. Pass `{ forceRefresh: true }`\n * to bypass the cache.\n *\n * @param options - Cache control options\n * @returns Array of detection results for all providers\n *\n * @example\n * ```typescript\n * const results = detectAllProviders({ forceRefresh: true });\n * const installed = results.filter(r => r.installed);\n * console.log(`${installed.length} agents detected`);\n * ```\n *\n * @public\n */\nexport function detectAllProviders(options: DetectionCacheOptions = {}): DetectionResult[] {\n const providers = getAllProviders() ?? [];\n const signature = buildProvidersSignature(providers);\n const cached = getCachedResults(signature, options);\n if (cached) {\n debug(`detection cache hit for ${providers.length} providers`);\n return cached;\n }\n\n const results = providers.map(detectProvider);\n setCachedResults(signature, results);\n return cloneDetectionResults(results);\n}\n\n/**\n * Get only providers that are currently installed on the system.\n *\n * Convenience wrapper that filters {@link detectAllProviders} results to only\n * those with `installed === true`.\n *\n * @remarks\n * Delegates to {@link detectAllProviders} and extracts the `provider` object\n * from each result where `installed` is true. Cache behavior is inherited\n * from the underlying detection call.\n *\n * @param options - Cache control options passed through to detection\n * @returns Array of installed provider definitions\n *\n * @example\n * ```typescript\n * const installed = getInstalledProviders({ forceRefresh: true });\n * console.log(installed.map(p => p.toolName).join(\", \"));\n * ```\n *\n * @see {@link detectAllProviders}\n *\n * @public\n */\nexport function getInstalledProviders(options: DetectionCacheOptions = {}): Provider[] {\n return detectAllProviders(options)\n .filter((r) => r.installed)\n .map((r) => r.provider);\n}\n\n/**\n * Detect all providers and enrich results with project-level presence.\n *\n * Extends {@link detectAllProviders} by also checking whether each provider\n * has a project-level config file in the given directory.\n *\n * @remarks\n * Calls {@link detectAllProviders} for system-level detection, then overlays\n * project-level checks via {@link detectProjectProvider} for each result.\n * The `projectDetected` field in the returned results will be `true` when the\n * provider has a config file (e.g. `.claude/settings.json`) in the given directory.\n *\n * @param projectDir - Absolute path to the project directory to check\n * @param options - Cache control options passed through to detection\n * @returns Array of detection results with `projectDetected` populated\n *\n * @example\n * ```typescript\n * const results = detectProjectProviders(\"/home/user/my-project\", { forceRefresh: true });\n * for (const r of results) {\n * if (r.projectDetected) {\n * console.log(`${r.provider.toolName} has project config`);\n * }\n * }\n * ```\n *\n * @see {@link detectAllProviders}\n *\n * @public\n */\nexport function detectProjectProviders(\n projectDir: string,\n options: DetectionCacheOptions = {},\n): DetectionResult[] {\n const results = detectAllProviders(options);\n return results.map((r) => ({\n ...r,\n projectDetected: detectProjectProvider(r.provider, projectDir),\n }));\n}\n\n/**\n * Reset the detection result cache, forcing fresh detection on next call.\n *\n * @remarks\n * Clears the in-memory detection cache. Primarily used in test suites to\n * ensure deterministic results between test cases. After calling this,\n * the next invocation of {@link detectAllProviders} will perform a full\n * system scan regardless of TTL.\n *\n * @example\n * ```typescript\n * resetDetectionCache();\n * // Next detectAllProviders() call will bypass cache\n * const fresh = detectAllProviders();\n * ```\n *\n * @public\n */\nexport function resetDetectionCache(): void {\n detectionCache = null;\n}\n","/**\n * Skill installer - canonical + symlink model\n *\n * Skills are stored once in a canonical location (.agents/skills/<name>/)\n * and symlinked to each target agent's skills directory.\n */\n\nimport { existsSync, lstatSync } from 'node:fs';\nimport { cp, mkdir, rm, symlink } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Provider } from '../../types.js';\nimport { getCanonicalSkillsDir, resolveProviderSkillsDirs } from '../paths/standard.js';\n\n/**\n * Result of installing a skill to the canonical location and linking to agents.\n *\n * @example\n * ```typescript\n * const result = await installSkill(sourcePath, \"my-skill\", providers, true);\n * if (result.success) {\n * console.log(`Installed to ${result.canonicalPath}`);\n * console.log(`Linked to: ${result.linkedAgents.join(\", \")}`);\n * }\n * ```\n *\n * @public\n */\nexport interface SkillInstallResult {\n /** Skill name. */\n name: string;\n /** Absolute path to the canonical installation directory. */\n canonicalPath: string;\n /** Provider IDs that were successfully linked. */\n linkedAgents: string[];\n /** Error messages from failed link operations. */\n errors: string[];\n /** Whether at least one agent was successfully linked. */\n success: boolean;\n}\n\n/** Ensure canonical skills directory exists */\nasync function ensureCanonicalDir(): Promise<void> {\n await mkdir(getCanonicalSkillsDir(), { recursive: true });\n}\n\n/**\n * Copy skill files to the canonical location.\n *\n * @remarks\n * Removes any existing installation at the target directory before copying.\n * Handles race conditions where another concurrent install may create the\n * directory between removal and copy by retrying the operation.\n *\n * @param sourcePath - Absolute path to the source skill directory to copy\n * @param skillName - Name for the skill (used as the subdirectory name)\n * @returns Absolute path to the canonical installation directory\n *\n * @example\n * ```typescript\n * const canonicalPath = await installToCanonical(\"/tmp/my-skill\", \"my-skill\");\n * console.log(`Installed to: ${canonicalPath}`);\n * ```\n *\n * @public\n */\nexport async function installToCanonical(sourcePath: string, skillName: string): Promise<string> {\n await ensureCanonicalDir();\n\n const targetDir = join(getCanonicalSkillsDir(), skillName);\n\n // Remove existing (force: true ignores ENOENT if it doesn't exist)\n await rm(targetDir, { recursive: true, force: true });\n\n try {\n await cp(sourcePath, targetDir, { recursive: true });\n } catch (err: unknown) {\n // Handle race condition: another concurrent install may have created the dir\n if (err && typeof err === 'object' && 'code' in err && err.code === 'EEXIST') {\n await rm(targetDir, { recursive: true, force: true });\n await cp(sourcePath, targetDir, { recursive: true });\n } else {\n throw err;\n }\n }\n\n return targetDir;\n}\n\n/** Create symlinks from an agent's skills directories to the canonical location */\nasync function linkToAgent(\n canonicalPath: string,\n provider: Provider,\n skillName: string,\n isGlobal: boolean,\n projectDir?: string,\n): Promise<{ success: boolean; error?: string }> {\n const scope = isGlobal ? 'global' : 'project';\n const targetDirs = resolveProviderSkillsDirs(provider, scope, projectDir);\n\n if (targetDirs.length === 0) {\n return { success: false, error: `Provider ${provider.id} has no skills directory` };\n }\n\n const errors: string[] = [];\n let anySuccess = false;\n\n for (const targetSkillsDir of targetDirs) {\n if (!targetSkillsDir) continue;\n\n try {\n await mkdir(targetSkillsDir, { recursive: true });\n\n const linkPath = join(targetSkillsDir, skillName);\n\n // Remove existing link/directory\n if (existsSync(linkPath)) {\n const stat = lstatSync(linkPath);\n if (stat.isSymbolicLink()) {\n await rm(linkPath);\n } else {\n await rm(linkPath, { recursive: true });\n }\n }\n\n // Create symlink (junction on Windows for compat)\n const symlinkType = process.platform === 'win32' ? 'junction' : 'dir';\n try {\n await symlink(canonicalPath, linkPath, symlinkType);\n } catch {\n // Fallback to copy if symlinks not supported\n await cp(canonicalPath, linkPath, { recursive: true });\n }\n\n anySuccess = true;\n } catch (err) {\n errors.push(err instanceof Error ? err.message : String(err));\n }\n }\n\n if (anySuccess) {\n return { success: true };\n }\n return {\n success: false,\n error: errors.join('; ') || `Provider ${provider.id} has no skills directory`,\n };\n}\n\n/**\n * Install a skill from a local path to the canonical location and link to agents.\n *\n * @remarks\n * Copies the skill directory to the canonical skills directory and creates symlinks\n * (or copies on Windows) from each provider's skills directory to the canonical path.\n *\n * @param sourcePath - Local path to the skill directory to install\n * @param skillName - Name for the installed skill\n * @param providers - Target providers to link the skill to\n * @param isGlobal - Whether to link to global or project skill directories\n * @param projectDir - Project directory (defaults to `process.cwd()`)\n * @returns Install result with linked agents and any errors\n *\n * @example\n * ```typescript\n * const result = await installSkill(\"/tmp/my-skill\", \"my-skill\", providers, true, \"/my/project\");\n * if (result.success) {\n * console.log(`Linked to: ${result.linkedAgents.join(\", \")}`);\n * }\n * ```\n *\n * @public\n */\nexport async function installSkill(\n sourcePath: string,\n skillName: string,\n providers: Provider[],\n isGlobal: boolean,\n projectDir?: string,\n): Promise<SkillInstallResult> {\n const errors: string[] = [];\n const linkedAgents: string[] = [];\n\n // Step 1: Install to canonical location\n const canonicalPath = await installToCanonical(sourcePath, skillName);\n\n // Step 2: Link to each agent\n for (const provider of providers) {\n const result = await linkToAgent(canonicalPath, provider, skillName, isGlobal, projectDir);\n if (result.success) {\n linkedAgents.push(provider.id);\n } else if (result.error) {\n errors.push(`${provider.id}: ${result.error}`);\n }\n }\n\n return {\n name: skillName,\n canonicalPath,\n linkedAgents,\n errors,\n success: linkedAgents.length > 0,\n };\n}\n\n/**\n * Remove a skill from the canonical location and all agent symlinks.\n *\n * @remarks\n * Removes symlinks from each provider's skills directory and then removes the\n * canonical copy from the centralized canonical skills directory.\n *\n * @param skillName - Name of the skill to remove\n * @param providers - Providers to unlink the skill from\n * @param isGlobal - Whether to target global or project skill directories\n * @param projectDir - Project directory (defaults to `process.cwd()`)\n * @returns Object with arrays of successfully removed provider IDs and error messages\n *\n * @example\n * ```typescript\n * const { removed, errors } = await removeSkill(\"my-skill\", providers, true, \"/my/project\");\n * console.log(`Removed from: ${removed.join(\", \")}`);\n * ```\n *\n * @public\n */\nexport async function removeSkill(\n skillName: string,\n providers: Provider[],\n isGlobal: boolean,\n projectDir?: string,\n): Promise<{ removed: string[]; errors: string[] }> {\n const removed: string[] = [];\n const errors: string[] = [];\n\n // Remove symlinks from each agent (all precedence-aware paths)\n for (const provider of providers) {\n const scope = isGlobal ? 'global' : 'project';\n const targetDirs = resolveProviderSkillsDirs(provider, scope, projectDir);\n let providerRemoved = false;\n\n for (const skillsDir of targetDirs) {\n if (!skillsDir) continue;\n\n const linkPath = join(skillsDir, skillName);\n if (existsSync(linkPath)) {\n try {\n await rm(linkPath, { recursive: true });\n providerRemoved = true;\n } catch (err) {\n errors.push(`${provider.id}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n }\n\n if (providerRemoved) {\n removed.push(provider.id);\n }\n }\n\n // Remove canonical copy\n const canonicalPath = join(getCanonicalSkillsDir(), skillName);\n if (existsSync(canonicalPath)) {\n try {\n await rm(canonicalPath, { recursive: true });\n } catch (err) {\n errors.push(`canonical: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n return { removed, errors };\n}\n\n/**\n * List all skills installed in the canonical skills directory.\n *\n * @remarks\n * Returns the directory names of all skills, which correspond to skill names.\n * Includes both regular directories and symlinks in the canonical location.\n *\n * @returns Array of skill names\n *\n * @example\n * ```typescript\n * const skills = await listCanonicalSkills();\n * // [\"my-skill\", \"another-skill\"]\n * ```\n *\n * @public\n */\nexport async function listCanonicalSkills(): Promise<string[]> {\n if (!existsSync(getCanonicalSkillsDir())) return [];\n\n const { readdir } = await import('node:fs/promises');\n const entries = await readdir(getCanonicalSkillsDir(), { withFileTypes: true });\n return entries.filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => e.name);\n}\n","/**\n * Advanced orchestration helpers for multi-provider operations.\n *\n * These helpers compose CAAMP's lower-level APIs into production patterns:\n * tier-based targeting, rollback-capable skill batches, and instruction updates.\n */\n\nimport { existsSync, lstatSync } from 'node:fs';\nimport { cp, mkdir, readlink, rm, symlink } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { basename, dirname, join } from 'node:path';\nimport type { ConfigFormat, Provider, ProviderPriority } from '../../types.js';\nimport { injectAll } from '../instructions/injector.js';\nimport { groupByInstructFile } from '../instructions/templates.js';\nimport { CANONICAL_SKILLS_DIR } from '../paths/agents.js';\nimport { getInstalledProviders } from '../registry/detection.js';\nimport { installSkill, removeSkill } from '../skills/installer.js';\n\ntype Scope = 'project' | 'global';\n\nconst PRIORITY_ORDER: Record<ProviderPriority, number> = {\n primary: -1,\n high: 0,\n medium: 1,\n low: 2,\n};\n\n/**\n * Filters providers by minimum priority and returns them in deterministic tier order.\n *\n * @remarks\n * Providers are filtered to include only those at or above the specified priority\n * level, then sorted from highest to lowest priority. For example,\n * `minimumPriority = \"medium\"` returns providers with `high` and `medium` priority.\n *\n * @param providers - The full list of providers to filter\n * @param minimumPriority - The minimum priority threshold, defaults to `\"low\"` (include all)\n * @returns A filtered and sorted array of providers meeting the priority threshold\n *\n * @example\n * ```typescript\n * const highPriority = selectProvidersByMinimumPriority(allProviders, \"high\");\n * // returns only providers with priority \"high\"\n * ```\n *\n * @public\n */\nexport function selectProvidersByMinimumPriority(\n providers: Provider[],\n minimumPriority: ProviderPriority = 'low',\n): Provider[] {\n const maxRank = PRIORITY_ORDER[minimumPriority];\n\n return [...providers]\n .filter((provider) => PRIORITY_ORDER[provider.priority] <= maxRank)\n .sort((a, b) => PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority]);\n}\n\n/**\n * Single skill operation entry used by batch orchestration.\n *\n * @remarks\n * Represents one skill installation that will be applied across\n * all targeted providers during a batch operation.\n *\n * @public\n */\nexport interface SkillBatchOperation {\n /** The filesystem path to the skill source files. */\n sourcePath: string;\n /** The unique name for the skill being installed. */\n skillName: string;\n /** Whether to install globally or project-scoped, defaults to true. */\n isGlobal?: boolean;\n}\n\n/**\n * Options for rollback-capable batch installation.\n *\n * @remarks\n * All fields are optional. When providers are not specified, installed\n * providers are auto-detected. When minimumPriority is not specified,\n * all priority levels are included.\n *\n * @public\n */\nexport interface BatchInstallOptions {\n /** Explicit list of providers to target, auto-detected if omitted. */\n providers?: Provider[];\n /** Minimum provider priority threshold for filtering. */\n minimumPriority?: ProviderPriority;\n /** Skill operations to apply in the batch. */\n skills?: SkillBatchOperation[];\n /** Project root directory, defaults to `process.cwd()`. */\n projectDir?: string;\n}\n\n/**\n * Result of rollback-capable batch installation.\n *\n * @remarks\n * When `success` is false, `rollbackPerformed` indicates whether rollback\n * was attempted. Any errors during rollback are captured in `rollbackErrors`.\n *\n * @public\n */\nexport interface BatchInstallResult {\n /** Whether all operations completed successfully. */\n success: boolean;\n /** IDs of providers that were targeted. */\n providerIds: string[];\n /** Number of skill installations that were applied. */\n skillsApplied: number;\n /** Whether rollback was performed due to a failure. */\n rollbackPerformed: boolean;\n /** Error messages from any failures during rollback. */\n rollbackErrors: string[];\n /** Error message from the operation that triggered rollback. */\n error?: string;\n}\n\ninterface SkillPathSnapshot {\n linkPath: string;\n state: 'missing' | 'symlink' | 'directory' | 'file';\n symlinkTarget?: string;\n backupPath?: string;\n}\n\ninterface SkillSnapshot {\n skillName: string;\n isGlobal: boolean;\n canonicalPath: string;\n canonicalBackupPath?: string;\n canonicalExisted: boolean;\n pathSnapshots: SkillPathSnapshot[];\n}\n\ninterface AppliedSkillInstall {\n skillName: string;\n isGlobal: boolean;\n linkedProviders: Provider[];\n}\n\nfunction resolveSkillLinkPath(\n provider: Provider,\n skillName: string,\n isGlobal: boolean,\n projectDir: string,\n): string {\n const skillDir = isGlobal ? provider.pathSkills : join(projectDir, provider.pathProjectSkills);\n return join(skillDir, skillName);\n}\n\nasync function snapshotSkillState(\n providerTargets: Provider[],\n operation: SkillBatchOperation,\n projectDir: string,\n backupRoot: string,\n): Promise<SkillSnapshot> {\n const skillName = operation.skillName;\n const isGlobal = operation.isGlobal ?? true;\n const canonicalPath = join(CANONICAL_SKILLS_DIR, skillName);\n const canonicalExisted = existsSync(canonicalPath);\n const canonicalBackupPath = join(backupRoot, 'canonical', skillName);\n\n if (canonicalExisted) {\n await mkdir(dirname(canonicalBackupPath), { recursive: true });\n await cp(canonicalPath, canonicalBackupPath, { recursive: true });\n }\n\n const pathSnapshots: SkillPathSnapshot[] = [];\n for (const provider of providerTargets) {\n const linkPath = resolveSkillLinkPath(provider, skillName, isGlobal, projectDir);\n\n if (!existsSync(linkPath)) {\n pathSnapshots.push({ linkPath, state: 'missing' });\n continue;\n }\n\n const stat = lstatSync(linkPath);\n\n if (stat.isSymbolicLink()) {\n pathSnapshots.push({\n linkPath,\n state: 'symlink',\n symlinkTarget: await readlink(linkPath),\n });\n continue;\n }\n\n const backupPath = join(backupRoot, 'links', provider.id, `${skillName}-${basename(linkPath)}`);\n await mkdir(dirname(backupPath), { recursive: true });\n\n if (stat.isDirectory()) {\n await cp(linkPath, backupPath, { recursive: true });\n pathSnapshots.push({ linkPath, state: 'directory', backupPath });\n continue;\n }\n\n await cp(linkPath, backupPath);\n pathSnapshots.push({ linkPath, state: 'file', backupPath });\n }\n\n return {\n skillName,\n isGlobal,\n canonicalPath,\n canonicalBackupPath: canonicalExisted ? canonicalBackupPath : undefined,\n canonicalExisted,\n pathSnapshots,\n };\n}\n\nasync function restoreSkillSnapshot(snapshot: SkillSnapshot): Promise<void> {\n if (existsSync(snapshot.canonicalPath)) {\n await rm(snapshot.canonicalPath, { recursive: true, force: true });\n }\n\n if (\n snapshot.canonicalExisted &&\n snapshot.canonicalBackupPath &&\n existsSync(snapshot.canonicalBackupPath)\n ) {\n await mkdir(dirname(snapshot.canonicalPath), { recursive: true });\n await cp(snapshot.canonicalBackupPath, snapshot.canonicalPath, { recursive: true });\n }\n\n for (const pathSnapshot of snapshot.pathSnapshots) {\n await rm(pathSnapshot.linkPath, { recursive: true, force: true });\n\n if (pathSnapshot.state === 'missing') continue;\n\n await mkdir(dirname(pathSnapshot.linkPath), { recursive: true });\n\n if (pathSnapshot.state === 'symlink' && pathSnapshot.symlinkTarget) {\n const linkType = process.platform === 'win32' ? 'junction' : 'dir';\n await symlink(pathSnapshot.symlinkTarget, pathSnapshot.linkPath, linkType);\n continue;\n }\n\n if (\n (pathSnapshot.state === 'directory' || pathSnapshot.state === 'file') &&\n pathSnapshot.backupPath\n ) {\n if (pathSnapshot.state === 'directory') {\n await cp(pathSnapshot.backupPath, pathSnapshot.linkPath, { recursive: true });\n } else {\n await cp(pathSnapshot.backupPath, pathSnapshot.linkPath);\n }\n }\n }\n}\n\n/**\n * Installs multiple skills across filtered providers with rollback.\n *\n * @remarks\n * Snapshots all affected skill directories before applying operations.\n * If any operation fails, all changes are rolled back by reverting\n * skill symlinks and canonical directories to their pre-operation state.\n *\n * @param options - The batch installation options including providers, operations, and scope\n * @returns A result object indicating success, applied counts, and any rollback information\n *\n * @example\n * ```typescript\n * const result = await installBatchWithRollback({\n * minimumPriority: \"high\",\n * skills: [{ sourcePath: \"/path/to/skill\", skillName: \"my-skill\" }],\n * });\n * if (!result.success) {\n * console.error(\"Failed:\", result.error);\n * }\n * ```\n *\n * @public\n */\nexport async function installBatchWithRollback(\n options: BatchInstallOptions,\n): Promise<BatchInstallResult> {\n const projectDir = options.projectDir ?? process.cwd();\n const minimumPriority = options.minimumPriority ?? 'low';\n const skillOps = options.skills ?? [];\n const baseProviders = options.providers ?? getInstalledProviders();\n const providers = selectProvidersByMinimumPriority(baseProviders, minimumPriority);\n\n const backupRoot = join(\n tmpdir(),\n `caamp-skill-backup-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n );\n\n const skillSnapshots = await Promise.all(\n skillOps.map((operation) => snapshotSkillState(providers, operation, projectDir, backupRoot)),\n );\n\n const appliedSkills: AppliedSkillInstall[] = [];\n const rollbackErrors: string[] = [];\n let skillsApplied = 0;\n let rollbackPerformed = false;\n\n try {\n for (const operation of skillOps) {\n const isGlobal = operation.isGlobal ?? true;\n const result = await installSkill(\n operation.sourcePath,\n operation.skillName,\n providers,\n isGlobal,\n projectDir,\n );\n\n const linkedProviders = providers.filter((provider) =>\n result.linkedAgents.includes(provider.id),\n );\n appliedSkills.push({\n skillName: operation.skillName,\n isGlobal,\n linkedProviders,\n });\n\n if (result.errors.length > 0) {\n throw new Error(result.errors.join('; '));\n }\n\n skillsApplied += 1;\n }\n\n await rm(backupRoot, { recursive: true, force: true });\n\n return {\n success: true,\n providerIds: providers.map((provider) => provider.id),\n skillsApplied,\n rollbackPerformed: false,\n rollbackErrors: [],\n };\n } catch (error) {\n rollbackPerformed = true;\n\n for (const applied of [...appliedSkills].reverse()) {\n try {\n await removeSkill(applied.skillName, applied.linkedProviders, applied.isGlobal, projectDir);\n } catch (err) {\n rollbackErrors.push(err instanceof Error ? err.message : String(err));\n }\n }\n\n for (const snapshot of skillSnapshots) {\n try {\n await restoreSkillSnapshot(snapshot);\n } catch (err) {\n rollbackErrors.push(err instanceof Error ? err.message : String(err));\n }\n }\n\n await rm(backupRoot, { recursive: true, force: true });\n\n return {\n success: false,\n providerIds: providers.map((provider) => provider.id),\n skillsApplied,\n rollbackPerformed,\n rollbackErrors,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n}\n\n/**\n * Result of a single-operation instruction update across providers.\n *\n * @remarks\n * Summarizes the instruction files that were created, updated, or left\n * intact during an instruction injection operation.\n *\n * @public\n */\nexport interface InstructionUpdateSummary {\n /** The scope at which instructions were updated. */\n scope: Scope;\n /** The total number of instruction files that were modified. */\n updatedFiles: number;\n /** Detailed action log per instruction file. */\n actions: Array<{\n file: string;\n action: 'created' | 'added' | 'consolidated' | 'updated' | 'intact';\n providers: string[];\n configFormats: ConfigFormat[];\n }>;\n}\n\n/**\n * Updates instruction files across providers as a single operation.\n *\n * @remarks\n * Works the same regardless of provider config format (JSON/YAML/TOML/JSONC)\n * because instruction files are handled through CAAMP markers. Groups\n * providers by their instruction file targets and injects content using\n * marker-based sections.\n *\n * @param providers - The providers whose instruction files to update\n * @param content - The instruction content to inject\n * @param scope - The scope for instruction updates, defaults to `\"project\"`\n * @param projectDir - The project root directory, defaults to `process.cwd()`\n * @returns A summary of updated files and actions taken per file\n *\n * @example\n * ```typescript\n * const summary = await updateInstructionsSingleOperation(\n * providers,\n * \"## CAAMP Config\\nUse these MCP servers...\",\n * \"project\",\n * );\n * console.log(`Updated ${summary.updatedFiles} files`);\n * ```\n *\n * @public\n */\nexport async function updateInstructionsSingleOperation(\n providers: Provider[],\n content: string,\n scope: Scope = 'project',\n projectDir = process.cwd(),\n): Promise<InstructionUpdateSummary> {\n const actions = await injectAll(providers, projectDir, scope, content);\n const groupedByFile = groupByInstructFile(providers);\n\n const summary: InstructionUpdateSummary = {\n scope,\n updatedFiles: actions.size,\n actions: [],\n };\n\n for (const [filePath, action] of actions.entries()) {\n const providersForFile = providers.filter((provider) => {\n const expectedPath =\n scope === 'global'\n ? join(provider.pathGlobal, provider.instructFile)\n : join(projectDir, provider.instructFile);\n return expectedPath === filePath;\n });\n\n const fallback = groupedByFile.get(basename(filePath)) ?? [];\n const selected = providersForFile.length > 0 ? providersForFile : fallback;\n\n summary.actions.push({\n file: filePath,\n action,\n providers: selected.map((provider) => provider.id),\n configFormats: Array.from(\n new Set(\n selected\n .map((provider) => provider.capabilities.mcp?.configFormat)\n .filter((f): f is ConfigFormat => f !== undefined),\n ),\n ),\n });\n }\n\n return summary;\n}\n","import {\n getAgentsConfigPath,\n getAgentsHome,\n getAgentsMcpDir,\n getAgentsMcpServersPath,\n getCanonicalSkillsDir,\n getLockFilePath,\n} from './standard.js';\n\n/**\n * Global `.agents/` home directory (`~/.agents/` or `$AGENTS_HOME`).\n * @public\n */\nexport const AGENTS_HOME = getAgentsHome();\n\n/**\n * CAAMP lock file path (`~/.agents/.caamp-lock.json`).\n * @public\n */\nexport const LOCK_FILE_PATH = getLockFilePath();\n\n/**\n * Canonical skills directory (`~/.agents/skills/`).\n * @public\n */\nexport const CANONICAL_SKILLS_DIR = getCanonicalSkillsDir();\n\n/**\n * Global MCP directory (`~/.agents/mcp/`).\n * @public\n */\nexport const AGENTS_MCP_DIR = getAgentsMcpDir();\n\n/**\n * Global MCP servers.json path (`~/.agents/mcp/servers.json`).\n * @public\n */\nexport const AGENTS_MCP_SERVERS_PATH = getAgentsMcpServersPath();\n\n/**\n * Global agents config.toml path (`~/.agents/config.toml`).\n * @public\n */\nexport const AGENTS_CONFIG_PATH = getAgentsConfigPath();\n","/**\n * CAAMP-wide configuration accessors.\n *\n * @remarks\n * This module is the single source of truth for CAAMP configuration values\n * that affect runtime behaviour across the package. Today it carries one\n * setting — {@link ExclusivityMode} — introduced by ADR-035 §D7 to control\n * how `resolveDefaultTargetProviders()` selects target providers at runtime\n * invocation time.\n *\n * The accessor pattern intentionally uses a layered resolution order so the\n * setting can be overridden by tests, by environment variables in CI, and by\n * future programmatic callers (e.g. a `caamp config set` subcommand) without\n * any of those layers needing to know about the others:\n *\n * 1. Programmatic override via {@link setExclusivityMode} (highest priority).\n * 2. Environment variable `CAAMP_EXCLUSIVITY_MODE`.\n * 3. Default value `'auto'`.\n *\n * The programmatic override exists primarily for tests and for future\n * `caamp config set` integration; production code should prefer the\n * environment variable when wiring CI or shell sessions.\n *\n * @packageDocumentation\n */\n\nimport type { ExclusivityMode } from '../harness/types.js';\n\n/**\n * Default exclusivity mode used when no override is configured.\n *\n * @remarks\n * `auto` mirrors the v2026.4.5+ behaviour: Pi is preferred when installed,\n * but explicit non-Pi targets remain functional. This is the value the\n * accessor returns when neither {@link setExclusivityMode} nor the\n * `CAAMP_EXCLUSIVITY_MODE` environment variable is set.\n *\n * @public\n */\nexport const DEFAULT_EXCLUSIVITY_MODE: ExclusivityMode = 'auto';\n\n/**\n * Environment variable name read by {@link getExclusivityMode} when no\n * programmatic override is active.\n *\n * @remarks\n * Exported as a constant so tests and downstream tooling can refer to the\n * canonical name without typo risk. The variable accepts the same three\n * literal values as the {@link ExclusivityMode} type.\n *\n * @public\n */\nexport const EXCLUSIVITY_MODE_ENV_VAR = 'CAAMP_EXCLUSIVITY_MODE';\n\n/**\n * Programmatic override slot. `null` means \"no override active; fall through\n * to the environment variable layer\". Mutated by {@link setExclusivityMode}\n * and {@link resetExclusivityModeOverride}.\n */\nlet programmaticOverride: ExclusivityMode | null = null;\n\n/**\n * One-time warning latches for the two `auto`-mode warnings emitted by\n * `resolveDefaultTargetProviders`.\n *\n * @remarks\n * Kept in a single object so accessor and mutator helpers share state\n * without exporting individual `let` bindings. `piAbsentAutoWarned` tracks\n * the boot warning emitted when Pi is not installed; `explicitNonPiAutoWarned`\n * tracks the deprecation warning emitted when an explicit non-Pi target is\n * supplied while Pi is installed. Both flags reset to `false` via\n * {@link resetExclusivityWarningState}.\n *\n * @internal\n */\nconst exclusivityWarningState = {\n piAbsentAutoWarned: false,\n explicitNonPiAutoWarned: false,\n};\n\n/**\n * Read whether the `auto` + Pi-absent boot warning has already fired for\n * this process.\n *\n * @returns `true` once the warning has been emitted; `false` until then or\n * after {@link resetExclusivityWarningState} runs.\n *\n * @internal\n */\nexport function hasPiAbsentAutoWarned(): boolean {\n return exclusivityWarningState.piAbsentAutoWarned;\n}\n\n/**\n * Read whether the `auto` + explicit-non-Pi deprecation warning has already\n * fired for this process.\n *\n * @returns `true` once the warning has been emitted; `false` until then or\n * after {@link resetExclusivityWarningState} runs.\n *\n * @internal\n */\nexport function hasExplicitNonPiAutoWarned(): boolean {\n return exclusivityWarningState.explicitNonPiAutoWarned;\n}\n\n/**\n * Mark the `auto` + Pi-absent warning as already emitted for this process.\n *\n * @remarks\n * Called by `resolveDefaultTargetProviders` immediately after it writes the\n * boot warning to `console.warn`. Idempotent — calling more than once is\n * harmless.\n *\n * @internal\n */\nexport function markPiAbsentAutoWarned(): void {\n exclusivityWarningState.piAbsentAutoWarned = true;\n}\n\n/**\n * Mark the `auto` + explicit-non-Pi deprecation warning as already emitted\n * for this process.\n *\n * @remarks\n * Called by `resolveDefaultTargetProviders` immediately after it writes the\n * deprecation warning to `console.warn`. Idempotent.\n *\n * @internal\n */\nexport function markExplicitNonPiAutoWarned(): void {\n exclusivityWarningState.explicitNonPiAutoWarned = true;\n}\n\n/**\n * Reset both per-process exclusivity warning latches.\n *\n * @remarks\n * Exposed solely for tests so the matrix can verify both the \"warns once\"\n * and \"subsequent calls do not re-emit\" cases without relying on Vitest\n * isolation modes that reset the entire module graph between tests. Not\n * part of the user-facing API surface; do not call from production code.\n *\n * @internal\n */\nexport function resetExclusivityWarningState(): void {\n exclusivityWarningState.piAbsentAutoWarned = false;\n exclusivityWarningState.explicitNonPiAutoWarned = false;\n}\n\n/**\n * Error raised when {@link getExclusivityMode} resolves to `'force-pi'` but\n * Pi is not installed at the moment a runtime dispatch is requested.\n *\n * @remarks\n * Carries an `E_NOT_FOUND_RESOURCE`-shaped `code` so command-layer error\n * envelopes (LAFS) can map it to a stable category. Callers decide whether\n * to surface this as a process exit (typically code 4) or as a structured\n * envelope; the harness layer never calls `process.exit` itself.\n *\n * Lives next to the configuration accessor rather than in the harness\n * dispatcher so consumers that import the mode also pick up the matching\n * error type without a second module hop.\n *\n * @example\n * ```typescript\n * try {\n * const targets = resolveDefaultTargetProviders();\n * } catch (err) {\n * if (err instanceof PiRequiredError) {\n * process.exit(4);\n * }\n * throw err;\n * }\n * ```\n *\n * @public\n */\nexport class PiRequiredError extends Error {\n /** LAFS-stable error code identifying this failure mode. */\n public readonly code = 'E_NOT_FOUND_RESOURCE' as const;\n\n /**\n * Construct a new {@link PiRequiredError}.\n *\n * @param message - Human-readable failure description; defaults to a\n * stable string suitable for direct CLI display.\n */\n constructor(\n message = 'caamp.exclusivityMode is set to \"force-pi\" but Pi is not installed. Install Pi (https://github.com/mariozechner/pi-coding-agent) or change the mode with CAAMP_EXCLUSIVITY_MODE=auto.',\n ) {\n super(message);\n this.name = 'PiRequiredError';\n }\n}\n\n/**\n * Type guard that narrows an arbitrary string to {@link ExclusivityMode}.\n *\n * @remarks\n * Used both internally and by callers that need to validate untrusted input\n * (e.g. CLI flag parsers, env var readers) before passing it through to\n * {@link setExclusivityMode}.\n *\n * @param value - Candidate value to validate.\n * @returns `true` when `value` is one of `'auto'`, `'force-pi'`, `'legacy'`.\n *\n * @example\n * ```typescript\n * if (isExclusivityMode(userInput)) {\n * setExclusivityMode(userInput);\n * }\n * ```\n *\n * @public\n */\nexport function isExclusivityMode(value: string): value is ExclusivityMode {\n return value === 'auto' || value === 'force-pi' || value === 'legacy';\n}\n\n/**\n * Resolve the active CAAMP exclusivity mode using the layered precedence\n * documented in {@link DEFAULT_EXCLUSIVITY_MODE}.\n *\n * @remarks\n * Resolution order:\n *\n * 1. Programmatic override (set via {@link setExclusivityMode}).\n * 2. Environment variable `CAAMP_EXCLUSIVITY_MODE`.\n * 3. {@link DEFAULT_EXCLUSIVITY_MODE} (`'auto'`).\n *\n * Invalid environment variable values are silently ignored (resolution\n * falls through to the default) so a typo in CI does not crash the CLI.\n * The value is never cached — every call re-reads the environment so tests\n * that mutate `process.env` see consistent results without needing to\n * reset module state.\n *\n * @returns The currently effective exclusivity mode.\n *\n * @example\n * ```typescript\n * const mode = getExclusivityMode();\n * if (mode === 'force-pi') {\n * // ...\n * }\n * ```\n *\n * @public\n */\nexport function getExclusivityMode(): ExclusivityMode {\n if (programmaticOverride !== null) {\n return programmaticOverride;\n }\n const envValue = process.env[EXCLUSIVITY_MODE_ENV_VAR];\n if (envValue !== undefined && isExclusivityMode(envValue)) {\n return envValue;\n }\n return DEFAULT_EXCLUSIVITY_MODE;\n}\n\n/**\n * Install a programmatic override for the exclusivity mode.\n *\n * @remarks\n * The override takes precedence over the environment variable for the\n * remainder of the process or until {@link resetExclusivityModeOverride}\n * is called. Intended for tests, for future `caamp config set` wiring, and\n * for short-lived runtime adjustments (e.g. a one-shot CLI flag).\n *\n * @param mode - Mode to install.\n *\n * @example\n * ```typescript\n * setExclusivityMode('force-pi');\n * try {\n * await runCommand();\n * } finally {\n * resetExclusivityModeOverride();\n * }\n * ```\n *\n * @public\n */\nexport function setExclusivityMode(mode: ExclusivityMode): void {\n programmaticOverride = mode;\n}\n\n/**\n * Clear any programmatic override installed by {@link setExclusivityMode}.\n *\n * @remarks\n * After calling this, {@link getExclusivityMode} resumes reading from the\n * environment variable (and falls back to the default when the env var is\n * unset or invalid). Idempotent — safe to call when no override is active.\n *\n * @example\n * ```typescript\n * setExclusivityMode(\"force-pi\");\n * // ...run test...\n * resetExclusivityModeOverride();\n * // getExclusivityMode() now reads CAAMP_EXCLUSIVITY_MODE again\n * ```\n *\n * @public\n */\nexport function resetExclusivityModeOverride(): void {\n programmaticOverride = null;\n}\n","/**\n * Pi coding agent harness.\n *\n * @remarks\n * Concrete {@link Harness} implementation for the Pi coding agent\n * (https://github.com/badlogic/pi-mono). Pi is CAAMP's first first-class\n * primary harness: it owns skills, instructions, extensions, and subagent\n * spawning through native filesystem conventions rather than a generic\n * MCP config file.\n *\n * Filesystem layout honoured by this harness:\n * - Global state root: `$PI_CODING_AGENT_DIR` if set, else `~/.pi/agent/`.\n * - Global skills: `<root>/skills/<name>/`\n * - Global extensions: `<root>/extensions/*.ts`\n * - Global settings: `<root>/settings.json`\n * - Global instructions: `<root>/AGENTS.md`\n * - Project skills: `<projectDir>/.pi/skills/<name>/`\n * - Project extensions: `<projectDir>/.pi/extensions/*.ts`\n * - Project settings: `<projectDir>/.pi/settings.json`\n * - Project instructions: `<projectDir>/AGENTS.md` (at project root, NOT under `.pi/`)\n *\n * @packageDocumentation\n */\n\nimport { type ChildProcess, spawn } from 'node:child_process';\nimport { type Dirent, existsSync } from 'node:fs';\nimport {\n appendFile,\n cp,\n mkdir,\n open,\n readdir,\n readFile,\n rename,\n rm,\n stat,\n writeFile,\n} from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, dirname, extname, join } from 'node:path';\nimport { parseDocument, validateDocument } from '@cleocode/cant';\nimport { provisionIsolatedShell } from '@cleocode/contracts';\nimport type { Provider } from '../../types.js';\nimport type { HarnessTier } from './scope.js';\nimport { resolveAllTiers, resolveTierDir } from './scope.js';\nimport type {\n CantProfileCounts,\n CantProfileEntry,\n CantValidationDiagnostic,\n ExtensionEntry,\n Harness,\n HarnessInstallOptions,\n HarnessScope,\n ModelListEntry,\n PiModelProvider,\n PiModelsConfig,\n PromptEntry,\n SessionDocument,\n SessionSummary,\n SubagentExitResult,\n SubagentHandle,\n SubagentLinkEntry,\n SubagentResult,\n SubagentSpawnOptions,\n SubagentStreamEvent,\n SubagentTask,\n ThemeEntry,\n ValidateCantProfileResult,\n} from './types.js';\n\n// ── Marker constants ──────────────────────────────────────────────────\n\n/** Start marker for CAAMP-managed AGENTS.md injection blocks. */\nconst MARKER_START = '<!-- CAAMP:START -->';\n/** End marker for CAAMP-managed AGENTS.md injection blocks. */\nconst MARKER_END = '<!-- CAAMP:END -->';\n/** Matches an entire CAAMP-managed block including its markers. */\nconst MARKER_PATTERN = /<!-- CAAMP:START -->[\\s\\S]*?<!-- CAAMP:END -->/;\n\n// ── Private helpers ───────────────────────────────────────────────────\n\n/**\n * Resolve the Pi global state root directory.\n *\n * @remarks\n * Honours the `PI_CODING_AGENT_DIR` environment variable when set (with\n * `~` expansion), else falls back to `~/.pi/agent`. Kept private to this\n * module so tests can redirect it via the env var.\n */\nfunction getPiAgentDir(): string {\n const env = process.env['PI_CODING_AGENT_DIR'];\n if (env !== undefined && env.length > 0) {\n if (env === '~') return homedir();\n if (env.startsWith('~/')) return join(homedir(), env.slice(2));\n return env;\n }\n return join(homedir(), '.pi', 'agent');\n}\n\n/**\n * Narrow a value to a plain object suitable for deep merge.\n */\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/**\n * Recursively merge `patch` into `target`, returning a new object.\n *\n * @remarks\n * Nested plain objects are merged field-by-field. All other value types\n * (arrays, primitives, `null`) are replaced wholesale by the patch value.\n */\nfunction deepMerge(\n target: Record<string, unknown>,\n patch: Record<string, unknown>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = { ...target };\n for (const [key, value] of Object.entries(patch)) {\n const existing = out[key];\n if (isPlainObject(value) && isPlainObject(existing)) {\n out[key] = deepMerge(existing, value);\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\n/**\n * Write JSON to disk atomically via a tmp-then-rename sequence.\n *\n * @remarks\n * Ensures partial writes cannot leave a corrupted `settings.json` behind\n * if the process dies mid-write. The tmp filename is namespaced by pid\n * to stay unique under parallel runs.\n */\nasync function atomicWriteJson(filePath: string, data: unknown): Promise<void> {\n await mkdir(dirname(filePath), { recursive: true });\n const tmp = `${filePath}.tmp.${process.pid}`;\n await writeFile(tmp, `${JSON.stringify(data, null, 2)}\\n`, 'utf8');\n await rename(tmp, filePath);\n}\n\n// ── Subagent runtime constants & orphan tracker (ADR-035 §D6) ─────────\n\n/**\n * Default SIGTERM grace window before SIGKILL fires.\n *\n * @remarks\n * Per ADR-035 §D6 the configurable default is 5 seconds. Callers can\n * override per-spawn via {@link SubagentSpawnOptions.terminateGraceMs}\n * or globally via `settings.json:pi.subagent.terminateGraceMs`.\n */\nconst DEFAULT_TERMINATE_GRACE_MS = 5000;\n\n/** Maximum number of stderr lines retained per subagent for diagnostics. */\nconst STDERR_RING_BUFFER_SIZE = 100;\n\n/**\n * Internal record describing a live subagent so the module-level orphan\n * sweeper can terminate stragglers on parent exit.\n */\ninterface ActiveSubagent {\n child: ChildProcess;\n subagentId: string;\n terminate: () => void;\n}\n\n/**\n * Set of currently-live subagents owned by this PiHarness module.\n *\n * @remarks\n * Used by {@link ensureOrphanSweeperRegistered} so that on parent\n * shutdown every still-running subagent receives the cleanup signal\n * sequence. Entries are removed as soon as a child exits naturally.\n */\nconst activeSubagents = new Set<ActiveSubagent>();\n\n/** Tracks whether the process-exit orphan sweeper has been registered. */\nlet orphanSweeperRegistered = false;\n\n/**\n * Register a one-shot `process.on('exit', ...)` handler that terminates\n * any still-active subagents when the parent process is shutting down.\n *\n * @remarks\n * Idempotent — subsequent calls are no-ops. The handler walks\n * {@link activeSubagents} and invokes each entry's terminate hook so\n * the SIGTERM-then-SIGKILL sequence runs uniformly across crash and\n * graceful shutdown paths. Synchronous because Node's `'exit'` event\n * does not await async work.\n */\nfunction ensureOrphanSweeperRegistered(): void {\n if (orphanSweeperRegistered) return;\n orphanSweeperRegistered = true;\n const sweeper = (): void => {\n for (const entry of activeSubagents) {\n try {\n entry.terminate();\n } catch {\n // Best-effort: a child that's already gone is fine.\n }\n }\n };\n process.on('exit', sweeper);\n}\n\n/**\n * Generate a short unique suffix for subagent ids and ad-hoc task ids.\n *\n * @remarks\n * Combines a timestamp with `Math.random` for collision resistance\n * inside a single process. Not cryptographically strong — these ids are\n * filesystem identifiers, not credentials.\n */\nfunction generateShortId(): string {\n const ts = Date.now().toString(36);\n const rand = Math.random().toString(36).slice(2, 8);\n return `${ts}${rand}`;\n}\n\n/**\n * Read `settings.json:pi.subagent.terminateGraceMs` from a settings blob.\n *\n * @remarks\n * Tolerant of missing or non-numeric values — falls through to the\n * supplied default when the path does not resolve to a positive finite\n * number. Centralised here so both spawn-time defaulting and tests can\n * share the lookup logic.\n */\nfunction readTerminateGraceFromSettings(settings: unknown, fallback: number): number {\n if (!isPlainObject(settings)) return fallback;\n const piBlock = settings['pi'];\n if (!isPlainObject(piBlock)) return fallback;\n const subBlock = piBlock['subagent'];\n if (!isPlainObject(subBlock)) return fallback;\n const value = subBlock['terminateGraceMs'];\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) return fallback;\n return value;\n}\n\n// ── PiHarness ─────────────────────────────────────────────────────────\n\n/**\n * Pi coding agent harness — CAAMP's first-class primary harness.\n *\n * @remarks\n * Implements the full {@link Harness} contract using Pi's filesystem\n * conventions. All mutating operations are idempotent: re-installing a\n * skill overwrites it cleanly, injecting instructions twice replaces the\n * marker block rather than appending, and removing absent assets is a\n * no-op.\n *\n * @see {@link https://github.com/badlogic/pi-mono | pi-mono}\n *\n * @public\n */\nexport class PiHarness implements Harness {\n /** Provider id, always `\"pi\"`. */\n readonly id = 'pi';\n\n /**\n * Construct a harness bound to a resolved Pi provider.\n *\n * @param provider - The resolved provider entry for `\"pi\"`.\n */\n constructor(readonly provider: Provider) {}\n\n // ── Path helpers ────────────────────────────────────────────────────\n\n /**\n * Resolve the skills directory for a given scope.\n */\n private skillsDir(scope: HarnessScope): string {\n return scope.kind === 'global'\n ? join(getPiAgentDir(), 'skills')\n : join(scope.projectDir, '.pi', 'skills');\n }\n\n /**\n * Resolve the settings.json path for a given scope.\n */\n private settingsPath(scope: HarnessScope): string {\n return scope.kind === 'global'\n ? join(getPiAgentDir(), 'settings.json')\n : join(scope.projectDir, '.pi', 'settings.json');\n }\n\n /**\n * Resolve the AGENTS.md instruction file path for a given scope.\n *\n * @remarks\n * Global scope lives under the Pi state root; project scope lives at\n * the project root (NOT under `.pi/`), matching Pi's convention of\n * auto-discovering `AGENTS.md` from the working directory upwards.\n */\n private agentsMdPath(scope: HarnessScope): string {\n return scope.kind === 'global'\n ? join(getPiAgentDir(), 'AGENTS.md')\n : join(scope.projectDir, 'AGENTS.md');\n }\n\n // ── Skills ──────────────────────────────────────────────────────────\n\n /**\n * Install a skill directory into the resolved Pi skills location.\n */\n async installSkill(sourcePath: string, skillName: string, scope: HarnessScope): Promise<void> {\n const targetDir = join(this.skillsDir(scope), skillName);\n await rm(targetDir, { recursive: true, force: true });\n await mkdir(dirname(targetDir), { recursive: true });\n await cp(sourcePath, targetDir, { recursive: true });\n }\n\n /**\n * Remove a skill directory from the resolved Pi skills location.\n */\n async removeSkill(skillName: string, scope: HarnessScope): Promise<void> {\n const targetDir = join(this.skillsDir(scope), skillName);\n await rm(targetDir, { recursive: true, force: true });\n }\n\n /**\n * List the installed skill directories at the given scope.\n */\n async listSkills(scope: HarnessScope): Promise<string[]> {\n const dir = this.skillsDir(scope);\n if (!existsSync(dir)) return [];\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n }\n\n // ── Instructions ────────────────────────────────────────────────────\n\n /**\n * Inject or replace a CAAMP-managed instruction block inside the Pi\n * `AGENTS.md` file for the resolved scope.\n */\n async injectInstructions(content: string, scope: HarnessScope): Promise<void> {\n const filePath = this.agentsMdPath(scope);\n await mkdir(dirname(filePath), { recursive: true });\n\n const block = `${MARKER_START}\\n${content.trim()}\\n${MARKER_END}`;\n\n let existing = '';\n if (existsSync(filePath)) {\n existing = await readFile(filePath, 'utf8');\n }\n\n let updated: string;\n if (MARKER_PATTERN.test(existing)) {\n updated = existing.replace(MARKER_PATTERN, block);\n } else if (existing.length === 0) {\n updated = `${block}\\n`;\n } else {\n const separator = existing.endsWith('\\n') ? '\\n' : '\\n\\n';\n updated = `${existing}${separator}${block}\\n`;\n }\n await writeFile(filePath, updated, 'utf8');\n }\n\n /**\n * Remove the CAAMP-managed instruction block from the Pi `AGENTS.md`\n * file at the resolved scope.\n */\n async removeInstructions(scope: HarnessScope): Promise<void> {\n const filePath = this.agentsMdPath(scope);\n if (!existsSync(filePath)) return;\n const existing = await readFile(filePath, 'utf8');\n if (!MARKER_PATTERN.test(existing)) return;\n const stripped = existing\n .replace(MARKER_PATTERN, '')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trimEnd();\n await writeFile(filePath, stripped.length === 0 ? '' : `${stripped}\\n`, 'utf8');\n }\n\n // ── Subagent spawn (ADR-035 §D6) ────────────────────────────────────\n\n /**\n * Spawn a subagent through Pi's configured `spawnCommand` and return a\n * live handle bound to the canonical streaming, attribution, and\n * cleanup contract.\n *\n * @remarks\n * Per ADR-035 §D6 this is the **only** sanctioned subagent spawn path\n * in CLEO. All historical direct `child_process.spawn` callers in\n * subagent contexts (including the `cant-bridge.ts` Pi extension and\n * the legacy CLEO orchestrator paths) MUST migrate to this method so\n * the contract below holds uniformly. A custom biome rule banning\n * raw `spawn()` from subagent code is planned for v3 cleanup but is\n * intentionally NOT enforced in v2 to keep the migration incremental.\n *\n * **Streaming semantics** — Pi's `--mode json` produces line-delimited\n * JSON on stdout. The harness:\n *\n * - Line-buffers stdout, parses each line as JSON, and forwards a\n * `{ kind: 'message', subagentId, lineNumber, payload }`\n * {@link SubagentStreamEvent} via {@link SubagentSpawnOptions.onStream}.\n * Non-parseable lines increment a warning counter (recorded in the\n * child session as `{ type: 'raw' }`) but never crash the loop.\n * - Line-buffers stderr separately, forwards each line as\n * `{ kind: 'stderr', subagentId, payload: { line } }`, and stores\n * it in a 100-line ring buffer accessible via\n * {@link SubagentHandle.recentStderr}. Stderr is **never** injected\n * into the parent LLM context per ADR-035 §D6.\n * - Emits a final `{ kind: 'exit', subagentId, payload: SubagentExitResult }`\n * when the child terminates.\n *\n * **Session attribution** — Every spawn produces a child session JSONL\n * file at\n * `~/.pi/agent/sessions/subagents/subagent-{parentSessionId}-{taskId}.jsonl`.\n * The header line records the subagentId, taskId, and parent linkage.\n * When {@link SubagentTask.parentSessionPath} is supplied, a\n * {@link SubagentLinkEntry} is appended to the parent session file as\n * a JSONL line so listing the parent surfaces its children.\n *\n * **Exit propagation** — {@link SubagentHandle.exitPromise} resolves\n * with `{ code, signal, childSessionPath, durationMs }` exactly once\n * when the child exits. The promise NEVER rejects: failure is\n * encoded by a non-zero `code`, a non-null `signal`, or partial\n * output preserved in the child session file.\n *\n * **Cleanup** — {@link SubagentHandle.terminate} sends SIGTERM, waits\n * the configured grace window, then sends SIGKILL if the child is\n * still alive. The grace window is sourced from\n * {@link SubagentSpawnOptions.terminateGraceMs} when supplied,\n * otherwise from `settings.json:pi.subagent.terminateGraceMs`,\n * otherwise from {@link DEFAULT_TERMINATE_GRACE_MS}. A\n * `subagent_exit` entry with reason `terminated` is appended to the\n * child session file when cleanup runs.\n *\n * **Concurrency** — Use the static helpers\n * {@link PiHarness.raceSubagents} and\n * {@link PiHarness.settleAllSubagents} to compose `parallel: race`\n * and `parallel: settle` constructs from CANT workflows over multiple\n * handles.\n *\n * **Orphan handling** — On the first spawn the harness registers a\n * process-wide `'exit'` handler that terminates every still-active\n * subagent so a parent crash never strands children.\n *\n * Throws immediately when the provider entry is missing a\n * `spawnCommand` so callers see configuration errors early rather\n * than at child-exit time.\n *\n * **Worktree isolation (T380/ADR-041 §D2)** — Pass a\n * {@link SubagentSpawnOptions.worktree} handle to bind the spawned\n * process to a physical git worktree. When set, this method:\n * - Uses `worktree.path` as the child cwd.\n * - Injects `CLEO_WORKTREE_ROOT`, `CLEO_WORKTREE_BRANCH`, and\n * `CLEO_PROJECT_HASH` env vars so the child's path resolvers\n * (including `getProjectRoot()` via `worktreeScope`) direct DB I/O\n * to the correct worktree directory.\n *\n * The spawned worker MUST run the worktree guard defined in\n * `packages/agents/cleo-subagent/AGENT.md §WORKTREE GUARD` as its\n * first Bash call. That guard verifies the cwd binding was applied\n * correctly before any file I/O occurs.\n *\n * @param task - Subagent task specification.\n * @param opts - Per-call streaming, cleanup, and worktree overrides.\n * @returns A live subagent handle.\n * @task T380\n */\n async spawnSubagent(\n task: SubagentTask,\n opts: SubagentSpawnOptions = {},\n ): Promise<SubagentHandle> {\n const cmd = this.provider.capabilities.spawn.spawnCommand;\n if (cmd === null || cmd.length === 0) {\n throw new Error(\n 'PiHarness.spawnSubagent: provider has no spawn.spawnCommand in capabilities',\n );\n }\n\n const program = cmd[0];\n if (typeof program !== 'string' || program.length === 0) {\n throw new Error('PiHarness.spawnSubagent: invalid spawnCommand (missing program)');\n }\n\n // Resolve identity + paths up front so they are stable across the\n // streaming + cleanup callbacks below.\n const taskId = task.taskId ?? generateShortId();\n const parentSessionId = task.parentSessionId ?? 'orphan';\n const subagentId = `sub-${taskId}-${generateShortId().slice(0, 6)}`;\n const childSessionPath = join(\n getPiAgentDir(),\n 'sessions',\n 'subagents',\n `subagent-${parentSessionId}-${taskId}.jsonl`,\n );\n await mkdir(dirname(childSessionPath), { recursive: true });\n\n // Resolve the SIGTERM grace window: per-call override → settings.json\n // → hardcoded default. Read settings via the public reader so the\n // standard tolerant-merge contract applies.\n let grace = opts.terminateGraceMs;\n if (grace === undefined) {\n try {\n const settings = await this.readSettings({ kind: 'global' });\n grace = readTerminateGraceFromSettings(settings, DEFAULT_TERMINATE_GRACE_MS);\n } catch {\n grace = DEFAULT_TERMINATE_GRACE_MS;\n }\n }\n if (!Number.isFinite(grace) || grace < 0) {\n grace = DEFAULT_TERMINATE_GRACE_MS;\n }\n\n const startedAt = new Date();\n const startedAtIso = startedAt.toISOString();\n\n // T1759/T380/ADR-041 §D2 — worktree handle drives cwd and env injection.\n //\n // When opts.worktree is set, `provisionIsolatedShell` (centralized utility,\n // T1759) computes the authoritative cwd, env-var block, and boundary\n // contract from a single source of truth. This eliminates the inline env\n // building that previously lived here so PiHarness and orchestrateSpawnExecute\n // share exactly the same implementation.\n //\n // Per-call opts.env overrides still win (merge order below).\n let isolatedCwd: string | undefined;\n let isolatedEnv: Record<string, string> = {};\n if (opts.worktree !== undefined) {\n const isolation = provisionIsolatedShell({\n worktreePath: opts.worktree.path,\n branch: opts.worktree.branch,\n role: 'worker',\n projectHash: opts.worktree.projectHash,\n });\n isolatedCwd = isolation.cwd;\n isolatedEnv = isolation.env;\n }\n\n // Write the child session header so `listSessions` and `showSession`\n // can attribute the file even before the child has produced output.\n const resolvedCwd = isolatedCwd ?? opts.cwd ?? task.cwd ?? process.cwd();\n const sessionHeader = {\n type: 'session',\n version: 3,\n id: subagentId,\n timestamp: startedAtIso,\n cwd: resolvedCwd,\n parentSession: task.parentSessionId ?? null,\n taskId,\n childSessionPath,\n };\n await writeFile(childSessionPath, `${JSON.stringify(sessionHeader)}\\n`, 'utf8');\n\n // Spawn the child. Merge order (lowest to highest priority):\n // process.env < isolatedEnv < task.env < opts.env\n // This ensures per-call secrets win but worktree vars are always present\n // when a worktree handle is supplied. See ADR-041 §D2.\n const baseArgs = cmd.slice(1);\n const args = [...baseArgs, task.prompt];\n const child = spawn(program, args, {\n cwd: isolatedCwd ?? opts.cwd ?? task.cwd,\n env: { ...process.env, ...isolatedEnv, ...task.env, ...opts.env },\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n // Streaming state — line buffers, captured aggregates, and the\n // bounded stderr ring buffer.\n let stdoutAccum = '';\n let stderrAccum = '';\n let stdoutBuffer = '';\n let stderrBuffer = '';\n let stdoutLineNumber = 0;\n let nonJsonLineCount = 0;\n const stderrRing: string[] = [];\n\n const safeOnStream = (event: SubagentStreamEvent): void => {\n if (opts.onStream === undefined) return;\n try {\n opts.onStream(event);\n } catch (err) {\n // Never let user-callback errors abort the spawn loop. Record\n // the failure as a stderr line so it surfaces in diagnostics.\n const message = err instanceof Error ? err.message : String(err);\n stderrRing.push(`[onStream] ${message}`);\n if (stderrRing.length > STDERR_RING_BUFFER_SIZE) stderrRing.shift();\n }\n };\n\n // Track all in-flight appendFile promises so the `'close'` handler can\n // drain them before `exitPromise` resolves. Without this, callers that\n // `await handle.exitPromise` and then immediately read the JSONL file\n // race against pending writes (see caamp pi-harness attribution test).\n const pendingWrites: Promise<void>[] = [];\n\n const writeChildSession = (entry: Record<string, unknown>): void => {\n // Ordering within the file is preserved by Node's append semantics\n // for a single-writer file. We no longer `void` the promise — we\n // track it in `pendingWrites` so the exit path can await completion.\n const writePromise = appendFile(childSessionPath, `${JSON.stringify(entry)}\\n`, 'utf8').catch(\n () => {\n // Disk errors are recorded as a synthetic stderr line so they\n // surface in `recentStderr` without aborting the child.\n const synthetic = `[childSession] failed to append entry`;\n stderrRing.push(synthetic);\n if (stderrRing.length > STDERR_RING_BUFFER_SIZE) stderrRing.shift();\n },\n );\n pendingWrites.push(writePromise);\n };\n\n const flushStdoutBuffer = (final: boolean): void => {\n let nlIdx = stdoutBuffer.indexOf('\\n');\n while (nlIdx !== -1) {\n const line = stdoutBuffer.slice(0, nlIdx);\n stdoutBuffer = stdoutBuffer.slice(nlIdx + 1);\n this.handleStdoutLine(line, {\n subagentId,\n increment: () => ++stdoutLineNumber,\n incrementNonJson: () => ++nonJsonLineCount,\n writeChildSession,\n safeOnStream,\n });\n nlIdx = stdoutBuffer.indexOf('\\n');\n }\n // On final flush, drain any trailing partial line so the JSON\n // parser still gets a chance at it.\n if (final && stdoutBuffer.length > 0) {\n const remainder = stdoutBuffer;\n stdoutBuffer = '';\n this.handleStdoutLine(remainder, {\n subagentId,\n increment: () => ++stdoutLineNumber,\n incrementNonJson: () => ++nonJsonLineCount,\n writeChildSession,\n safeOnStream,\n });\n }\n };\n\n const flushStderrBuffer = (final: boolean): void => {\n let nlIdx = stderrBuffer.indexOf('\\n');\n while (nlIdx !== -1) {\n const line = stderrBuffer.slice(0, nlIdx);\n stderrBuffer = stderrBuffer.slice(nlIdx + 1);\n stderrRing.push(line);\n if (stderrRing.length > STDERR_RING_BUFFER_SIZE) stderrRing.shift();\n writeChildSession({ type: 'subagent_stderr', line });\n safeOnStream({ kind: 'stderr', subagentId, payload: { line } });\n nlIdx = stderrBuffer.indexOf('\\n');\n }\n if (final && stderrBuffer.length > 0) {\n const line = stderrBuffer;\n stderrBuffer = '';\n stderrRing.push(line);\n if (stderrRing.length > STDERR_RING_BUFFER_SIZE) stderrRing.shift();\n writeChildSession({ type: 'subagent_stderr', line });\n safeOnStream({ kind: 'stderr', subagentId, payload: { line } });\n }\n };\n\n child.stdout?.on('data', (chunk: Buffer) => {\n const text = chunk.toString('utf8');\n stdoutAccum += text;\n stdoutBuffer += text;\n flushStdoutBuffer(false);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n const text = chunk.toString('utf8');\n stderrAccum += text;\n stderrBuffer += text;\n flushStderrBuffer(false);\n });\n\n // Cleanup state — single shared object so the terminate path,\n // exitPromise resolver, and orphan sweeper all observe the same\n // values without re-entrancy bugs.\n let terminating = false;\n let terminationReason: 'natural' | 'terminated' = 'natural';\n let terminatePromise: Promise<void> | null = null;\n\n const terminateImpl = (): Promise<void> => {\n if (terminatePromise !== null) return terminatePromise;\n terminating = true;\n terminationReason = 'terminated';\n terminatePromise = terminateSubagent(child, grace ?? DEFAULT_TERMINATE_GRACE_MS);\n return terminatePromise;\n };\n\n // Synchronous variant used by the orphan sweeper (process 'exit' is\n // synchronous and cannot await async work).\n const terminateSync = (): void => {\n if (terminating) return;\n terminating = true;\n terminationReason = 'terminated';\n try {\n child.kill('SIGTERM');\n } catch {\n // Already gone — fine.\n }\n };\n\n // Track this subagent so the orphan sweeper can clean it up if the\n // parent crashes before exit fires.\n const activeRecord: ActiveSubagent = {\n child,\n subagentId,\n terminate: terminateSync,\n };\n activeSubagents.add(activeRecord);\n ensureOrphanSweeperRegistered();\n\n // Wire up the legacy abort-signal channel so existing callers that\n // pass `task.signal` keep working under the new cleanup contract.\n if (task.signal !== undefined) {\n const onAbort = (): void => {\n void terminateImpl();\n };\n if (task.signal.aborted) {\n onAbort();\n } else {\n task.signal.addEventListener('abort', onAbort, { once: true });\n }\n }\n\n // Build the rich exit promise. Resolves on `'close'` (which fires\n // after stdio streams have flushed) so we observe the final stdout\n // chunk before resolving. Also drains all in-flight JSONL writes\n // before resolving so tests (and any other caller) can safely read\n // the child session file immediately after awaiting exitPromise.\n // NEVER rejects.\n const exitPromise: Promise<SubagentExitResult> = new Promise((resolve) => {\n child.on('close', (exitCode, signal) => {\n // Flush any trailing partial lines from both streams so the\n // child session file captures the full conversation.\n flushStdoutBuffer(true);\n flushStderrBuffer(true);\n\n const durationMs = Date.now() - startedAt.getTime();\n writeChildSession({\n type: 'subagent_exit',\n code: exitCode,\n signal,\n reason: terminationReason,\n durationMs,\n nonJsonLineCount,\n });\n\n activeSubagents.delete(activeRecord);\n\n const result: SubagentExitResult = {\n code: exitCode,\n signal,\n childSessionPath,\n durationMs,\n };\n\n // Drain all pending JSONL writes before resolving so callers that\n // read the file immediately after `await handle.exitPromise` see\n // the complete conversation, not a partially-flushed prefix.\n // Settlement (not failure) is what we need — writeChildSession\n // already swallows disk errors internally.\n Promise.allSettled(pendingWrites).then(() => {\n safeOnStream({ kind: 'exit', subagentId, payload: result });\n resolve(result);\n });\n });\n // A spawn failure (e.g. ENOENT) emits 'error' before 'close'.\n // Synthesise a deterministic exit so the promise still resolves\n // and the file system state is consistent.\n child.on('error', () => {\n // 'close' will fire after 'error'; nothing else to do here.\n });\n });\n\n // Build the legacy v1 result promise from the same data so existing\n // callers (and the existing test suite) keep working unchanged.\n const result: Promise<SubagentResult> = exitPromise.then(({ code }) => {\n let parsed: unknown;\n try {\n parsed = JSON.parse(stdoutAccum);\n } catch {\n // Non-JSON aggregate is fine — leave parsed undefined.\n }\n return { exitCode: code, stdout: stdoutAccum, stderr: stderrAccum, parsed };\n });\n\n // Append the parent-side `subagent_link` entry. We do this AFTER the\n // child has been spawned successfully so a spawn failure is not\n // recorded as a live link in the parent file.\n const linkEntry: SubagentLinkEntry = {\n type: 'subagent_link',\n subagentId,\n taskId,\n childSessionPath,\n startedAt: startedAtIso,\n };\n if (task.parentSessionPath !== undefined && task.parentSessionPath.length > 0) {\n try {\n await writeSubagentLink(task.parentSessionPath, linkEntry);\n safeOnStream({ kind: 'link', subagentId, payload: linkEntry });\n } catch {\n // Parent file unwritable — record diagnostically and continue.\n // We do NOT fail the spawn because the child is already running.\n stderrRing.push(`[link] failed to write subagent_link to parent`);\n if (stderrRing.length > STDERR_RING_BUFFER_SIZE) stderrRing.shift();\n }\n }\n\n return {\n subagentId,\n taskId,\n childSessionPath,\n pid: child.pid ?? null,\n startedAt,\n exitPromise,\n result,\n terminate: terminateImpl,\n abort: () => {\n void terminateImpl();\n },\n recentStderr: () => stderrRing.slice(),\n };\n }\n\n /**\n * Race a set of subagent handles, returning the first one that exits.\n *\n * @remarks\n * Maps CANT's `parallel: race` construct (per ADR-035 §D6) onto the\n * canonical {@link spawnSubagent} contract. The losing handles are\n * gracefully terminated via {@link SubagentHandle.terminate} once the\n * first settles so no straggler children outlive the race.\n *\n * @param handles - Subagent handles to race.\n * @returns The {@link SubagentExitResult} of the first child to exit.\n * @throws When `handles` is empty (caller bug — a race over zero\n * children has no winner).\n */\n static async raceSubagents(handles: SubagentHandle[]): Promise<SubagentExitResult> {\n if (handles.length === 0) {\n throw new Error('PiHarness.raceSubagents: cannot race an empty handle list');\n }\n // Tag each promise with its index so we can identify the loser set.\n const tagged = handles.map((handle, index) =>\n handle.exitPromise.then((value) => ({ index, value })),\n );\n const winner = await Promise.race(tagged);\n // Terminate the losers in parallel; ignore individual errors so a\n // single stuck child cannot block the race resolution.\n const losers: Promise<void>[] = [];\n for (let i = 0; i < handles.length; i += 1) {\n if (i === winner.index) continue;\n const loser = handles[i];\n if (loser === undefined) continue;\n losers.push(loser.terminate().catch(() => undefined));\n }\n await Promise.all(losers);\n return winner.value;\n }\n\n /**\n * Settle a set of subagent handles, returning a parallel array of\n * results.\n *\n * @remarks\n * Maps CANT's `parallel: settle` construct (per ADR-035 §D6) onto the\n * canonical {@link spawnSubagent} contract. Because\n * {@link SubagentHandle.exitPromise} never rejects, every entry in\n * the returned array is `{ status: 'fulfilled', value: ... }` under\n * normal operation; the `PromiseSettledResult` shape is preserved\n * for forward compatibility with future failure modes.\n *\n * @param handles - Subagent handles to settle.\n * @returns Parallel array of settled exit results, one per input.\n */\n static async settleAllSubagents(\n handles: SubagentHandle[],\n ): Promise<PromiseSettledResult<SubagentExitResult>[]> {\n return Promise.allSettled(handles.map((h) => h.exitPromise));\n }\n\n /**\n * Per-line stdout dispatcher used by the streaming buffer flusher.\n *\n * @remarks\n * Extracted as a private method so the line-handling logic stays\n * close to {@link spawnSubagent} but does not bloat the parent\n * function. Skips empty lines (a leading newline produces a zero-\n * length entry that has no semantic meaning).\n */\n private handleStdoutLine(\n rawLine: string,\n ctx: {\n subagentId: string;\n increment: () => number;\n incrementNonJson: () => number;\n writeChildSession: (entry: Record<string, unknown>) => void;\n safeOnStream: (event: SubagentStreamEvent) => void;\n },\n ): void {\n // Pi prints `\\r\\n` on Windows and `\\n` on POSIX; strip a trailing\n // CR so JSON.parse never sees it.\n const trimmed = rawLine.endsWith('\\r') ? rawLine.slice(0, -1) : rawLine;\n if (trimmed.length === 0) return;\n const lineNumber = ctx.increment();\n let parsed: unknown;\n try {\n parsed = JSON.parse(trimmed);\n } catch {\n ctx.incrementNonJson();\n ctx.writeChildSession({ type: 'raw', lineNumber, line: trimmed });\n return;\n }\n ctx.writeChildSession({ type: 'custom_message', lineNumber, payload: parsed });\n ctx.safeOnStream({\n kind: 'message',\n subagentId: ctx.subagentId,\n lineNumber,\n payload: parsed,\n });\n }\n\n // ── Settings ────────────────────────────────────────────────────────\n\n /**\n * Read the Pi `settings.json` file for the resolved scope.\n */\n async readSettings(scope: HarnessScope): Promise<unknown> {\n const filePath = this.settingsPath(scope);\n if (!existsSync(filePath)) return {};\n const raw = await readFile(filePath, 'utf8');\n try {\n return JSON.parse(raw);\n } catch {\n return {};\n }\n }\n\n /**\n * Merge a partial patch into the Pi `settings.json` file for the\n * resolved scope using an atomic write.\n */\n async writeSettings(patch: Record<string, unknown>, scope: HarnessScope): Promise<void> {\n const filePath = this.settingsPath(scope);\n const current = await this.readSettings(scope);\n const currentObj = isPlainObject(current) ? current : {};\n const merged = deepMerge(currentObj, patch);\n await atomicWriteJson(filePath, merged);\n }\n\n /**\n * Persist the supplied model-name patterns into `settings.enabledModels`\n * at the resolved scope.\n */\n async configureModels(modelPatterns: string[], scope: HarnessScope): Promise<void> {\n await this.writeSettings({ enabledModels: modelPatterns }, scope);\n }\n\n // ── Wave-1 three-tier helpers ───────────────────────────────────────\n\n /**\n * Resolve the `models.json` path for a given legacy two-tier scope.\n *\n * @remarks\n * Lives next to `settings.json`. Global scope uses the Pi state root,\n * project scope uses the project's `.pi/` directory, matching the\n * dual-file authority model documented in ADR-035 §D3.\n */\n private modelsConfigPath(scope: HarnessScope): string {\n return scope.kind === 'global'\n ? join(getPiAgentDir(), 'models.json')\n : join(scope.projectDir, '.pi', 'models.json');\n }\n\n /**\n * Resolve the sessions directory — always user-tier because Pi owns\n * session storage and the three-tier model folds session listings to\n * the single authoritative location per ADR-035 §D2.\n */\n private sessionsDir(): string {\n return join(getPiAgentDir(), 'sessions');\n }\n\n // ── Extensions (Wave-1, T263) ───────────────────────────────────────\n\n /**\n * Install a Pi extension `.ts` source file into the resolved tier's\n * extensions directory, validating that it has a default export.\n */\n async installExtension(\n sourcePath: string,\n name: string,\n tier: HarnessTier,\n projectDir?: string,\n opts?: HarnessInstallOptions,\n ): Promise<{ targetPath: string; tier: HarnessTier }> {\n if (!existsSync(sourcePath)) {\n throw new Error(`installExtension: source file does not exist: ${sourcePath}`);\n }\n const stats = await stat(sourcePath);\n if (!stats.isFile()) {\n throw new Error(`installExtension: source path is not a regular file: ${sourcePath}`);\n }\n\n const ext = extname(sourcePath);\n if (ext !== '.ts' && ext !== '.tsx' && ext !== '.mts') {\n throw new Error(\n `installExtension: expected a TypeScript source file (.ts/.tsx/.mts), got: ${ext || '(no extension)'}`,\n );\n }\n\n const contents = await readFile(sourcePath, 'utf8');\n if (!/\\bexport\\s+default\\b/.test(contents)) {\n throw new Error(\n `installExtension: source file is missing an 'export default' — Pi extensions must export a default function`,\n );\n }\n\n const dir = resolveTierDir({ tier, kind: 'extensions', projectDir });\n const targetPath = join(dir, `${name}.ts`);\n\n if (existsSync(targetPath) && opts?.force !== true) {\n throw new Error(\n `installExtension: target already exists at ${targetPath} (pass --force to overwrite)`,\n );\n }\n\n await mkdir(dir, { recursive: true });\n await writeFile(targetPath, contents, 'utf8');\n return { targetPath, tier };\n }\n\n /**\n * Remove a Pi extension `.ts` source file from the resolved tier.\n */\n async removeExtension(name: string, tier: HarnessTier, projectDir?: string): Promise<boolean> {\n const dir = resolveTierDir({ tier, kind: 'extensions', projectDir });\n const targetPath = join(dir, `${name}.ts`);\n if (!existsSync(targetPath)) return false;\n await rm(targetPath, { force: true });\n return true;\n }\n\n /**\n * List Pi extension files across every tier in precedence order,\n * flagging shadowed entries from lower tiers.\n */\n async listExtensions(projectDir?: string): Promise<ExtensionEntry[]> {\n const tiers = resolveAllTiers('extensions', projectDir);\n const out: ExtensionEntry[] = [];\n const seenNames = new Set<string>();\n\n for (const { tier, dir } of tiers) {\n if (!existsSync(dir)) continue;\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n const fileName = entry.name;\n if (!fileName.endsWith('.ts')) continue;\n const name = fileName.slice(0, -'.ts'.length);\n const shadowed = seenNames.has(name);\n out.push({\n name,\n tier,\n path: join(dir, fileName),\n shadowed,\n });\n seenNames.add(name);\n }\n }\n\n return out;\n }\n\n // ── Sessions (Wave-1, T264) ─────────────────────────────────────────\n\n /**\n * List Pi session JSONL files (including subagent children when\n * requested), summarising only the header line per file.\n */\n async listSessions(opts?: { includeSubagents?: boolean }): Promise<SessionSummary[]> {\n const rootDir = this.sessionsDir();\n if (!existsSync(rootDir)) return [];\n\n const files: string[] = [];\n\n // Top-level `*.jsonl` files.\n let rootEntries: Dirent[];\n try {\n rootEntries = await readdir(rootDir, { withFileTypes: true });\n } catch {\n return [];\n }\n for (const entry of rootEntries) {\n if (entry.isFile() && entry.name.endsWith('.jsonl')) {\n files.push(join(rootDir, entry.name));\n }\n }\n\n // Subagents subdir (per ADR-035 §D6 session attribution convention).\n if (opts?.includeSubagents !== false) {\n const subDir = join(rootDir, 'subagents');\n if (existsSync(subDir)) {\n try {\n const subEntries = await readdir(subDir, { withFileTypes: true });\n for (const entry of subEntries) {\n if (entry.isFile() && entry.name.endsWith('.jsonl')) {\n files.push(join(subDir, entry.name));\n }\n }\n } catch {\n // Ignore — treat as empty.\n }\n }\n }\n\n const summaries: SessionSummary[] = [];\n for (const filePath of files) {\n const summary = await readSessionHeader(filePath);\n if (summary !== null) {\n summaries.push(summary);\n }\n }\n\n summaries.sort((a, b) => b.mtimeMs - a.mtimeMs);\n return summaries;\n }\n\n /**\n * Show the full entries of a single Pi session by id.\n */\n async showSession(id: string): Promise<SessionDocument> {\n const summaries = await this.listSessions({ includeSubagents: true });\n const match = summaries.find((s) => s.id === id);\n if (match === undefined) {\n throw new Error(`showSession: no session found with id ${id}`);\n }\n\n const raw = await readFile(match.filePath, 'utf8');\n const allLines = raw.split('\\n');\n // Strip trailing empty lines (JSONL files often end with a newline).\n while (allLines.length > 0 && allLines[allLines.length - 1] === '') {\n allLines.pop();\n }\n // First line is the header (already in `match`); drop it from entries.\n const entries = allLines.slice(1);\n return { summary: match, entries };\n }\n\n // ── Models (Wave-1, T265) ───────────────────────────────────────────\n\n /**\n * Read the Pi `models.json` file for the resolved scope, tolerating\n * missing or malformed files by returning an empty provider map.\n */\n async readModelsConfig(scope: HarnessScope): Promise<PiModelsConfig> {\n const filePath = this.modelsConfigPath(scope);\n if (!existsSync(filePath)) return { providers: {} };\n let raw: string;\n try {\n raw = await readFile(filePath, 'utf8');\n } catch {\n return { providers: {} };\n }\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!isPlainObject(parsed)) return { providers: {} };\n const providersField = parsed['providers'];\n if (!isPlainObject(providersField)) return { providers: {} };\n const providers: Record<string, PiModelProvider> = {};\n for (const [id, block] of Object.entries(providersField)) {\n if (isPlainObject(block)) {\n providers[id] = block as PiModelProvider;\n }\n }\n return { providers };\n } catch {\n return { providers: {} };\n }\n }\n\n /**\n * Write the Pi `models.json` file for the resolved scope via an atomic\n * tmp-then-rename sequence.\n */\n async writeModelsConfig(config: PiModelsConfig, scope: HarnessScope): Promise<void> {\n const filePath = this.modelsConfigPath(scope);\n await atomicWriteJson(filePath, config);\n }\n\n /**\n * Compose a flat `ModelListEntry` list from `models.json` plus the\n * `enabledModels` and default-model hints in `settings.json`.\n */\n async listModels(scope: HarnessScope): Promise<ModelListEntry[]> {\n const models = await this.readModelsConfig(scope);\n const settings = await this.readSettings(scope);\n const settingsObj = isPlainObject(settings) ? settings : {};\n const enabledRaw = settingsObj['enabledModels'];\n const enabled = Array.isArray(enabledRaw)\n ? enabledRaw.filter((v): v is string => typeof v === 'string')\n : [];\n const defaultModel =\n typeof settingsObj['defaultModel'] === 'string' ? settingsObj['defaultModel'] : null;\n const defaultProvider =\n typeof settingsObj['defaultProvider'] === 'string' ? settingsObj['defaultProvider'] : null;\n\n const out: ModelListEntry[] = [];\n const seen = new Set<string>();\n\n // 1. Emit every custom model defined in models.json.\n for (const [providerId, providerBlock] of Object.entries(models.providers)) {\n const modelDefs = providerBlock.models ?? [];\n for (const def of modelDefs) {\n const key = `${providerId}:${def.id}`;\n seen.add(key);\n const isEnabled = enabled.includes(key) || enabled.includes(`${providerId}/*`);\n const isDefault = defaultProvider === providerId && defaultModel === def.id;\n out.push({\n provider: providerId,\n id: def.id,\n name: def.name ?? null,\n enabled: isEnabled,\n isDefault,\n custom: true,\n });\n }\n }\n\n // 2. Emit any enabled selection that was NOT already represented by a\n // custom definition. These resolve against Pi's built-in registry.\n for (const selection of enabled) {\n // Skip glob-only patterns (no concrete model id).\n if (!selection.includes(':') && !selection.includes('/')) continue;\n // Parse \"provider:model-id\" or \"provider/model-id\".\n const match = selection.match(/^([^:/]+)[:/]([^:/].*)$/);\n if (match === null) continue;\n const provider = match[1];\n const id = match[2];\n if (provider === undefined || id === undefined) continue;\n if (id.endsWith('*')) continue; // glob, not a concrete id\n const key = `${provider}:${id}`;\n if (seen.has(key)) continue;\n seen.add(key);\n const isDefault = defaultProvider === provider && defaultModel === id;\n out.push({\n provider,\n id,\n name: null,\n enabled: true,\n isDefault,\n custom: false,\n });\n }\n\n // 3. Surface a bare default selection even if it is not in the\n // enabled list (Pi treats `defaultModel` as authoritative).\n if (\n defaultProvider !== null &&\n defaultModel !== null &&\n !seen.has(`${defaultProvider}:${defaultModel}`)\n ) {\n out.push({\n provider: defaultProvider,\n id: defaultModel,\n name: null,\n enabled: false,\n isDefault: true,\n custom: false,\n });\n }\n\n return out;\n }\n\n // ── Prompts (Wave-1, T266) ──────────────────────────────────────────\n\n /**\n * Install a Pi prompt directory (containing `prompt.md`) into the\n * resolved tier's prompts directory.\n */\n async installPrompt(\n sourceDir: string,\n name: string,\n tier: HarnessTier,\n projectDir?: string,\n opts?: HarnessInstallOptions,\n ): Promise<{ targetPath: string; tier: HarnessTier }> {\n if (!existsSync(sourceDir)) {\n throw new Error(`installPrompt: source directory does not exist: ${sourceDir}`);\n }\n const stats = await stat(sourceDir);\n if (!stats.isDirectory()) {\n throw new Error(`installPrompt: source path is not a directory: ${sourceDir}`);\n }\n if (!existsSync(join(sourceDir, 'prompt.md'))) {\n throw new Error(`installPrompt: source directory is missing a prompt.md file: ${sourceDir}`);\n }\n\n const baseDir = resolveTierDir({ tier, kind: 'prompts', projectDir });\n const targetPath = join(baseDir, name);\n\n if (existsSync(targetPath)) {\n if (opts?.force !== true) {\n throw new Error(\n `installPrompt: target already exists at ${targetPath} (pass --force to overwrite)`,\n );\n }\n await rm(targetPath, { recursive: true, force: true });\n }\n\n await mkdir(baseDir, { recursive: true });\n await cp(sourceDir, targetPath, { recursive: true });\n return { targetPath, tier };\n }\n\n /**\n * List Pi prompt directories across every tier in precedence order,\n * flagging shadowed entries from lower tiers.\n */\n async listPrompts(projectDir?: string): Promise<PromptEntry[]> {\n const tiers = resolveAllTiers('prompts', projectDir);\n const out: PromptEntry[] = [];\n const seenNames = new Set<string>();\n\n for (const { tier, dir } of tiers) {\n if (!existsSync(dir)) continue;\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n const name = entry.name;\n // Token-efficient list: NEVER read prompt bodies — only the\n // directory name is surfaced per ADR-035 spec hook T266.\n const shadowed = seenNames.has(name);\n out.push({\n name,\n tier,\n path: join(dir, name),\n shadowed,\n });\n seenNames.add(name);\n }\n }\n\n return out;\n }\n\n /**\n * Remove a Pi prompt directory from the resolved tier.\n */\n async removePrompt(name: string, tier: HarnessTier, projectDir?: string): Promise<boolean> {\n const dir = resolveTierDir({ tier, kind: 'prompts', projectDir });\n const targetPath = join(dir, name);\n if (!existsSync(targetPath)) return false;\n await rm(targetPath, { recursive: true, force: true });\n return true;\n }\n\n // ── Themes (Wave-1, T267) ───────────────────────────────────────────\n\n /**\n * Install a Pi theme file (`.ts`/`.tsx`/`.mts`/`.json`) into the\n * resolved tier's themes directory, blocking same-stem conflicts\n * unless `--force` is supplied.\n */\n async installTheme(\n sourceFile: string,\n name: string,\n tier: HarnessTier,\n projectDir?: string,\n opts?: HarnessInstallOptions,\n ): Promise<{ targetPath: string; tier: HarnessTier }> {\n if (!existsSync(sourceFile)) {\n throw new Error(`installTheme: source file does not exist: ${sourceFile}`);\n }\n const stats = await stat(sourceFile);\n if (!stats.isFile()) {\n throw new Error(`installTheme: source path is not a regular file: ${sourceFile}`);\n }\n const ext = extname(sourceFile);\n if (ext !== '.ts' && ext !== '.tsx' && ext !== '.mts' && ext !== '.json') {\n throw new Error(\n `installTheme: expected a theme file (.ts/.tsx/.mts/.json), got: ${ext || '(no extension)'}`,\n );\n }\n\n const dir = resolveTierDir({ tier, kind: 'themes', projectDir });\n const targetPath = join(dir, `${name}${ext}`);\n\n if (existsSync(targetPath) && opts?.force !== true) {\n throw new Error(\n `installTheme: target already exists at ${targetPath} (pass --force to overwrite)`,\n );\n }\n\n // Also block installing a .ts theme when a .json with the same stem\n // exists (and vice versa) unless force is set.\n const otherExts = ['.ts', '.tsx', '.mts', '.json'].filter((e) => e !== ext);\n for (const otherExt of otherExts) {\n const otherPath = join(dir, `${name}${otherExt}`);\n if (existsSync(otherPath) && opts?.force !== true) {\n throw new Error(\n `installTheme: conflicting theme exists at ${otherPath} (pass --force to overwrite both)`,\n );\n }\n if (existsSync(otherPath) && opts?.force === true) {\n await rm(otherPath, { force: true });\n }\n }\n\n await mkdir(dir, { recursive: true });\n const contents = await readFile(sourceFile);\n await writeFile(targetPath, contents);\n return { targetPath, tier };\n }\n\n /**\n * List Pi theme files across every tier in precedence order, flagging\n * shadowed entries from lower tiers.\n */\n async listThemes(projectDir?: string): Promise<ThemeEntry[]> {\n const tiers = resolveAllTiers('themes', projectDir);\n const out: ThemeEntry[] = [];\n const seenNames = new Set<string>();\n const validExts = new Set(['.ts', '.tsx', '.mts', '.json']);\n\n for (const { tier, dir } of tiers) {\n if (!existsSync(dir)) continue;\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n const fileExt = extname(entry.name);\n if (!validExts.has(fileExt)) continue;\n const name = entry.name.slice(0, -fileExt.length);\n const shadowed = seenNames.has(name);\n out.push({\n name,\n tier,\n path: join(dir, entry.name),\n fileExt,\n shadowed,\n });\n seenNames.add(name);\n }\n }\n\n return out;\n }\n\n /**\n * Remove a Pi theme from the resolved tier, matching any of the\n * supported theme extensions for the given name stem.\n */\n async removeTheme(name: string, tier: HarnessTier, projectDir?: string): Promise<boolean> {\n const dir = resolveTierDir({ tier, kind: 'themes', projectDir });\n let removed = false;\n for (const ext of ['.ts', '.tsx', '.mts', '.json']) {\n const targetPath = join(dir, `${name}${ext}`);\n if (existsSync(targetPath)) {\n await rm(targetPath, { force: true });\n removed = true;\n }\n }\n return removed;\n }\n\n // ── CANT profiles (Wave-1, T276) ────────────────────────────────────\n\n /**\n * Install a `.cant` profile into the resolved tier after passing it\n * through the cant-core validator.\n *\n * Validates the source via {@link PiHarness.validateCantProfile}\n * before copying so we never persist a `.cant` file the runtime bridge\n * cannot load. The target layout is `<tier-root>/cant/<name>.cant`,\n * resolved through `resolveTierDir` so the project/user/global\n * hierarchy stays consistent with the other Wave-1 verbs.\n */\n async installCantProfile(\n sourcePath: string,\n name: string,\n tier: HarnessTier,\n projectDir?: string,\n opts?: HarnessInstallOptions,\n ): Promise<{ targetPath: string; tier: HarnessTier; counts: CantProfileCounts }> {\n if (!existsSync(sourcePath)) {\n throw new Error(`installCantProfile: source file does not exist: ${sourcePath}`);\n }\n const stats = await stat(sourcePath);\n if (!stats.isFile()) {\n throw new Error(`installCantProfile: source path is not a regular file: ${sourcePath}`);\n }\n\n const ext = extname(sourcePath);\n if (ext !== '.cant') {\n throw new Error(\n `installCantProfile: expected a CANT source file (.cant), got: ${ext || '(no extension)'}`,\n );\n }\n\n // Hard validation gate: refuse to install a profile cant-core rejects.\n const validation = await this.validateCantProfile(sourcePath);\n if (!validation.valid) {\n const firstError =\n validation.errors.find((e) => e.severity === 'error') ?? validation.errors[0];\n const detail =\n firstError !== undefined\n ? ` (${firstError.ruleId} at ${firstError.line}:${firstError.col}: ${firstError.message})`\n : '';\n throw new Error(`installCantProfile: source file failed cant-core validation${detail}`);\n }\n\n const dir = resolveTierDir({ tier, kind: 'cant', projectDir });\n const targetPath = join(dir, `${name}.cant`);\n\n if (existsSync(targetPath) && opts?.force !== true) {\n throw new Error(\n `installCantProfile: target already exists at ${targetPath} (pass --force to overwrite)`,\n );\n }\n\n const contents = await readFile(sourcePath);\n await mkdir(dir, { recursive: true });\n await writeFile(targetPath, contents);\n return { targetPath, tier, counts: validation.counts };\n }\n\n /**\n * Remove a `.cant` profile from the resolved tier.\n */\n async removeCantProfile(name: string, tier: HarnessTier, projectDir?: string): Promise<boolean> {\n const dir = resolveTierDir({ tier, kind: 'cant', projectDir });\n const targetPath = join(dir, `${name}.cant`);\n if (!existsSync(targetPath)) return false;\n await rm(targetPath, { force: true });\n return true;\n }\n\n /**\n * List installed `.cant` profiles across every tier in precedence\n * order, parsing each file to extract section counts.\n *\n * Walks every tier in `TIER_PRECEDENCE` order, parsing each\n * discovered `.cant` file via cant-core to extract a\n * {@link CantProfileCounts} bag. Higher-precedence tiers shadow\n * lower-precedence entries with the same name; shadowed entries\n * still appear in the result but carry the\n * `shadowedByHigherTier` flag so callers can render the precedence\n * story without losing visibility of the duplicate.\n */\n async listCantProfiles(projectDir?: string): Promise<CantProfileEntry[]> {\n const tiers = resolveAllTiers('cant', projectDir);\n const out: CantProfileEntry[] = [];\n const seenNames = new Set<string>();\n\n for (const { tier, dir } of tiers) {\n if (!existsSync(dir)) continue;\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n continue;\n }\n for (const entry of entries) {\n if (!entry.isFile()) continue;\n const fileName = entry.name;\n if (!fileName.endsWith('.cant')) continue;\n const name = fileName.slice(0, -'.cant'.length);\n const sourcePath = join(dir, fileName);\n const counts = await extractCantCounts(sourcePath);\n const shadowed = seenNames.has(name);\n const profile: CantProfileEntry = {\n name,\n tier,\n sourcePath,\n counts,\n };\n if (shadowed) {\n profile.shadowedByHigherTier = true;\n }\n out.push(profile);\n seenNames.add(name);\n }\n }\n\n return out;\n }\n\n /**\n * Validate a `.cant` source file against cant-core's parser and\n * 42-rule linter, returning section counts and per-diagnostic detail.\n *\n * Pure validator. Reads the file, runs `parseDocument` to derive\n * counts (when parsing succeeds) and `validateDocument` to collect\n * the 42-rule diagnostic feed. The two calls are kept independent so\n * we can still report counts for files that pass parsing but fail a\n * lint rule.\n */\n async validateCantProfile(sourcePath: string): Promise<ValidateCantProfileResult> {\n if (!existsSync(sourcePath)) {\n throw new Error(`validateCantProfile: source file does not exist: ${sourcePath}`);\n }\n const stats = await stat(sourcePath);\n if (!stats.isFile()) {\n throw new Error(`validateCantProfile: source path is not a regular file: ${sourcePath}`);\n }\n\n const counts = await extractCantCounts(sourcePath);\n const validation = await validateDocument(sourcePath);\n const errors: CantValidationDiagnostic[] = validation.diagnostics.map((d) => ({\n ruleId: d.ruleId,\n message: d.message,\n line: d.line,\n col: d.col,\n severity: normaliseSeverity(d.severity),\n }));\n\n return {\n valid: validation.valid,\n errors,\n counts,\n };\n }\n}\n\n// ── Private subagent runtime helpers (ADR-035 §D6) ─────────────────────\n\n/**\n * Terminate a child process via the canonical SIGTERM-then-SIGKILL\n * cleanup sequence used by {@link PiHarness.spawnSubagent}.\n *\n * @remarks\n * Sends SIGTERM, polls every 25 ms (or `min(graceMs, 25)` ms when the\n * grace window is shorter than the poll interval, to keep tests fast),\n * and escalates to SIGKILL once the grace window has elapsed if the\n * child is still alive. Tolerates a child that has already exited at\n * any point in the sequence — `child.kill` on a dead pid is a no-op.\n *\n * The promise resolves once the child has emitted `'close'` so callers\n * can be sure the cleanup is complete before continuing.\n */\nasync function terminateSubagent(child: ChildProcess, graceMs: number): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) {\n return;\n }\n try {\n child.kill('SIGTERM');\n } catch {\n // Already dead.\n return;\n }\n\n const pollInterval = Math.min(25, Math.max(1, graceMs));\n const deadline = Date.now() + graceMs;\n\n await new Promise<void>((resolve) => {\n const timer = setInterval(() => {\n if (child.exitCode !== null || child.signalCode !== null) {\n clearInterval(timer);\n resolve();\n return;\n }\n if (Date.now() >= deadline) {\n clearInterval(timer);\n try {\n child.kill('SIGKILL');\n } catch {\n // Already dead.\n }\n resolve();\n }\n }, pollInterval);\n\n // Belt-and-braces: if 'close' fires before the polling tick, resolve\n // immediately so callers do not wait the full grace window.\n child.once('close', () => {\n clearInterval(timer);\n resolve();\n });\n });\n}\n\n/**\n * Append a {@link SubagentLinkEntry} to a parent session JSONL file.\n *\n * @remarks\n * Creates the parent directory if needed and uses an atomic\n * single-line append so the entry is well-formed under concurrent\n * writers. Throws on disk errors so the caller can record the failure\n * diagnostically; {@link PiHarness.spawnSubagent} catches the throw\n * and surfaces it via the stderr ring buffer rather than aborting the\n * spawn.\n */\nasync function writeSubagentLink(\n parentSessionPath: string,\n entry: SubagentLinkEntry,\n): Promise<void> {\n await mkdir(dirname(parentSessionPath), { recursive: true });\n // Wrap the typed entry as a Pi `custom` JSONL entry. The original\n // `type` field is preserved as `subtype` so the parent session loader\n // can still discriminate `subagent_link` records when listing.\n const wrapped = {\n type: 'custom',\n subtype: entry.type,\n subagentId: entry.subagentId,\n taskId: entry.taskId,\n childSessionPath: entry.childSessionPath,\n startedAt: entry.startedAt,\n };\n const line = `${JSON.stringify(wrapped)}\\n`;\n await appendFile(parentSessionPath, line, 'utf8');\n}\n\n// ── Private session-header helper ──────────────────────────────────────\n\n/**\n * Read only the first line of a Pi session JSONL file and extract the\n * header summary.\n *\n * @remarks\n * Implements the ADR-035 §D2 rule that session listings MUST NOT read\n * past line 1. Uses a buffered file handle so we never pull more than\n * the first chunk off disk. Returns `null` when the file is empty,\n * unreadable, or its header is malformed — callers skip null entries.\n */\nasync function readSessionHeader(filePath: string): Promise<SessionSummary | null> {\n let handle: Awaited<ReturnType<typeof open>> | null = null;\n try {\n handle = await open(filePath, 'r');\n const stats = await handle.stat();\n const capacity = Math.min(stats.size, 64 * 1024);\n if (capacity === 0) return null;\n const buffer = Buffer.alloc(capacity);\n const { bytesRead } = await handle.read(buffer, 0, capacity, 0);\n const text = buffer.subarray(0, bytesRead).toString('utf8');\n const newlineIdx = text.indexOf('\\n');\n const firstLine = newlineIdx === -1 ? text : text.slice(0, newlineIdx);\n if (firstLine.trim().length === 0) return null;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(firstLine);\n } catch {\n return null;\n }\n if (!isPlainObject(parsed)) return null;\n const id = typeof parsed['id'] === 'string' ? parsed['id'] : null;\n if (id === null) {\n // Fall back to file stem if the header has no id — preserves the\n // file in the listing rather than dropping it silently.\n const stem = basename(filePath, '.jsonl');\n return {\n id: stem,\n version: typeof parsed['version'] === 'number' ? parsed['version'] : 0,\n timestamp: typeof parsed['timestamp'] === 'string' ? parsed['timestamp'] : null,\n cwd: typeof parsed['cwd'] === 'string' ? parsed['cwd'] : null,\n parentSession: typeof parsed['parentSession'] === 'string' ? parsed['parentSession'] : null,\n filePath,\n mtimeMs: stats.mtimeMs,\n };\n }\n\n return {\n id,\n version: typeof parsed['version'] === 'number' ? parsed['version'] : 0,\n timestamp: typeof parsed['timestamp'] === 'string' ? parsed['timestamp'] : null,\n cwd: typeof parsed['cwd'] === 'string' ? parsed['cwd'] : null,\n parentSession: typeof parsed['parentSession'] === 'string' ? parsed['parentSession'] : null,\n filePath,\n mtimeMs: stats.mtimeMs,\n };\n } catch {\n return null;\n } finally {\n if (handle !== null) {\n await handle.close().catch(() => {\n // Ignore close errors — we're already returning.\n });\n }\n }\n}\n\n// ── Private CANT helpers (T276) ────────────────────────────────────────\n\n/** Empty count bag returned when parsing fails. */\nconst EMPTY_CANT_COUNTS: CantProfileCounts = {\n agentCount: 0,\n workflowCount: 0,\n pipelineCount: 0,\n hookCount: 0,\n skillCount: 0,\n};\n\n/**\n * Narrow a value to a record so we can safely walk the cant-core AST.\n */\nfunction isRecord(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\n/**\n * Extract a string value from a cant-core spanned-name node.\n *\n * @remarks\n * Cant-core wraps identifiers/property keys in a `{ span, value }`\n * envelope. This helper unwraps the envelope or accepts a raw string,\n * returning `null` for anything else.\n */\nfunction unwrapSpanned(value: unknown): string | null {\n if (typeof value === 'string') return value;\n if (isRecord(value) && typeof value['value'] === 'string') {\n return value['value'];\n }\n return null;\n}\n\n/**\n * Drill into a cant-core property `value` union and pull out the\n * declared skill names from a `skills:` array.\n *\n * @remarks\n * The cant-core AST encodes property values as discriminated objects\n * like `{ Array: [{ String: { raw: \"ct-cleo\" } }, ...] }` or\n * `{ Identifier: \"name\" }`. This helper walks just the shape used by\n * `skills: [\"ct-cleo\", \"ct-task-executor\"]` and pushes every string\n * literal into `out`. It is intentionally tolerant: anything that does\n * not match the expected shape is ignored rather than thrown.\n */\nfunction collectSkillNames(value: unknown, out: Set<string>): void {\n if (!isRecord(value)) return;\n const arr = value['Array'];\n if (!Array.isArray(arr)) return;\n for (const item of arr) {\n if (!isRecord(item)) continue;\n const stringWrapper = item['String'];\n if (isRecord(stringWrapper) && typeof stringWrapper['raw'] === 'string') {\n out.add(stringWrapper['raw']);\n continue;\n }\n const identWrapper = item['Identifier'];\n if (typeof identWrapper === 'string') {\n out.add(identWrapper);\n }\n }\n}\n\n/**\n * Parse a `.cant` file and return its top-level section counts.\n *\n * @remarks\n * Used by both {@link PiHarness.listCantProfiles} and\n * {@link PiHarness.validateCantProfile}. Walks\n * `document.sections` (a tagged-union array where each element is a\n * single-key object such as `{ Agent: ... }`, `{ Workflow: ... }`,\n * `{ Pipeline: ... }`, `{ Hook: ... }`, `{ Comment: ... }`) and tallies\n * each section type. Hook bodies nested inside an Agent section's\n * `hooks` array are added to {@link CantProfileCounts.hookCount}, and\n * skill names referenced via the agent's `skills:` property are\n * de-duplicated into {@link CantProfileCounts.skillCount}.\n *\n * Returns the empty count bag when parsing fails — callers can still\n * surface the file in a list, just without per-section detail.\n */\nasync function extractCantCounts(sourcePath: string): Promise<CantProfileCounts> {\n let parsed: Awaited<ReturnType<typeof parseDocument>>;\n try {\n parsed = await parseDocument(sourcePath);\n } catch {\n return { ...EMPTY_CANT_COUNTS };\n }\n if (!parsed.success || !isRecord(parsed.document)) {\n return { ...EMPTY_CANT_COUNTS };\n }\n const sections = parsed.document['sections'];\n if (!Array.isArray(sections)) {\n return { ...EMPTY_CANT_COUNTS };\n }\n\n let agentCount = 0;\n let workflowCount = 0;\n let pipelineCount = 0;\n let hookCount = 0;\n const skillNames = new Set<string>();\n\n for (const section of sections) {\n if (!isRecord(section)) continue;\n if (isRecord(section['Agent'])) {\n agentCount += 1;\n const agent = section['Agent'];\n const hooks = agent['hooks'];\n if (Array.isArray(hooks)) {\n hookCount += hooks.length;\n }\n const properties = agent['properties'];\n if (Array.isArray(properties)) {\n for (const prop of properties) {\n if (!isRecord(prop)) continue;\n const key = unwrapSpanned(prop['key']);\n if (key === 'skills') {\n collectSkillNames(prop['value'], skillNames);\n }\n }\n }\n continue;\n }\n if (isRecord(section['Workflow'])) {\n workflowCount += 1;\n continue;\n }\n if (isRecord(section['Pipeline'])) {\n pipelineCount += 1;\n continue;\n }\n if (isRecord(section['Hook'])) {\n hookCount += 1;\n }\n }\n\n return {\n agentCount,\n workflowCount,\n pipelineCount,\n hookCount,\n skillCount: skillNames.size,\n };\n}\n\n/**\n * Normalise a cant-core severity string into the harness layer's typed\n * union.\n *\n * @remarks\n * Cant-core's native binding returns severity as a free-form string;\n * the harness contract types it as a closed union so downstream\n * envelope builders can safely render it. Unknown severities collapse\n * to `'error'` to fail closed.\n */\nfunction normaliseSeverity(raw: string): 'error' | 'warning' | 'info' | 'hint' {\n if (raw === 'warning' || raw === 'info' || raw === 'hint') return raw;\n return 'error';\n}\n","/**\n * Three-tier scope helper for Pi harness operations.\n *\n * @remarks\n * Per ADR-035 §D1, CAAMP wraps Pi's native two-tier extension model\n * (project-local `.pi/extensions/` → global `~/.pi/agent/extensions/`)\n * with a third tier for the CleoOS-managed cross-project hub. The\n * resulting hierarchy, in precedence order on reads:\n *\n * | Tier | Path | Who owns it |\n * | --------- | ---------------------------------------------- | ------------------------ |\n * | `project` | `<cwd>/.pi/<asset>/` | repository |\n * | `user` | `$PI_CODING_AGENT_DIR` or `~/.pi/agent/<asset>`| Pi itself |\n * | `global` | `$CLEO_HOME/pi-<asset>/` | CleoOS (this wrapper) |\n *\n * Pi's own discovery loader is NOT modified. The `global` tier is either\n * copied or symlinked into the `user` tier on first use (lazy\n * materialization), so Pi's native two-tier loader picks extensions up\n * from the familiar location without needing a patch.\n *\n * This helper intentionally lives next to {@link ../harness/pi.ts} rather\n * than being exported at a higher level — three-tier scope is a Pi\n * concept today, and other harnesses (if any ever land) will want their\n * own mapping.\n *\n * @see ADR-035 §D1 (Three-tier scope hierarchy with explicit precedence)\n *\n * @packageDocumentation\n */\n\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { getCleoHome } from '@cleocode/paths';\n\n/**\n * Three-tier scope identifier for Pi harness operations.\n *\n * @remarks\n * Each tier maps to a distinct root directory; see the module overview\n * for the precedence and resolution rules. This is distinct from (and\n * coexists with) the legacy {@link HarnessScope} discriminated union,\n * which only supports two tiers (`global`/`project`) and is preserved\n * for back-compat with existing skill/instruction installers.\n *\n * @public\n */\nexport type HarnessTier = 'project' | 'user' | 'global';\n\n/**\n * Asset kinds managed by the three-tier scope model.\n *\n * @remarks\n * Each asset kind has its own directory name on disk, so a single tier\n * resolver function can be used for extensions, prompts, themes, and\n * CANT files without repeating path logic across the harness.\n *\n * - `extensions` — Pi extension modules (`.ts` files)\n * - `prompts` — Prompt templates (directories containing `prompt.md`)\n * - `themes` — Theme modules (`.ts` or `.json` files)\n * - `sessions` — Session JSONL files (only the `user` tier is meaningful)\n * - `cant` — CANT DSL files (`.cant`)\n *\n * @public\n */\nexport type HarnessAssetKind = 'extensions' | 'prompts' | 'themes' | 'sessions' | 'cant';\n\n/**\n * Precedence-ordered iteration of tiers for read operations.\n *\n * @remarks\n * `list`-style operations iterate this array and merge results with\n * higher-precedence tiers winning on name collisions. Write operations\n * use a single tier selected by the caller.\n *\n * @public\n */\nexport const TIER_PRECEDENCE: readonly HarnessTier[] = ['project', 'user', 'global'];\n\n/**\n * Resolve the Pi state root for the `user` tier, honouring\n * `$PI_CODING_AGENT_DIR`.\n *\n * @remarks\n * Kept private to this module so that tests can redirect it by setting\n * the env var. Matches the logic in {@link ../pi.ts}'s private\n * `getPiAgentDir` helper, intentionally duplicated here to avoid\n * exposing it as a public export.\n */\nfunction getPiAgentDir(): string {\n const env = process.env['PI_CODING_AGENT_DIR'];\n if (env !== undefined && env.length > 0) {\n if (env === '~') return homedir();\n if (env.startsWith('~/')) return join(homedir(), env.slice(2));\n return env;\n }\n return join(homedir(), '.pi', 'agent');\n}\n\n/**\n * Resolve the CleoOS-managed hub root for the `global` tier, honouring\n * `$CLEO_HOME`.\n *\n * @remarks\n * Delegates to {@link getCleoHome} from `@cleocode/paths`, which is the\n * canonical SSoT for XDG / platform-specific CLEO home resolution.\n */\nfunction getCleoHomeDir(): string {\n return getCleoHome();\n}\n\n/**\n * Map an asset kind to the directory name Pi uses natively under its\n * state root, and the name CleoOS uses under its hub.\n *\n * @remarks\n * Extensions live at `<piRoot>/extensions/` natively and at\n * `<cleoHome>/pi-extensions/` in the hub. Prompts and themes follow the\n * same `pi-<kind>` pattern in the hub because `<cleoHome>/extensions/`\n * and `<cleoHome>/themes/` would collide with non-Pi CleoOS assets.\n *\n * For the `sessions` asset kind the `global` tier is meaningless — Pi\n * owns session storage — so calls with `tier='global'` and\n * `kind='sessions'` deliberately return the user-tier path (the only\n * sane fallback) rather than inventing a second session store. Likewise\n * the `project` tier is meaningless for sessions and also folds back to\n * the user tier.\n */\nfunction assetDirName(kind: HarnessAssetKind): {\n native: string;\n hubSuffix: string;\n} {\n switch (kind) {\n case 'extensions':\n return { native: 'extensions', hubSuffix: 'pi-extensions' };\n case 'prompts':\n return { native: 'prompts', hubSuffix: 'pi-prompts' };\n case 'themes':\n return { native: 'themes', hubSuffix: 'pi-themes' };\n case 'sessions':\n return { native: 'sessions', hubSuffix: 'pi-sessions' };\n case 'cant':\n return { native: 'cant', hubSuffix: 'pi-cant' };\n }\n}\n\n/**\n * Options accepted by {@link resolveTierDir}.\n *\n * @public\n */\nexport interface ResolveTierDirOptions {\n /** Tier to resolve. */\n tier: HarnessTier;\n /** Asset kind (extensions, prompts, themes, sessions, cant). */\n kind: HarnessAssetKind;\n /**\n * Project directory used when {@link tier} is `project`. Ignored for\n * other tiers. When omitted with `tier='project'` the caller MUST\n * substitute `process.cwd()` before invoking the resolver, so the\n * harness never silently resolves to an unexpected working directory.\n *\n * @defaultValue undefined\n */\n projectDir?: string;\n}\n\n/**\n * Resolve the on-disk directory for an asset at a given tier.\n *\n * @remarks\n * This is the single source of truth for three-tier path resolution.\n * Every PiHarness method that accepts a {@link HarnessTier} MUST route\n * through this resolver rather than re-deriving the path, so that\n * changes to the hierarchy (e.g. XDG migration) happen in one place.\n *\n * @param opts - Resolution options (see {@link ResolveTierDirOptions})\n * @returns Absolute directory path for the asset at the requested tier\n * @throws `Error` when `tier='project'` and no `projectDir` is supplied\n *\n * @example\n * ```typescript\n * import { resolveTierDir } from \"./scope.js\";\n *\n * const projectExt = resolveTierDir({\n * tier: \"project\",\n * kind: \"extensions\",\n * projectDir: \"/home/alice/repos/cleo\",\n * });\n * // → \"/home/alice/repos/cleo/.pi/extensions\"\n *\n * const userExt = resolveTierDir({ tier: \"user\", kind: \"extensions\" });\n * // → \"/home/alice/.pi/agent/extensions\"\n *\n * const globalExt = resolveTierDir({ tier: \"global\", kind: \"extensions\" });\n * // → \"/home/alice/.local/share/cleo/pi-extensions\"\n * ```\n *\n * @public\n */\nexport function resolveTierDir(opts: ResolveTierDirOptions): string {\n const { tier, kind } = opts;\n const names = assetDirName(kind);\n\n if (tier === 'project') {\n if (opts.projectDir === undefined || opts.projectDir.length === 0) {\n throw new Error(\"resolveTierDir: 'project' tier requires a projectDir argument\");\n }\n return join(opts.projectDir, '.pi', names.native);\n }\n\n if (tier === 'user') {\n return join(getPiAgentDir(), names.native);\n }\n\n // global\n return join(getCleoHomeDir(), names.hubSuffix);\n}\n\n/**\n * Resolve every tier directory for a given asset kind, in precedence\n * order.\n *\n * @remarks\n * Convenience wrapper for `list`-style operations that need to walk all\n * three tiers. Entries are returned in the precedence order declared in\n * {@link TIER_PRECEDENCE}, so a naive de-duplication loop that keeps the\n * first seen name automatically implements the \"higher tier wins\" rule.\n *\n * @param kind - Asset kind to resolve\n * @param projectDir - Project directory for the `project` tier. When\n * omitted the `project` tier entry is skipped rather than failing.\n * @returns Array of `{ tier, dir }` pairs in precedence order\n *\n * @example\n * ```typescript\n * const tiers = resolveAllTiers(\"extensions\", \"/home/alice/repo\");\n * for (const { tier, dir } of tiers) {\n * for (const entry of await safeReaddir(dir)) {\n * // higher-precedence tier wins on name collision\n * }\n * }\n * ```\n *\n * @public\n */\nexport function resolveAllTiers(\n kind: HarnessAssetKind,\n projectDir?: string,\n): Array<{ tier: HarnessTier; dir: string }> {\n const out: Array<{ tier: HarnessTier; dir: string }> = [];\n for (const tier of TIER_PRECEDENCE) {\n if (tier === 'project' && (projectDir === undefined || projectDir.length === 0)) {\n continue;\n }\n out.push({ tier, dir: resolveTierDir({ tier, kind, projectDir }) });\n }\n return out;\n}\n","/**\n * Harness layer dispatcher.\n *\n * @remarks\n * This module is the single entry point for resolving a concrete\n * {@link Harness} implementation from a provider (or from the registry's\n * primary provider). It owns the switch that maps provider ids to\n * implementations so the rest of CAAMP never needs to construct harness\n * classes directly.\n *\n * Today only Pi has a harness implementation. Future harnesses (Goose,\n * OpenCode, ...) will be added to {@link getHarnessFor} alongside Pi.\n *\n * @packageDocumentation\n */\n\nimport type { Provider } from '../../types.js';\nimport {\n getExclusivityMode,\n hasExplicitNonPiAutoWarned,\n hasPiAbsentAutoWarned,\n markExplicitNonPiAutoWarned,\n markPiAbsentAutoWarned,\n PiRequiredError,\n} from '../config/caamp-config.js';\nimport { getInstalledProviders } from '../registry/detection.js';\nimport { getAllProviders, getPrimaryProvider } from '../registry/providers.js';\nimport {\n installSkill as genericInstallSkill,\n removeSkill as genericRemoveSkill,\n type SkillInstallResult,\n} from '../skills/installer.js';\nimport { PiHarness } from './pi.js';\nimport type {\n ExclusivityMode,\n Harness,\n HarnessScope,\n ResolveDefaultTargetProvidersOptions,\n} from './types.js';\n\n/**\n * Return the harness implementation for a provider, or `null` if the\n * provider has no first-class harness.\n *\n * @remarks\n * This is the primary dispatcher. As new harnesses are added, their\n * provider id → implementation mapping lives here.\n *\n * @param provider - Resolved provider to look up.\n * @returns A harness instance, or `null` if the provider is a pure spawn\n * target with no native harness.\n *\n * @example\n * ```typescript\n * const pi = getProvider(\"pi\");\n * if (pi) {\n * const harness = getHarnessFor(pi);\n * await harness?.installSkill(\"/path/to/skill\", \"my-skill\", { kind: \"global\" });\n * }\n * ```\n *\n * @public\n */\nexport function getHarnessFor(provider: Provider): Harness | null {\n if (provider.id === 'pi') return new PiHarness(provider);\n return null;\n}\n\n/**\n * Return the primary harness declared in the registry, if any.\n *\n * @remarks\n * Resolves the registry's primary provider (via\n * {@link getPrimaryProvider}) and returns its harness implementation.\n * Callers use this to resolve the default `--agent` target when no flag\n * is provided.\n *\n * @returns The primary harness, or `null` if no primary provider exists\n * or the primary provider has no harness implementation.\n *\n * @example\n * ```typescript\n * const primary = getPrimaryHarness();\n * if (primary) {\n * console.log(`Primary harness: ${primary.provider.toolName}`);\n * }\n * ```\n *\n * @public\n */\nexport function getPrimaryHarness(): Harness | null {\n const primary = getPrimaryProvider();\n if (primary === undefined) return null;\n return getHarnessFor(primary);\n}\n\n/**\n * Return every provider that has a harness implementation.\n *\n * @remarks\n * Iterates all registered providers and collects their harness instances.\n * Useful for diagnostics and for surfaces like\n * `caamp providers list --harnesses`.\n *\n * @returns Array of harness instances, one per provider that implements\n * the {@link Harness} contract.\n *\n * @example\n * ```typescript\n * for (const harness of getAllHarnesses()) {\n * console.log(harness.provider.id); // \"pi\", ...\n * }\n * ```\n *\n * @public\n */\nexport function getAllHarnesses(): Harness[] {\n const result: Harness[] = [];\n for (const provider of getAllProviders()) {\n const harness = getHarnessFor(provider);\n if (harness !== null) result.push(harness);\n }\n return result;\n}\n\n/**\n * Resolve the default set of target providers when the user has not passed\n * `--agent`, honouring the active {@link ExclusivityMode}.\n *\n * @remarks\n * Resolution policy is layered. The active {@link ExclusivityMode} (read\n * via {@link getExclusivityMode}) selects which branch of the matrix runs:\n *\n * | Mode | Pi installed | Pi absent |\n * |---|---|---|\n * | `'auto'` (default) | Returns `[piProvider]`. Explicit non-Pi targets emit a one-time deprecation warning per process. | Falls back to installed primary/high-tier providers (legacy v2026.4.5 behaviour) and emits a one-time boot warning. |\n * | `'force-pi'` | Returns `[piProvider]`. | Throws {@link PiRequiredError}. |\n * | `'legacy'` | Returns the full installed provider list in priority order (matches pre-exclusivity behaviour). | Same. |\n *\n * **Install paths are unaffected.** Per ADR-035 §D7, this helper governs\n * RUNTIME INVOCATION dispatch only. Skill and instruction install\n * dispatchers ({@link dispatchInstallSkillAcrossProviders},\n * {@link dispatchRemoveSkillAcrossProviders}) intentionally do not call\n * this function — they target every requested provider directly so that\n * users in `force-pi` mode can still run\n * `caamp skills install foo --agent claude-code` while Pi is being\n * installed.\n *\n * The helper is intentionally defensive: registry/detection exceptions\n * are caught and treated as \"Pi unknown\" so stubbed test environments\n * that do not wire the full registry still behave sensibly.\n *\n * @param options - Optional explicit provider selection (e.g. from\n * `--agent`) used by `auto`-mode deprecation warning detection. Omit to\n * request the implicit default resolution.\n * @returns Ordered list of providers to target by default.\n * @throws {@link PiRequiredError} when mode is `'force-pi'` and Pi is not\n * installed.\n *\n * @example\n * ```typescript\n * // Implicit default — used by `caamp skills list` and friends.\n * const targets = resolveDefaultTargetProviders();\n *\n * // Explicit user selection — emits a deprecation warning in `auto` mode\n * // when the selection excludes Pi and Pi is installed.\n * const explicit = resolveDefaultTargetProviders({\n * explicit: [getProvider('claude-code')!],\n * });\n * ```\n *\n * @public\n */\nexport function resolveDefaultTargetProviders(\n options: ResolveDefaultTargetProvidersOptions = {},\n): Provider[] {\n const mode: ExclusivityMode = getExclusivityMode();\n\n let primary: Harness | null = null;\n try {\n primary = getPrimaryHarness();\n } catch {\n primary = null;\n }\n\n let installed: Provider[];\n try {\n installed = getInstalledProviders();\n } catch {\n installed = [];\n }\n\n const primaryId = primary?.provider.id ?? null;\n const primaryInstalled =\n primaryId !== null && installed.some((provider) => provider.id === primaryId);\n const explicit = options.explicit;\n const explicitContainsPrimary =\n explicit !== undefined && primaryId !== null\n ? explicit.some((provider) => provider.id === primaryId)\n : false;\n\n // Inlined legacy fallback (v2026.4.5 algorithm). Used by `legacy` mode\n // and by the `auto` + Pi-absent branch. Captured as a closure so the\n // exclusivity matrix below has a single call site for both paths\n // without introducing a new top-level symbol.\n const legacyFallback = (): Provider[] => {\n if (primary !== null && primaryInstalled) {\n return [primary.provider];\n }\n const highTier = installed.filter(\n (provider) => provider.priority === 'primary' || provider.priority === 'high',\n );\n if (highTier.length > 0) {\n return highTier;\n }\n return installed;\n };\n\n // ── force-pi: Pi is mandatory at runtime invocation ─────────────────\n if (mode === 'force-pi') {\n if (primary === null || !primaryInstalled) {\n throw new PiRequiredError();\n }\n return [primary.provider];\n }\n\n // ── legacy: pre-exclusivity behaviour, no warnings, no requirement ──\n if (mode === 'legacy') {\n if (explicit !== undefined) {\n return explicit;\n }\n return legacyFallback();\n }\n\n // ── auto (default) ──────────────────────────────────────────────────\n // Emit a one-time deprecation warning when an explicit non-Pi target is\n // supplied while Pi is installed. This is the user-visible nudge that\n // direct provider targeting will be deprecated in a future major.\n if (\n explicit !== undefined &&\n explicit.length > 0 &&\n !explicitContainsPrimary &&\n primaryInstalled &&\n !hasExplicitNonPiAutoWarned()\n ) {\n console.warn(\n 'Warning: Targeting a non-Pi provider explicitly is deprecated when Pi is installed. ' +\n \"Future versions will route all runtime commands through Pi. To suppress this warning, set caamp.exclusivityMode to 'legacy'.\",\n );\n markExplicitNonPiAutoWarned();\n }\n\n // Honour an explicit selection verbatim once the warning (if any) has\n // been emitted. This preserves the v2026.4.5 contract that explicit\n // `--agent` flags target exactly what the user requested.\n if (explicit !== undefined) {\n return explicit;\n }\n\n if (primary !== null && primaryInstalled) {\n return [primary.provider];\n }\n\n // Pi is not installed — fall back to the legacy detected-high-tier set\n // and emit a one-time boot warning so the user knows orchestration is\n // not engaged.\n if (!hasPiAbsentAutoWarned()) {\n console.warn(\n 'Warning: Pi is not installed. CAAMP is falling back to direct provider dispatch. ' +\n 'Install Pi (https://github.com/mariozechner/pi-coding-agent) to enable orchestration, ' +\n \"or set caamp.exclusivityMode to 'legacy' to suppress this warning.\",\n );\n markPiAbsentAutoWarned();\n }\n return legacyFallback();\n}\n\n/**\n * Install a skill across a mixed set of providers, dispatching each provider\n * to its {@link Harness} implementation when one exists and falling through\n * to the legacy canonical+symlink installer for generic providers.\n *\n * @remarks\n * This is the command-layer bridge introduced in Wave 3. It is the single\n * point where CAAMP decides, per-provider, whether to go through a harness\n * or the generic code path. The merged {@link SkillInstallResult} mirrors\n * the shape the legacy installer returns so downstream envelope builders do\n * not need to branch on dispatch type.\n *\n * Errors from an individual harness are collected into `errors` rather than\n * thrown, matching the legacy installer's tolerant contract.\n *\n * @param sourcePath - Absolute path to the source skill directory.\n * @param skillName - Target skill name.\n * @param providers - Ordered list of target providers.\n * @param isGlobal - Whether to target global or project scope.\n * @param projectDir - Project directory used by the harness project scope\n * and forwarded to the generic installer when provided. When omitted,\n * harness project scope falls back to `process.cwd()` and the generic\n * installer is invoked without a `projectDir` argument so it retains its\n * legacy default-handling behavior.\n * @returns Merged install result across the harness and generic paths.\n *\n * @example\n * ```typescript\n * const result = await dispatchInstallSkillAcrossProviders(\n * \"/abs/path/to/skill\",\n * \"my-skill\",\n * [getProvider(\"pi\")!, getProvider(\"claude-code\")!],\n * true,\n * );\n * console.log(result.linkedAgents); // e.g. [\"pi\", \"claude-code\"]\n * ```\n *\n * @public\n */\nexport async function dispatchInstallSkillAcrossProviders(\n sourcePath: string,\n skillName: string,\n providers: Provider[],\n isGlobal: boolean,\n projectDir?: string,\n): Promise<SkillInstallResult> {\n const harnessTargets: Array<{ provider: Provider; harness: Harness }> = [];\n const genericTargets: Provider[] = [];\n for (const provider of providers) {\n const harness = getHarnessFor(provider);\n if (harness !== null) {\n harnessTargets.push({ provider, harness });\n } else {\n genericTargets.push(provider);\n }\n }\n\n const linkedAgents: string[] = [];\n const errors: string[] = [];\n const scope: HarnessScope = isGlobal\n ? { kind: 'global' }\n : { kind: 'project', projectDir: projectDir ?? process.cwd() };\n\n for (const { provider, harness } of harnessTargets) {\n try {\n await harness.installSkill(sourcePath, skillName, scope);\n linkedAgents.push(provider.id);\n } catch (err) {\n errors.push(`${provider.id}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n let canonicalPath = '';\n if (genericTargets.length > 0) {\n // Preserve the legacy installer's default-argument contract when the\n // caller did not supply `projectDir`, so existing tests that assert\n // a 4-argument call shape keep passing.\n const genericResult =\n projectDir !== undefined\n ? await genericInstallSkill(sourcePath, skillName, genericTargets, isGlobal, projectDir)\n : await genericInstallSkill(sourcePath, skillName, genericTargets, isGlobal);\n canonicalPath = genericResult.canonicalPath;\n for (const id of genericResult.linkedAgents) {\n linkedAgents.push(id);\n }\n for (const err of genericResult.errors) {\n errors.push(err);\n }\n } else if (linkedAgents.length > 0) {\n canonicalPath = sourcePath;\n }\n\n return {\n name: skillName,\n canonicalPath,\n linkedAgents,\n errors,\n success: linkedAgents.length > 0,\n };\n}\n\n/**\n * Remove a skill across a mixed set of providers, dispatching each provider\n * to its {@link Harness} implementation when one exists and falling through\n * to the legacy canonical+symlink uninstaller for generic providers.\n *\n * @remarks\n * Harness `removeSkill` is idempotent; missing targets are silently tolerated\n * and reported as successfully removed. Exceptions raised by a harness are\n * captured in the returned `errors` array instead of propagating.\n *\n * @param skillName - Skill name to remove.\n * @param providers - Ordered list of target providers.\n * @param isGlobal - Whether to target global or project scope.\n * @param projectDir - Project directory used by the harness project scope\n * and forwarded to the generic uninstaller when provided. When omitted,\n * harness project scope falls back to `process.cwd()` and the generic\n * uninstaller is invoked without a `projectDir` argument.\n * @returns Merged `{ removed, errors }` result across both dispatch paths.\n *\n * @example\n * ```typescript\n * const result = await dispatchRemoveSkillAcrossProviders(\n * \"my-skill\",\n * [getProvider(\"pi\")!, getProvider(\"claude-code\")!],\n * true,\n * );\n * console.log(result.removed); // providers the skill was removed from\n * ```\n *\n * @public\n */\nexport async function dispatchRemoveSkillAcrossProviders(\n skillName: string,\n providers: Provider[],\n isGlobal: boolean,\n projectDir?: string,\n): Promise<{ removed: string[]; errors: string[] }> {\n const harnessTargets: Array<{ provider: Provider; harness: Harness }> = [];\n const genericTargets: Provider[] = [];\n for (const provider of providers) {\n const harness = getHarnessFor(provider);\n if (harness !== null) {\n harnessTargets.push({ provider, harness });\n } else {\n genericTargets.push(provider);\n }\n }\n\n const removed: string[] = [];\n const errors: string[] = [];\n const scope: HarnessScope = isGlobal\n ? { kind: 'global' }\n : { kind: 'project', projectDir: projectDir ?? process.cwd() };\n\n for (const { provider, harness } of harnessTargets) {\n try {\n await harness.removeSkill(skillName, scope);\n removed.push(provider.id);\n } catch (err) {\n errors.push(`${provider.id}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n // Always invoke the generic uninstaller path — even with an empty generic\n // target list — to preserve the legacy contract that `removeSkill` is\n // called once per command invocation. This also ensures canonical-directory\n // cleanup always runs, which is crucial when no matching providers were\n // detected but the canonical copy still needs to be removed.\n const genericResult =\n projectDir !== undefined\n ? await genericRemoveSkill(skillName, genericTargets, isGlobal, projectDir)\n : await genericRemoveSkill(skillName, genericTargets, isGlobal);\n for (const id of genericResult.removed) {\n removed.push(id);\n }\n for (const err of genericResult.errors) {\n errors.push(err);\n }\n\n return { removed, errors };\n}\n\nexport { PiHarness } from './pi.js';\nexport type {\n Harness,\n HarnessScope,\n SubagentHandle,\n SubagentResult,\n SubagentTask,\n} from './types.js';\n","/**\n * Format utility functions\n */\n\n/**\n * Deep merge two objects, with `source` values winning on conflict.\n *\n * Recursively merges nested plain objects. Arrays and non-object values from\n * `source` overwrite `target` values.\n *\n * @param target - Base object to merge into\n * @param source - Object with values that take precedence\n * @returns A new merged object (does not mutate inputs)\n *\n * @remarks\n * Only plain objects are recursively merged. Arrays and primitive values from\n * `source` replace `target` values outright. Neither input is mutated.\n *\n * @example\n * ```typescript\n * const merged = deepMerge({ a: 1, b: { c: 2 } }, { b: { d: 3 } });\n * // { a: 1, b: { c: 2, d: 3 } }\n * ```\n *\n * @public\n */\nexport function deepMerge(\n target: Record<string, unknown>,\n source: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...target };\n\n for (const key of Object.keys(source)) {\n const sourceVal = source[key];\n const targetVal = target[key];\n\n if (\n sourceVal !== null &&\n typeof sourceVal === 'object' &&\n !Array.isArray(sourceVal) &&\n targetVal !== null &&\n typeof targetVal === 'object' &&\n !Array.isArray(targetVal)\n ) {\n result[key] = deepMerge(\n targetVal as Record<string, unknown>,\n sourceVal as Record<string, unknown>,\n );\n } else {\n result[key] = sourceVal;\n }\n }\n\n return result;\n}\n\n/**\n * Set a nested value using a dot-notation key path.\n *\n * @remarks\n * Creates intermediate objects as needed. Returns a shallow copy of the\n * root object (does not mutate the input).\n *\n * @param obj - Root object to modify\n * @param keyPath - Dot-separated path to the parent key (e.g. `\"mcpServers\"`)\n * @param key - Final key name for the value\n * @param value - Value to set at the nested location\n * @returns A new object with the value set at the specified path\n *\n * @example\n * ```typescript\n * const result = setNestedValue({}, \"a.b\", \"c\", 42);\n * // { a: { b: { c: 42 } } }\n * ```\n *\n * @public\n */\nexport function setNestedValue(\n obj: Record<string, unknown>,\n keyPath: string,\n key: string,\n value: unknown,\n): Record<string, unknown> {\n const parts = keyPath.split('.');\n const result = { ...obj };\n let current: Record<string, unknown> = result;\n\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n if (part === undefined) continue;\n if (i === parts.length - 1) {\n // Last part: set the server entry\n const existing = (current[part] as Record<string, unknown>) ?? {};\n current[part] = { ...existing, [key]: value };\n } else {\n // Intermediate: ensure object exists\n if (typeof current[part] !== 'object' || current[part] === null) {\n current[part] = {};\n }\n current[part] = { ...(current[part] as Record<string, unknown>) };\n current = current[part] as Record<string, unknown>;\n }\n }\n\n return result;\n}\n\n/**\n * Get a nested value from an object using a dot-notation key path.\n *\n * @param obj - Object to traverse\n * @param keyPath - Dot-separated key path (e.g. `\"mcpServers\"` or `\"a.b.c\"`)\n * @returns The value at the key path, or `undefined` if not found\n *\n * @remarks\n * Splits the key path on `.` and walks the object tree. Returns `undefined`\n * at the first missing or non-object segment.\n *\n * @example\n * ```typescript\n * getNestedValue({ a: { b: { c: 42 } } }, \"a.b.c\"); // 42\n * getNestedValue({ a: 1 }, \"a.b\"); // undefined\n * ```\n *\n * @public\n */\nexport function getNestedValue(obj: Record<string, unknown>, keyPath: string): unknown {\n const parts = keyPath.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (current === null || typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n\n return current;\n}\n\n/**\n * Ensure that the parent directories of a file path exist.\n *\n * Creates directories recursively if they do not exist.\n *\n * @param filePath - Absolute path to a file (parent directories will be created)\n *\n * @remarks\n * Uses `mkdir` with `recursive: true` so existing directories are not an error.\n *\n * @example\n * ```typescript\n * await ensureDir(\"/path/to/new/dir/file.json\");\n * // /path/to/new/dir/ now exists\n * ```\n *\n * @public\n */\nexport async function ensureDir(filePath: string): Promise<void> {\n const { mkdir } = await import('node:fs/promises');\n const { dirname } = await import('node:path');\n await mkdir(dirname(filePath), { recursive: true });\n}\n","/**\n * JSON/JSONC config reader/writer with comment preservation\n *\n * Uses jsonc-parser for surgical edits that preserve comments,\n * formatting, and trailing commas in JSONC files.\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport * as jsonc from 'jsonc-parser';\nimport { ensureDir } from './utils.js';\n\n/**\n * Read and parse a JSON or JSONC config file.\n *\n * @remarks\n * Uses `jsonc-parser` to handle JSONC features (comments, trailing commas).\n * Returns an empty object when the file does not exist or is empty.\n *\n * @param filePath - Absolute path to the JSON/JSONC file\n * @returns Parsed config object\n *\n * @example\n * ```typescript\n * const config = await readJsonConfig(\"/home/user/.config/claude/settings.json\");\n * ```\n *\n * @public\n */\nexport async function readJsonConfig(filePath: string): Promise<Record<string, unknown>> {\n if (!existsSync(filePath)) return {};\n\n const content = await readFile(filePath, 'utf-8');\n if (!content.trim()) return {};\n\n const errors: jsonc.ParseError[] = [];\n const result = jsonc.parse(content, errors);\n\n if (errors.length > 0) {\n // Fall back to standard JSON parse for better error messages\n return JSON.parse(content) as Record<string, unknown>;\n }\n\n return (result ?? {}) as Record<string, unknown>;\n}\n\n/** Detect indentation from existing file content */\nfunction detectIndent(content: string): { indent: string; insertSpaces: boolean; tabSize: number } {\n const lines = content.split('\\n');\n for (const line of lines) {\n const match = line.match(/^(\\s+)/);\n if (match?.[1]) {\n const ws = match[1];\n if (ws.startsWith('\\t')) {\n return { indent: '\\t', insertSpaces: false, tabSize: 1 };\n }\n return { indent: ws, insertSpaces: true, tabSize: ws.length };\n }\n }\n return { indent: ' ', insertSpaces: true, tabSize: 2 };\n}\n\n/**\n * Write a server config entry to a JSON/JSONC file, preserving comments.\n *\n * @remarks\n * Uses `jsonc-parser.modify` for surgical edits that preserve comments,\n * formatting, and trailing commas. Creates the file if it does not exist.\n *\n * @param filePath - Absolute path to the JSON/JSONC file\n * @param configKey - Dot-notation key path to the servers section (e.g. `\"mcpServers\"`)\n * @param serverName - Name/key for the server entry\n * @param serverConfig - Server configuration object to write\n *\n * @example\n * ```typescript\n * await writeJsonConfig(\"/path/to/config.json\", \"mcpServers\", \"my-server\", { command: \"node\" });\n * ```\n *\n * @public\n */\nexport async function writeJsonConfig(\n filePath: string,\n configKey: string,\n serverName: string,\n serverConfig: unknown,\n): Promise<void> {\n await ensureDir(filePath);\n\n let content: string;\n\n if (existsSync(filePath)) {\n content = await readFile(filePath, 'utf-8');\n if (!content.trim()) {\n content = '{}';\n }\n } else {\n content = '{}';\n }\n\n const { tabSize, insertSpaces } = detectIndent(content);\n\n const formatOptions: jsonc.FormattingOptions = {\n tabSize,\n insertSpaces,\n eol: '\\n',\n };\n\n // Build the JSON path for the server entry\n const keyParts = configKey.split('.');\n const jsonPath = [...keyParts, serverName];\n\n // Use jsonc.modify for surgical, comment-preserving edits\n const edits = jsonc.modify(content, jsonPath, serverConfig, { formattingOptions: formatOptions });\n\n if (edits.length > 0) {\n content = jsonc.applyEdits(content, edits);\n }\n\n // Ensure trailing newline\n if (!content.endsWith('\\n')) {\n content += '\\n';\n }\n\n await writeFile(filePath, content, 'utf-8');\n}\n\n/**\n * Remove a server entry from a JSON/JSONC config file.\n *\n * @remarks\n * Uses `jsonc-parser.modify` with `undefined` value to remove the key while\n * preserving surrounding comments and formatting.\n *\n * @param filePath - Absolute path to the JSON/JSONC file\n * @param configKey - Dot-notation key path to the servers section\n * @param serverName - Name/key of the server entry to remove\n * @returns `true` if the entry was removed, `false` if the file or entry was not found\n *\n * @example\n * ```typescript\n * const removed = await removeJsonConfig(\"/path/to/config.json\", \"mcpServers\", \"old-server\");\n * ```\n *\n * @public\n */\nexport async function removeJsonConfig(\n filePath: string,\n configKey: string,\n serverName: string,\n): Promise<boolean> {\n if (!existsSync(filePath)) return false;\n\n let content = await readFile(filePath, 'utf-8');\n if (!content.trim()) return false;\n\n const { tabSize, insertSpaces } = detectIndent(content);\n\n const formatOptions: jsonc.FormattingOptions = {\n tabSize,\n insertSpaces,\n eol: '\\n',\n };\n\n const keyParts = configKey.split('.');\n const jsonPath = [...keyParts, serverName];\n\n const edits = jsonc.modify(content, jsonPath, undefined, { formattingOptions: formatOptions });\n\n if (edits.length === 0) return false;\n\n content = jsonc.applyEdits(content, edits);\n\n if (!content.endsWith('\\n')) {\n content += '\\n';\n }\n\n await writeFile(filePath, content, 'utf-8');\n return true;\n}\n","/**\n * TOML config reader/writer\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport TOML from '@iarna/toml';\nimport { deepMerge, ensureDir } from './utils.js';\n\n/**\n * Read and parse a TOML config file.\n *\n * @remarks\n * Uses `@iarna/toml` for parsing. Returns an empty object when the file does\n * not exist or is empty.\n *\n * @param filePath - Absolute path to the TOML file\n * @returns Parsed config object\n *\n * @example\n * ```typescript\n * const config = await readTomlConfig(\"/path/to/config.toml\");\n * ```\n *\n * @public\n */\nexport async function readTomlConfig(filePath: string): Promise<Record<string, unknown>> {\n if (!existsSync(filePath)) return {};\n\n const content = await readFile(filePath, 'utf-8');\n if (!content.trim()) return {};\n\n const result = TOML.parse(content);\n return result as unknown as Record<string, unknown>;\n}\n\n/**\n * Write a server config entry to a TOML file.\n *\n * @remarks\n * Reads the existing file, deep-merges the new entry, and writes the entire\n * result back. Creates the file and parent directories if they do not exist.\n *\n * @param filePath - Absolute path to the TOML file\n * @param configKey - Dot-notation key path to the servers section\n * @param serverName - Name/key for the server entry\n * @param serverConfig - Server configuration object to write\n *\n * @example\n * ```typescript\n * await writeTomlConfig(\"/path/to/config.toml\", \"mcpServers\", \"my-server\", { command: \"node\" });\n * ```\n *\n * @public\n */\nexport async function writeTomlConfig(\n filePath: string,\n configKey: string,\n serverName: string,\n serverConfig: unknown,\n): Promise<void> {\n await ensureDir(filePath);\n\n const existing = await readTomlConfig(filePath);\n\n // Build nested structure\n const keyParts = configKey.split('.');\n let newEntry: Record<string, unknown> = { [serverName]: serverConfig };\n\n for (const part of [...keyParts].reverse()) {\n newEntry = { [part]: newEntry };\n }\n\n const merged = deepMerge(existing, newEntry);\n\n const content = TOML.stringify(merged as TOML.JsonMap);\n\n await writeFile(filePath, content, 'utf-8');\n}\n\n/**\n * Remove a server entry from a TOML config file.\n *\n * @remarks\n * Navigates the parsed TOML object to the config key, deletes the server\n * entry, and re-serializes the entire config.\n *\n * @param filePath - Absolute path to the TOML file\n * @param configKey - Dot-notation key path to the servers section\n * @param serverName - Name/key of the server entry to remove\n * @returns `true` if the entry was removed, `false` if the file or entry was not found\n *\n * @example\n * ```typescript\n * const removed = await removeTomlConfig(\"/path/to/config.toml\", \"mcpServers\", \"old-server\");\n * ```\n *\n * @public\n */\nexport async function removeTomlConfig(\n filePath: string,\n configKey: string,\n serverName: string,\n): Promise<boolean> {\n if (!existsSync(filePath)) return false;\n\n const existing = await readTomlConfig(filePath);\n\n const keyParts = configKey.split('.');\n let current: Record<string, unknown> = existing;\n\n for (const part of keyParts) {\n const next = current[part];\n if (typeof next !== 'object' || next === null) return false;\n current = next as Record<string, unknown>;\n }\n\n if (!(serverName in current)) return false;\n\n delete current[serverName];\n\n const content = TOML.stringify(existing as TOML.JsonMap);\n\n await writeFile(filePath, content, 'utf-8');\n return true;\n}\n","/**\n * YAML config reader/writer\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile, writeFile } from 'node:fs/promises';\nimport yaml from 'js-yaml';\nimport { deepMerge, ensureDir } from './utils.js';\n\n/**\n * Read and parse a YAML config file.\n *\n * @remarks\n * Uses `js-yaml` for parsing. Returns an empty object when the file does not\n * exist or is empty.\n *\n * @param filePath - Absolute path to the YAML file\n * @returns Parsed config object\n *\n * @example\n * ```typescript\n * const config = await readYamlConfig(\"/path/to/config.yaml\");\n * ```\n *\n * @public\n */\nexport async function readYamlConfig(filePath: string): Promise<Record<string, unknown>> {\n if (!existsSync(filePath)) return {};\n\n const content = await readFile(filePath, 'utf-8');\n if (!content.trim()) return {};\n\n const result = yaml.load(content);\n return (result ?? {}) as Record<string, unknown>;\n}\n\n/**\n * Write a server config entry to a YAML file.\n *\n * @remarks\n * Reads the existing file, deep-merges the new entry, and writes the entire\n * result back. Creates the file and parent directories if they do not exist.\n *\n * @param filePath - Absolute path to the YAML file\n * @param configKey - Dot-notation key path to the servers section\n * @param serverName - Name/key for the server entry\n * @param serverConfig - Server configuration object to write\n *\n * @example\n * ```typescript\n * await writeYamlConfig(\"/path/to/config.yaml\", \"mcpServers\", \"my-server\", { command: \"node\" });\n * ```\n *\n * @public\n */\nexport async function writeYamlConfig(\n filePath: string,\n configKey: string,\n serverName: string,\n serverConfig: unknown,\n): Promise<void> {\n await ensureDir(filePath);\n\n const existing = await readYamlConfig(filePath);\n\n // Build nested structure\n const keyParts = configKey.split('.');\n let newEntry: Record<string, unknown> = { [serverName]: serverConfig };\n\n for (const part of [...keyParts].reverse()) {\n newEntry = { [part]: newEntry };\n }\n\n const merged = deepMerge(existing, newEntry);\n\n const content = yaml.dump(merged, {\n indent: 2,\n lineWidth: -1,\n noRefs: true,\n sortKeys: false,\n });\n\n await writeFile(filePath, content, 'utf-8');\n}\n\n/**\n * Remove a server entry from a YAML config file.\n *\n * @remarks\n * Navigates the parsed YAML object to the config key, deletes the server\n * entry, and re-serializes the entire config.\n *\n * @param filePath - Absolute path to the YAML file\n * @param configKey - Dot-notation key path to the servers section\n * @param serverName - Name/key of the server entry to remove\n * @returns `true` if the entry was removed, `false` if the file or entry was not found\n *\n * @example\n * ```typescript\n * const removed = await removeYamlConfig(\"/path/to/config.yaml\", \"mcpServers\", \"old-server\");\n * ```\n *\n * @public\n */\nexport async function removeYamlConfig(\n filePath: string,\n configKey: string,\n serverName: string,\n): Promise<boolean> {\n if (!existsSync(filePath)) return false;\n\n const existing = await readYamlConfig(filePath);\n\n // Navigate to the config key\n const keyParts = configKey.split('.');\n let current: Record<string, unknown> = existing;\n\n for (const part of keyParts) {\n const next = current[part];\n if (typeof next !== 'object' || next === null) return false;\n current = next as Record<string, unknown>;\n }\n\n if (!(serverName in current)) return false;\n\n delete current[serverName];\n\n const content = yaml.dump(existing, {\n indent: 2,\n lineWidth: -1,\n noRefs: true,\n sortKeys: false,\n });\n\n await writeFile(filePath, content, 'utf-8');\n return true;\n}\n","/**\n * Provides format-agnostic config read, write, and remove operations that\n * dispatch to JSON/JSONC, YAML, or TOML handlers based on the specified\n * format.\n *\n * @packageDocumentation\n */\n\nimport type { ConfigFormat } from '../../types.js';\nimport { debug } from '../logger.js';\nimport { readJsonConfig, removeJsonConfig, writeJsonConfig } from './json.js';\nimport { readTomlConfig, removeTomlConfig, writeTomlConfig } from './toml.js';\nimport { readYamlConfig, removeYamlConfig, writeYamlConfig } from './yaml.js';\n\nexport { deepMerge, ensureDir, getNestedValue } from './utils.js';\n\n/**\n * Read and parse a config file in the specified format.\n *\n * Dispatches to the appropriate format handler (JSON/JSONC, YAML, or TOML).\n *\n * @param filePath - Absolute path to the config file\n * @param format - Config file format\n * @returns Parsed config object\n * @throws If the file cannot be read or the format is unsupported\n *\n * @remarks\n * Supported formats: `\"json\"`, `\"jsonc\"`, `\"yaml\"`, `\"toml\"`. Throws for\n * any unrecognized format string.\n *\n * @example\n * ```typescript\n * const config = await readConfig(\"/path/to/config.json\", \"jsonc\");\n * ```\n *\n * @public\n */\nexport async function readConfig(\n filePath: string,\n format: ConfigFormat,\n): Promise<Record<string, unknown>> {\n debug(`reading config: ${filePath} (format: ${format})`);\n switch (format) {\n case 'json':\n case 'jsonc':\n return readJsonConfig(filePath);\n case 'yaml':\n return readYamlConfig(filePath);\n case 'toml':\n return readTomlConfig(filePath);\n default:\n throw new Error(`Unsupported config format: ${format as string}`);\n }\n}\n\n/**\n * Write a server entry to a config file, preserving existing content.\n *\n * Dispatches to the appropriate format handler. For JSONC files, comments are\n * preserved using `jsonc-parser`.\n *\n * @param filePath - Absolute path to the config file\n * @param format - Config file format\n * @param key - Dot-notation key path to the servers section (e.g. `\"mcpServers\"`)\n * @param serverName - Name/key for the server entry\n * @param serverConfig - Server configuration object to write\n * @throws If the format is unsupported\n *\n * @remarks\n * For JSONC files, comments and formatting are preserved using `jsonc-parser`.\n * For YAML and TOML, the file is fully re-serialized after deep-merging.\n *\n * @example\n * ```typescript\n * await writeConfig(\"/path/to/config.json\", \"jsonc\", \"mcpServers\", \"my-server\", config);\n * ```\n *\n * @public\n */\nexport async function writeConfig(\n filePath: string,\n format: ConfigFormat,\n key: string,\n serverName: string,\n serverConfig: unknown,\n): Promise<void> {\n debug(`writing config: ${filePath} (format: ${format}, key: ${key}, server: ${serverName})`);\n switch (format) {\n case 'json':\n case 'jsonc':\n return writeJsonConfig(filePath, key, serverName, serverConfig);\n case 'yaml':\n return writeYamlConfig(filePath, key, serverName, serverConfig);\n case 'toml':\n return writeTomlConfig(filePath, key, serverName, serverConfig);\n default:\n throw new Error(`Unsupported config format: ${format as string}`);\n }\n}\n\n/**\n * Remove a server entry from a config file in the specified format.\n *\n * @param filePath - Absolute path to the config file\n * @param format - Config file format\n * @param key - Dot-notation key path to the servers section\n * @param serverName - Name/key of the server entry to remove\n * @returns `true` if the entry was removed, `false` otherwise\n * @throws If the format is unsupported\n *\n * @remarks\n * Delegates to the format-specific removal function. Returns `false` when the\n * file does not exist or the entry is not found.\n *\n * @example\n * ```typescript\n * const removed = await removeConfig(\"/path/to/config.json\", \"jsonc\", \"mcpServers\", \"my-server\");\n * ```\n *\n * @public\n */\nexport async function removeConfig(\n filePath: string,\n format: ConfigFormat,\n key: string,\n serverName: string,\n): Promise<boolean> {\n switch (format) {\n case 'json':\n case 'jsonc':\n return removeJsonConfig(filePath, key, serverName);\n case 'yaml':\n return removeYamlConfig(filePath, key, serverName);\n case 'toml':\n return removeTomlConfig(filePath, key, serverName);\n default:\n throw new Error(`Unsupported config format: ${format as string}`);\n }\n}\n","/**\n * MCP server config reader.\n *\n * @remarks\n * Reads MCP server entries from a provider's per-agent config file using\n * the format-agnostic {@link readConfig} substrate from `core/formats`.\n * The provider's `capabilities.mcp` block is the single source of truth\n * for the file path, format, and dot-notation key — this module never\n * hard-codes provider-specific layout.\n *\n * Three operations are exported:\n *\n * - {@link listMcpServers} — enumerate every server entry on a single\n * provider's config file at a given scope.\n * - {@link listAllMcpServers} — fan out across every MCP-capable\n * provider in the registry, returning a map keyed by provider id.\n * - {@link detectMcpInstallations} — lighter-weight scan that just\n * reports which providers currently have any MCP config files on\n * disk (used by `caamp mcp detect`).\n *\n * @packageDocumentation\n */\n\nimport { existsSync } from 'node:fs';\nimport { stat } from 'node:fs/promises';\nimport { resolveProviderConfigPath } from '../../core/paths/standard.js';\nimport { getAllProviders } from '../../core/registry/providers.js';\nimport type { McpServerEntry, Provider } from '../../types.js';\nimport { readConfig } from '../formats/index.js';\nimport { getNestedValue } from '../formats/utils.js';\nimport { debug } from '../logger.js';\n\n/**\n * Scope identifier for MCP config file resolution.\n *\n * @remarks\n * Mirrors the two-tier scope model the underlying provider config files\n * already use: `project` reads `<projectDir>/<provider.configPathProject>`\n * and `global` reads the absolute `provider.configPathGlobal` path. The\n * three-tier {@link HarnessTier} model used by the Pi extensions\n * verbs is intentionally not adopted here — MCP config files are owned\n * by individual tools and live on those tools' two-tier hierarchy, not\n * on a CleoOS-managed hub.\n *\n * @public\n */\nexport type McpScope = 'project' | 'global';\n\n/**\n * Result of a single provider's MCP installation probe.\n *\n * @remarks\n * Returned by {@link detectMcpInstallations} for each provider that\n * declares an MCP capability in the registry. The `configPath` field\n * is the resolved file path the probe inspected; `exists` indicates\n * whether the file is present on disk; `serverCount` is `null` when\n * the file is missing or unparseable, otherwise the number of server\n * entries found at the dot-notation key.\n *\n * @public\n */\nexport interface McpDetectionEntry {\n /** Provider id (e.g. `\"claude-code\"`). */\n providerId: string;\n /** Human-readable provider name. */\n providerName: string;\n /** Resolved scope of the probed config file. */\n scope: McpScope;\n /** Absolute path to the provider's MCP config file. */\n configPath: string;\n /** Whether the config file exists on disk. */\n exists: boolean;\n /** Number of server entries found, or `null` when the file is missing/unparseable. */\n serverCount: number | null;\n /** ISO 8601 timestamp of the file's last modification, or `null` when the file is missing. */\n lastModified: string | null;\n}\n\n/**\n * Resolve a provider's MCP config file path for the given scope, or\n * `null` if the provider does not declare an MCP capability or the\n * scope is unsupported.\n *\n * @remarks\n * Thin wrapper over {@link resolveProviderConfigPath} that filters out\n * providers without an MCP capability up front so callers can use a\n * single null check rather than two.\n *\n * @param provider - Provider to resolve a config path for.\n * @param scope - Scope to resolve.\n * @param projectDir - Project directory used for the `project` scope.\n * @returns The absolute config file path, or `null` when unavailable.\n *\n * @example\n * ```typescript\n * const claudeCode = getProvider(\"claude-code\")!;\n * const path = resolveMcpConfigPath(claudeCode, \"project\", \"/tmp/app\");\n * // e.g. \"/tmp/app/.mcp.json\"\n * ```\n *\n * @public\n */\nexport function resolveMcpConfigPath(\n provider: Provider,\n scope: McpScope,\n projectDir?: string,\n): string | null {\n if (provider.capabilities.mcp === null) return null;\n return resolveProviderConfigPath(provider, scope, projectDir);\n}\n\n/**\n * List MCP server entries declared in a single provider's config file.\n *\n * @remarks\n * Reads the provider's MCP config file using the format-agnostic\n * {@link readConfig} substrate, walks the dot-notation\n * `provider.capabilities.mcp.configKey` to find the servers section,\n * and returns one {@link McpServerEntry} per child key.\n *\n * Returns an empty array (not an error) when:\n *\n * - the provider does not declare an MCP capability,\n * - the resolved config path is unavailable for the requested scope,\n * - the config file does not exist on disk,\n * - the config file is empty or unparseable,\n * - the config file exists but has no MCP servers section.\n *\n * \"No file\" is a normal state for an uninstalled tool, so callers\n * should treat the empty array as success and only escalate to an\n * error envelope when the user explicitly asked about a missing\n * provider.\n *\n * @param provider - Provider whose config file to read.\n * @param scope - Scope to resolve (project|global).\n * @param projectDir - Project directory used for the `project` scope\n * (defaults to `process.cwd()`).\n * @returns Array of MCP server entries, or `[]` when nothing was found.\n *\n * @example\n * ```typescript\n * const provider = getProvider(\"claude-code\")!;\n * const entries = await listMcpServers(provider, \"project\");\n * for (const entry of entries) {\n * console.log(entry.name, entry.configPath);\n * }\n * ```\n *\n * @public\n */\nexport async function listMcpServers(\n provider: Provider,\n scope: McpScope,\n projectDir?: string,\n): Promise<McpServerEntry[]> {\n const mcp = provider.capabilities.mcp;\n if (mcp === null) return [];\n\n const configPath = resolveMcpConfigPath(provider, scope, projectDir);\n if (configPath === null) return [];\n if (!existsSync(configPath)) {\n debug(`mcp.list: ${provider.id} (${scope}) — config file missing at ${configPath}`);\n return [];\n }\n\n let parsed: Record<string, unknown>;\n try {\n parsed = await readConfig(configPath, mcp.configFormat);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n debug(`mcp.list: ${provider.id} parse failed at ${configPath}: ${message}`);\n return [];\n }\n\n const servers = getNestedValue(parsed, mcp.configKey);\n if (servers === undefined || servers === null || typeof servers !== 'object') return [];\n\n const out: McpServerEntry[] = [];\n for (const [name, raw] of Object.entries(servers as Record<string, unknown>)) {\n out.push({\n name,\n providerId: provider.id,\n providerName: provider.toolName,\n scope,\n configPath,\n config: (raw ?? {}) as Record<string, unknown>,\n });\n }\n return out;\n}\n\n/**\n * Map of provider id → MCP server entries for that provider.\n *\n * @remarks\n * Return shape of {@link listAllMcpServers}. Providers without an MCP\n * capability are intentionally absent from the map (rather than mapped\n * to an empty array) so callers can iterate the result and immediately\n * know which providers were probed.\n *\n * @public\n */\nexport type McpServerEntriesByProvider = Map<string, McpServerEntry[]>;\n\n/**\n * List MCP server entries for every MCP-capable provider in the\n * registry at the given scope.\n *\n * @remarks\n * Iterates {@link getAllProviders}, filters to those with a\n * `capabilities.mcp` block, calls {@link listMcpServers} on each, and\n * collects the results into a map keyed by provider id.\n *\n * Each provider is probed independently — a parse failure on one\n * provider will not affect the others. The result map only contains\n * entries for providers that were actually probed (i.e. had an MCP\n * capability), so consumers can iterate `result.entries()` without\n * skipping non-MCP providers.\n *\n * @param scope - Scope to resolve for every provider.\n * @param projectDir - Project directory used for the `project` scope.\n * @returns Map of provider id → server entries.\n *\n * @example\n * ```typescript\n * const byProvider = await listAllMcpServers(\"global\");\n * for (const [providerId, entries] of byProvider) {\n * console.log(`${providerId}: ${entries.length} server(s)`);\n * }\n * ```\n *\n * @public\n */\nexport async function listAllMcpServers(\n scope: McpScope,\n projectDir?: string,\n): Promise<McpServerEntriesByProvider> {\n const out: McpServerEntriesByProvider = new Map();\n for (const provider of getAllProviders()) {\n if (provider.capabilities.mcp === null) continue;\n const entries = await listMcpServers(provider, scope, projectDir);\n out.set(provider.id, entries);\n }\n return out;\n}\n\n/**\n * Probe every MCP-capable provider in the registry to determine which\n * ones have a config file on disk and how many servers are configured.\n *\n * @remarks\n * Lightweight detection used by `caamp mcp detect`. Unlike\n * {@link listAllMcpServers} this does not return individual server\n * entries — it just reports a count plus the file mtime so callers can\n * answer \"which tools on my machine already have MCP configured?\"\n * without paying the cost of materialising every server entry.\n *\n * @param scope - Scope to probe.\n * @param projectDir - Project directory used for the `project` scope.\n * @returns Array of detection entries, one per MCP-capable provider.\n *\n * @example\n * ```typescript\n * const hits = await detectMcpInstallations(\"project\");\n * const installed = hits.filter((h) => h.exists);\n * console.log(`MCP found on ${installed.length} providers`);\n * ```\n *\n * @public\n */\nexport async function detectMcpInstallations(\n scope: McpScope,\n projectDir?: string,\n): Promise<McpDetectionEntry[]> {\n const out: McpDetectionEntry[] = [];\n for (const provider of getAllProviders()) {\n const mcp = provider.capabilities.mcp;\n if (mcp === null) continue;\n const configPath = resolveMcpConfigPath(provider, scope, projectDir);\n if (configPath === null) continue;\n const exists = existsSync(configPath);\n let serverCount: number | null = null;\n let lastModified: string | null = null;\n if (exists) {\n try {\n const stats = await stat(configPath);\n lastModified = stats.mtime.toISOString();\n } catch {\n lastModified = null;\n }\n const entries = await listMcpServers(provider, scope, projectDir);\n serverCount = entries.length;\n }\n out.push({\n providerId: provider.id,\n providerName: provider.toolName,\n scope,\n configPath,\n exists,\n serverCount,\n lastModified,\n });\n }\n return out;\n}\n","/**\n * MCP server config installer.\n *\n * @remarks\n * Writes a single MCP server entry into a provider's config file using\n * the format-agnostic {@link writeConfig} substrate from `core/formats`.\n * The provider's `capabilities.mcp` block is the single source of\n * truth for the file path, format, and dot-notation key.\n *\n * Conflict-on-write semantics:\n *\n * - When the target server name does not yet exist in the file, the\n * write succeeds and {@link InstallMcpServerResult.conflicted} is\n * `false`.\n * - When the target server name already exists in the file and `force`\n * is `false`, the installer DOES NOT write — it returns\n * `installed: false, conflicted: true` so the caller can emit a\n * typed conflict error envelope.\n * - When `force` is `true`, an existing entry is overwritten and the\n * result reports `installed: true, conflicted: true` so the caller\n * can surface the overwrite in its envelope.\n *\n * Parent directories of the resolved config path are created lazily on\n * write — see {@link writeConfig} for the per-format details.\n *\n * @packageDocumentation\n */\n\nimport type { McpServerConfig, Provider } from '../../types.js';\nimport { writeConfig } from '../formats/index.js';\nimport { debug } from '../logger.js';\nimport { listMcpServers, type McpScope, resolveMcpConfigPath } from './reader.js';\n\n/**\n * Options accepted by {@link installMcpServer}.\n *\n * @public\n */\nexport interface InstallMcpServerOptions {\n /** Scope to write to (project|global). */\n scope: McpScope;\n /** When `true`, overwrite an existing server entry instead of failing. */\n force?: boolean;\n /** Project directory used for the `project` scope. */\n projectDir?: string;\n}\n\n/**\n * Result of an {@link installMcpServer} call.\n *\n * @remarks\n * `installed` is `true` only when the file was actually written.\n * `conflicted` is `true` whenever the target server name was already\n * present, regardless of whether the write went through (force was\n * supplied) or was suppressed (force was withheld).\n *\n * @public\n */\nexport interface InstallMcpServerResult {\n /** Whether the entry was written to the config file. */\n installed: boolean;\n /** Whether the target server name already existed before the call. */\n conflicted: boolean;\n /** Absolute path to the config file that was (or would have been) written. */\n sourcePath: string;\n /** Provider id the entry was written for. */\n providerId: string;\n /** Server name that was written. */\n serverName: string;\n}\n\n/**\n * Install an MCP server entry into a single provider's config file.\n *\n * @remarks\n * Resolves the provider's MCP config path for the requested scope,\n * checks for an existing entry with the same server name, and either\n * writes the new config (when no conflict, or when `force` is set) or\n * returns a non-installed conflict result.\n *\n * Throws a plain `Error` (not a `LAFSCommandError`) when the provider\n * has no MCP capability or no config path for the requested scope —\n * those are caller-side validation failures and should be caught and\n * re-thrown as typed `LAFSCommandError`s in the command layer.\n *\n * @param provider - Target provider.\n * @param serverName - Name/key for the new server entry.\n * @param config - Canonical {@link McpServerConfig} payload to write.\n * @param opts - Install options (scope, force, projectDir).\n * @returns Structured install result describing what happened.\n * @throws `Error` when the provider has no MCP capability or no\n * project-scoped config path is available.\n *\n * @example\n * ```typescript\n * const provider = getProvider(\"claude-code\")!;\n * const result = await installMcpServer(\n * provider,\n * \"github\",\n * { command: \"npx\", args: [\"-y\", \"@modelcontextprotocol/server-github\"] },\n * { scope: \"project\", force: false },\n * );\n * console.log(result.installed, result.conflicted);\n * ```\n *\n * @public\n */\nexport async function installMcpServer(\n provider: Provider,\n serverName: string,\n config: McpServerConfig,\n opts: InstallMcpServerOptions,\n): Promise<InstallMcpServerResult> {\n const mcp = provider.capabilities.mcp;\n if (mcp === null) {\n throw new Error(`Provider ${provider.id} does not declare an MCP capability.`);\n }\n const configPath = resolveMcpConfigPath(provider, opts.scope, opts.projectDir);\n if (configPath === null) {\n throw new Error(\n `Provider ${provider.id} has no ${opts.scope}-scoped MCP config path available.`,\n );\n }\n\n debug(\n `mcp.install: ${provider.id} ${serverName} → ${configPath} (format=${mcp.configFormat}, key=${mcp.configKey})`,\n );\n\n const existing = await listMcpServers(provider, opts.scope, opts.projectDir);\n const conflicted = existing.some((e) => e.name === serverName);\n if (conflicted && opts.force !== true) {\n return {\n installed: false,\n conflicted: true,\n sourcePath: configPath,\n providerId: provider.id,\n serverName,\n };\n }\n\n await writeConfig(configPath, mcp.configFormat, mcp.configKey, serverName, config);\n\n return {\n installed: true,\n conflicted,\n sourcePath: configPath,\n providerId: provider.id,\n serverName,\n };\n}\n","/**\n * MCP server config remover.\n *\n * @remarks\n * Removes a single MCP server entry from a provider's config file\n * using the format-agnostic {@link removeConfig} substrate from\n * `core/formats`. The provider's `capabilities.mcp` block is the\n * single source of truth for the file path, format, and dot-notation\n * key.\n *\n * Both single-provider and all-providers variants are exported. Both\n * are idempotent: removing a server that does not exist returns\n * `removed: false` rather than throwing.\n *\n * @packageDocumentation\n */\n\nimport { existsSync } from 'node:fs';\nimport { getAllProviders } from '../../core/registry/providers.js';\nimport type { Provider } from '../../types.js';\nimport { removeConfig } from '../formats/index.js';\nimport { debug } from '../logger.js';\nimport { type McpScope, resolveMcpConfigPath } from './reader.js';\n\n/**\n * Options accepted by {@link removeMcpServer} and\n * {@link removeMcpServerFromAll}.\n *\n * @public\n */\nexport interface RemoveMcpServerOptions {\n /** Scope to target (project|global). */\n scope: McpScope;\n /** Project directory used for the `project` scope. */\n projectDir?: string;\n}\n\n/**\n * Result of a single-provider {@link removeMcpServer} call.\n *\n * @remarks\n * `removed` is `true` only when an entry was actually deleted from\n * the file. The `reason` field carries an optional discriminator when\n * the call was a no-op so the command layer can surface a precise\n * envelope (e.g. \"no config file\" vs \"entry not present\").\n *\n * @public\n */\nexport interface RemoveMcpServerResult {\n /** Provider id the call targeted. */\n providerId: string;\n /** Server name the call targeted. */\n serverName: string;\n /** Resolved config file path, or `null` when the provider had no MCP capability. */\n sourcePath: string | null;\n /** Whether an entry was actually deleted. */\n removed: boolean;\n /**\n * Diagnostic discriminator when `removed` is `false`.\n *\n * - `\"no-mcp-capability\"` — provider does not consume MCP servers\n * - `\"no-config-path\"` — provider has no config path for the scope\n * - `\"file-missing\"` — config file does not exist on disk\n * - `\"entry-missing\"` — config file exists but had no matching entry\n *\n * Set to `null` when `removed` is `true`.\n */\n reason: 'no-mcp-capability' | 'no-config-path' | 'file-missing' | 'entry-missing' | null;\n}\n\n/**\n * Remove an MCP server entry from a single provider's config file.\n *\n * @remarks\n * Idempotent: when the entry is not present (or the file is missing\n * entirely) the call returns `removed: false` with a structured\n * `reason` rather than throwing.\n *\n * @param provider - Provider whose config file to modify.\n * @param serverName - Server name/key to remove.\n * @param opts - Removal options.\n * @returns Structured result describing whether the entry was removed.\n *\n * @example\n * ```typescript\n * const provider = getProvider(\"claude-code\")!;\n * const result = await removeMcpServer(provider, \"my-server\", { scope: \"project\" });\n * console.log(result.removed); // true | false\n * ```\n *\n * @public\n */\nexport async function removeMcpServer(\n provider: Provider,\n serverName: string,\n opts: RemoveMcpServerOptions,\n): Promise<RemoveMcpServerResult> {\n const mcp = provider.capabilities.mcp;\n if (mcp === null) {\n return {\n providerId: provider.id,\n serverName,\n sourcePath: null,\n removed: false,\n reason: 'no-mcp-capability',\n };\n }\n const configPath = resolveMcpConfigPath(provider, opts.scope, opts.projectDir);\n if (configPath === null) {\n return {\n providerId: provider.id,\n serverName,\n sourcePath: null,\n removed: false,\n reason: 'no-config-path',\n };\n }\n if (!existsSync(configPath)) {\n return {\n providerId: provider.id,\n serverName,\n sourcePath: configPath,\n removed: false,\n reason: 'file-missing',\n };\n }\n debug(`mcp.remove: ${provider.id} ${serverName} → ${configPath}`);\n const removed = await removeConfig(configPath, mcp.configFormat, mcp.configKey, serverName);\n return {\n providerId: provider.id,\n serverName,\n sourcePath: configPath,\n removed,\n reason: removed ? null : 'entry-missing',\n };\n}\n\n/**\n * Remove an MCP server entry from every MCP-capable provider in the\n * registry that currently has it configured.\n *\n * @remarks\n * Iterates {@link getAllProviders}, calls {@link removeMcpServer} on\n * each MCP-capable provider, and collects the per-provider results.\n * Each provider is processed independently — a failure on one does\n * not abort the others. The result array contains one entry per\n * MCP-capable provider, even when the entry was not present (so\n * callers can render a complete report).\n *\n * @param serverName - Server name/key to remove from every provider.\n * @param opts - Removal options applied uniformly to every provider.\n * @returns Array of per-provider removal results.\n *\n * @example\n * ```typescript\n * const results = await removeMcpServerFromAll(\"my-server\", { scope: \"global\" });\n * const removed = results.filter((r) => r.removed);\n * console.log(`Removed from ${removed.length} providers`);\n * ```\n *\n * @public\n */\nexport async function removeMcpServerFromAll(\n serverName: string,\n opts: RemoveMcpServerOptions,\n): Promise<RemoveMcpServerResult[]> {\n const out: RemoveMcpServerResult[] = [];\n for (const provider of getAllProviders()) {\n if (provider.capabilities.mcp === null) continue;\n out.push(await removeMcpServer(provider, serverName, opts));\n }\n return out;\n}\n","/**\n * Source URL/path classifier\n *\n * Classifies inputs as remote URLs, npm packages, GitHub shorthand,\n * GitLab URLs, local paths, or shell commands.\n */\n\nimport type { ParsedSource, SourceType } from '../../types.js';\n\nconst GITHUB_SHORTHAND = /^([a-zA-Z0-9_.-]+)\\/([a-zA-Z0-9_.-]+)(?:\\/(.+))?$/;\nconst GITHUB_URL =\n /^https?:\\/\\/(?:www\\.)?github\\.com\\/([^/]+)\\/([^/]+)(?:\\/(?:tree|blob)\\/([^/]+)(?:\\/(.+))?)?/;\nconst GITLAB_URL =\n /^https?:\\/\\/(?:www\\.)?gitlab\\.com\\/([^/]+)\\/([^/]+)(?:\\/-\\/(?:tree|blob)\\/([^/]+)(?:\\/(.+))?)?/;\nconst HTTP_URL = /^https?:\\/\\//;\nconst NPM_SCOPED = /^@[a-zA-Z0-9_.-]+\\/[a-zA-Z0-9_.-]+$/;\nconst NPM_PACKAGE = /^[a-zA-Z0-9_.-]+$/;\nconst LIBRARY_SKILL = /^(@[a-zA-Z0-9_.-]+\\/[a-zA-Z0-9_.-]+|[a-zA-Z0-9_.-]+):([a-zA-Z0-9_.-]+)$/;\n\n/** Infer a display name from a source */\nfunction inferName(source: string, type: SourceType): string {\n if (type === 'library') {\n const match = source.match(LIBRARY_SKILL);\n return match?.[2] ?? source;\n }\n\n if (type === 'remote') {\n try {\n const url = new URL(source);\n // Extract brand from hostname: mcp.neon.tech -> neon\n const parts = url.hostname.split('.');\n if (parts.length >= 2) {\n const fallback = parts[0] ?? source;\n const secondLevel = parts[parts.length - 2] ?? fallback;\n const brand = parts.length === 3 ? secondLevel : fallback;\n if (brand !== 'www' && brand !== 'api' && brand !== 'mcp') {\n return brand;\n }\n // Fall back to second-level domain\n return secondLevel;\n }\n return parts[0] ?? source;\n } catch {\n return source;\n }\n }\n\n if (type === 'package') {\n // Strip common MCP prefixes/suffixes\n let name = source.replace(/^@[^/]+\\//, ''); // Remove scope\n name = name.replace(/^mcp-server-/, '');\n name = name.replace(/^server-/, '');\n name = name.replace(/-mcp$/, '');\n name = name.replace(/-server$/, '');\n return name;\n }\n\n if (type === 'github' || type === 'gitlab') {\n // Use repo name\n const match = source.match(/\\/([^/]+?)(?:\\.git)?$/);\n return match?.[1] ?? source;\n }\n\n if (type === 'local') {\n // Extract directory basename from local path\n const normalized = source.replace(/\\\\/g, '/').replace(/\\/+$/, '');\n const lastSegment = normalized.split('/').pop();\n return lastSegment ?? source;\n }\n\n if (type === 'command') {\n // Extract first meaningful word\n const parts = source.split(/\\s+/);\n const command = parts.find(\n (p) => !p.startsWith('-') && p !== 'npx' && p !== 'node' && p !== 'python' && p !== 'python3',\n );\n return command ?? parts[0] ?? source;\n }\n\n return source;\n}\n\n/**\n * Parse and classify a source string into a typed {@link ParsedSource}.\n *\n * @remarks\n * Supports GitHub URLs, GitLab URLs, GitHub shorthand (`owner/repo`),\n * HTTP URLs (remote MCP servers), npm package names, local paths, and\n * shell commands as a fallback.\n *\n * @param input - Raw source string to classify\n * @returns Parsed source with type, value, and inferred name\n *\n * @example\n * ```typescript\n * parseSource(\"owner/repo\");\n * // { type: \"github\", value: \"https://github.com/owner/repo\", inferredName: \"repo\", ... }\n *\n * parseSource(\"https://mcp.example.com/sse\");\n * // { type: \"remote\", value: \"https://mcp.example.com/sse\", inferredName: \"example\" }\n *\n * parseSource(\"@modelcontextprotocol/server-filesystem\");\n * // { type: \"package\", value: \"@modelcontextprotocol/server-filesystem\", inferredName: \"filesystem\" }\n * ```\n *\n * @public\n */\nexport function parseSource(input: string): ParsedSource {\n // GitHub URL\n const ghUrlMatch = input.match(GITHUB_URL);\n if (ghUrlMatch) {\n const owner = ghUrlMatch[1];\n const repo = ghUrlMatch[2];\n const path = ghUrlMatch[4];\n if (!owner || !repo) {\n return { type: 'command', value: input, inferredName: inferName(input, 'command') };\n }\n // Use last path segment as name if subpath provided, otherwise use repo name\n const inferredName = path ? (path.split('/').pop() ?? repo) : repo;\n return {\n type: 'github',\n value: input,\n inferredName,\n owner,\n repo,\n ref: ghUrlMatch[3],\n path,\n };\n }\n\n // GitLab URL\n const glUrlMatch = input.match(GITLAB_URL);\n if (glUrlMatch) {\n const owner = glUrlMatch[1];\n const repo = glUrlMatch[2];\n const path = glUrlMatch[4];\n if (!owner || !repo) {\n return { type: 'command', value: input, inferredName: inferName(input, 'command') };\n }\n // Use last path segment as name if subpath provided, otherwise use repo name\n const inferredName = path ? (path.split('/').pop() ?? repo) : repo;\n return {\n type: 'gitlab',\n value: input,\n inferredName,\n owner,\n repo,\n ref: glUrlMatch[3],\n path,\n };\n }\n\n // HTTP URL (non-GitHub/GitLab = remote MCP server)\n if (HTTP_URL.test(input)) {\n return {\n type: 'remote',\n value: input,\n inferredName: inferName(input, 'remote'),\n };\n }\n\n // Local path (check before GitHub shorthand since ./ and ../ match shorthand regex)\n if (\n input.startsWith('/') ||\n input.startsWith('./') ||\n input.startsWith('../') ||\n input.startsWith('~')\n ) {\n return {\n type: 'local',\n value: input,\n inferredName: inferName(input, 'local'),\n };\n }\n\n // GitHub shorthand: owner/repo or owner/repo/path\n const ghShorthand = input.match(GITHUB_SHORTHAND);\n if (ghShorthand && !NPM_SCOPED.test(input)) {\n const owner = ghShorthand[1];\n const repo = ghShorthand[2];\n const path = ghShorthand[3];\n if (!owner || !repo) {\n return { type: 'command', value: input, inferredName: inferName(input, 'command') };\n }\n // Use last path segment as name if subpath provided, otherwise use repo name\n const inferredName = path ? (path.split('/').pop() ?? repo) : repo;\n return {\n type: 'github',\n value: `https://github.com/${owner}/${repo}`,\n inferredName,\n owner,\n repo,\n path,\n };\n }\n\n // Library skill: package:skill or @scope/package:skill\n const libraryMatch = input.match(LIBRARY_SKILL);\n if (libraryMatch) {\n return {\n type: 'library',\n value: input,\n inferredName: inferName(input, 'library'),\n owner: libraryMatch[1], // This will be the package name, e.g. @cleocode/skills\n repo: libraryMatch[2], // This will be the skill name, e.g. ct-research-agent\n };\n }\n\n // Scoped npm package: @scope/name\n if (NPM_SCOPED.test(input)) {\n return {\n type: 'package',\n value: input,\n inferredName: inferName(input, 'package'),\n };\n }\n\n // Simple npm package name (no spaces, no slashes)\n if (NPM_PACKAGE.test(input) && !input.includes(' ')) {\n return {\n type: 'package',\n value: input,\n inferredName: inferName(input, 'package'),\n };\n }\n\n // Default: treat as command\n return {\n type: 'command',\n value: input,\n inferredName: inferName(input, 'command'),\n };\n}\n\n/**\n * Check if a source string looks like a marketplace scoped name (`@author/name`).\n *\n * @remarks\n * Matches strings in the `@scope/name` format commonly used by npm packages\n * and marketplace skill identifiers.\n *\n * @param input - Source string to check\n * @returns `true` if the input matches the `@scope/name` pattern\n *\n * @example\n * ```typescript\n * isMarketplaceScoped(\"@anthropic/my-skill\"); // true\n * isMarketplaceScoped(\"my-skill\"); // false\n * isMarketplaceScoped(\"owner/repo\"); // false\n * ```\n *\n * @public\n */\nexport function isMarketplaceScoped(input: string): boolean {\n return /^@[a-zA-Z0-9_.-]+\\/[a-zA-Z0-9_.-]+$/.test(input);\n}\n","/**\n * Security scanning engine for SKILL.md files\n *\n * Scans skill content against 46+ security rules\n * and produces findings with line-level precision.\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport type { AuditFinding, AuditResult, AuditRule, AuditSeverity } from '../../../types.js';\nimport { AUDIT_RULES } from './rules.js';\n\nconst SEVERITY_WEIGHTS: Record<AuditSeverity, number> = {\n critical: 25,\n high: 15,\n medium: 8,\n low: 3,\n info: 0,\n};\n\n/**\n * Scan a single file against security audit rules.\n *\n * @remarks\n * Checks each line of the file against all active rules and produces findings\n * with line-level precision. Calculates a security score (100 = clean, 0 = dangerous)\n * based on severity-weighted penalties.\n *\n * @param filePath - Absolute path to the file to scan\n * @param rules - Custom rules to scan against (defaults to the built-in 46+ rules)\n * @returns Audit result with findings, score, and pass/fail status\n *\n * @example\n * ```typescript\n * const result = await scanFile(\"/path/to/SKILL.md\");\n * console.log(`Score: ${result.score}/100, Passed: ${result.passed}`);\n * ```\n *\n * @public\n */\nexport async function scanFile(filePath: string, rules?: AuditRule[]): Promise<AuditResult> {\n if (!existsSync(filePath)) {\n return { file: filePath, findings: [], score: 100, passed: true };\n }\n\n const content = await readFile(filePath, 'utf-8');\n const lines = content.split('\\n');\n const activeRules = rules ?? AUDIT_RULES;\n const findings: AuditFinding[] = [];\n\n for (const rule of activeRules) {\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? '';\n const match = line.match(rule.pattern);\n if (match) {\n findings.push({\n rule,\n line: i + 1,\n column: (match.index ?? 0) + 1,\n match: match[0],\n context: line.trim(),\n });\n }\n }\n }\n\n // Calculate score (100 = clean, 0 = very dangerous)\n const totalPenalty = findings.reduce(\n (sum, f) => sum + (SEVERITY_WEIGHTS[f.rule.severity] ?? 0),\n 0,\n );\n const score = Math.max(0, 100 - totalPenalty);\n const passed = !findings.some(\n (f) => f.rule.severity === 'critical' || f.rule.severity === 'high',\n );\n\n return { file: filePath, findings, score, passed };\n}\n\n/**\n * Scan a directory of skills for security issues.\n *\n * @remarks\n * Iterates over skill subdirectories and scans each `SKILL.md` file found.\n *\n * @param dirPath - Absolute path to the skills directory to scan\n * @returns Array of audit results, one per scanned SKILL.md\n *\n * @example\n * ```typescript\n * import { getCanonicalSkillsDir } from \"../../paths/standard.js\";\n *\n * const results = await scanDirectory(getCanonicalSkillsDir());\n * const failing = results.filter(r => !r.passed);\n * ```\n *\n * @public\n */\nexport async function scanDirectory(dirPath: string): Promise<AuditResult[]> {\n const { readdir } = await import('node:fs/promises');\n const { join } = await import('node:path');\n\n if (!existsSync(dirPath)) return [];\n\n const entries = await readdir(dirPath, { withFileTypes: true });\n const results: AuditResult[] = [];\n\n for (const entry of entries) {\n if (entry.isDirectory() || entry.isSymbolicLink()) {\n const skillFile = join(dirPath, entry.name, 'SKILL.md');\n if (existsSync(skillFile)) {\n results.push(await scanFile(skillFile));\n }\n }\n }\n\n return results;\n}\n\n/**\n * Convert audit results to SARIF 2.1.0 format (Static Analysis Results Interchange Format).\n *\n * @remarks\n * Produces a standards-compliant SARIF document suitable for CI/CD integration\n * and code scanning tools (e.g. GitHub Code Scanning).\n *\n * @param results - Array of audit results to convert\n * @returns SARIF 2.1.0 JSON object\n *\n * @example\n * ```typescript\n * const results = await scanDirectory(\"/path/to/skills\");\n * const sarif = toSarif(results);\n * writeFileSync(\"audit.sarif\", JSON.stringify(sarif, null, 2));\n * ```\n *\n * @public\n */\nexport function toSarif(results: AuditResult[]): object {\n return {\n $schema:\n 'https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json',\n version: '2.1.0',\n runs: [\n {\n tool: {\n driver: {\n name: 'caamp-audit',\n version: '0.1.0',\n rules: AUDIT_RULES.map((r) => ({\n id: r.id,\n name: r.name,\n shortDescription: { text: r.description },\n defaultConfiguration: {\n level: r.severity === 'critical' || r.severity === 'high' ? 'error' : 'warning',\n },\n properties: { category: r.category },\n })),\n },\n },\n results: results.flatMap((result) =>\n result.findings.map((f) => ({\n ruleId: f.rule.id,\n level:\n f.rule.severity === 'critical' || f.rule.severity === 'high' ? 'error' : 'warning',\n message: { text: `${f.rule.description}: ${f.match}` },\n locations: [\n {\n physicalLocation: {\n artifactLocation: { uri: result.file },\n region: {\n startLine: f.line,\n startColumn: f.column,\n },\n },\n },\n ],\n })),\n ),\n },\n ],\n };\n}\n","/**\n * Security audit rules for SKILL.md scanning\n *\n * 46+ rules across categories: prompt injection, command injection,\n * data exfiltration, privilege escalation, obfuscation, and more.\n */\n\nimport type { AuditRule, AuditSeverity } from '../../../types.js';\n\nfunction rule(\n id: string,\n name: string,\n description: string,\n severity: AuditSeverity,\n category: string,\n pattern: RegExp,\n): AuditRule {\n return { id, name, description, severity, category, pattern };\n}\n\n/**\n * Complete set of security audit rules for SKILL.md scanning.\n *\n * @remarks\n * Contains 46+ rules covering prompt injection, command injection, data exfiltration,\n * privilege escalation, filesystem abuse, network abuse, obfuscation, supply chain,\n * and information disclosure categories.\n *\n * @public\n */\nexport const AUDIT_RULES: AuditRule[] = [\n // ── Prompt Injection ────────────────────────────────────────\n rule(\n 'PI001',\n 'System prompt override',\n 'Attempts to override system instructions',\n 'critical',\n 'prompt-injection',\n /(?:ignore|forget|disregard)\\s+(?:all\\s+)?(?:previous|prior|above|system)\\s+(?:instructions|prompts|rules)/i,\n ),\n rule(\n 'PI002',\n 'Role manipulation',\n 'Attempts to assume a different role',\n 'critical',\n 'prompt-injection',\n /(?:you\\s+are\\s+now|act\\s+as|pretend\\s+(?:to\\s+be|you're)|your\\s+new\\s+role\\s+is)/i,\n ),\n rule(\n 'PI003',\n 'Jailbreak attempt',\n 'Common jailbreak patterns',\n 'critical',\n 'prompt-injection',\n /(?:DAN|Do\\s+Anything\\s+Now|developer\\s+mode|god\\s+mode|unrestricted\\s+mode)/i,\n ),\n rule(\n 'PI004',\n 'Instruction override',\n 'Direct instruction override attempt',\n 'high',\n 'prompt-injection',\n /(?:new\\s+instructions?:|updated?\\s+instructions?:|override\\s+instructions?:)/i,\n ),\n rule(\n 'PI005',\n 'Hidden instructions',\n 'Instructions hidden in comments or whitespace',\n 'high',\n 'prompt-injection',\n /<!--[\\s\\S]*?(?:execute|run|ignore|override)[\\s\\S]*?-->/i,\n ),\n rule(\n 'PI006',\n 'Encoding bypass',\n 'Base64 or encoded content',\n 'medium',\n 'prompt-injection',\n /(?:base64|atob|btoa|decodeURI|unescape)\\s*\\(/i,\n ),\n rule(\n 'PI007',\n 'Context manipulation',\n 'Fake conversation context',\n 'high',\n 'prompt-injection',\n /(?:Human:|Assistant:|User:|System:)\\s*(?:ignore|execute|run)/i,\n ),\n rule(\n 'PI008',\n 'Token smuggling',\n 'Invisible characters or zero-width spaces',\n 'medium',\n 'prompt-injection',\n /(?:\\u200B|\\u200C|\\u200D|\\u2060|\\uFEFF)/,\n ),\n\n // ── Command Injection ───────────────────────────────────────\n rule(\n 'CI001',\n 'Destructive command',\n 'File deletion or system modification',\n 'critical',\n 'command-injection',\n /(?:rm\\s+-rf|rmdir\\s+\\/s|del\\s+\\/f|format\\s+[a-z]:|mkfs|dd\\s+if=)/i,\n ),\n rule(\n 'CI002',\n 'Remote code execution',\n 'Downloading and executing remote code',\n 'critical',\n 'command-injection',\n /(?:curl|wget|fetch)\\s+.*\\|\\s*(?:sh|bash|zsh|python|node|eval)/i,\n ),\n rule('CI003', 'Eval usage', 'Dynamic code execution', 'high', 'command-injection', /\\beval\\s*\\(/),\n rule(\n 'CI004',\n 'Shell spawn',\n 'Spawning shell processes',\n 'high',\n 'command-injection',\n /(?:exec|spawn|system|popen)\\s*\\(\\s*['\"`]/,\n ),\n rule(\n 'CI005',\n 'Sudo escalation',\n 'Privilege escalation via sudo',\n 'critical',\n 'command-injection',\n /sudo\\s+(?:rm|chmod|chown|mv|cp|dd|mkfs|format)/i,\n ),\n rule(\n 'CI006',\n 'Environment manipulation',\n 'Modifying PATH or critical env vars',\n 'high',\n 'command-injection',\n /(?:export\\s+PATH|setx?\\s+PATH|PATH=.*:)/i,\n ),\n rule(\n 'CI007',\n 'Cron/scheduled task',\n 'Installing scheduled tasks',\n 'high',\n 'command-injection',\n /(?:crontab|at\\s+\\d|schtasks|launchctl\\s+load)/i,\n ),\n rule(\n 'CI008',\n 'Network listener',\n 'Starting network services',\n 'high',\n 'command-injection',\n /(?:nc\\s+-l|ncat\\s+-l|socat\\s+|python.*SimpleHTTPServer|php\\s+-S)/i,\n ),\n\n // ── Data Exfiltration ───────────────────────────────────────\n rule(\n 'DE001',\n 'Credential access',\n 'Reading credential files',\n 'critical',\n 'data-exfiltration',\n /(?:\\.env|\\.aws\\/credentials|\\.ssh\\/|\\.gnupg|\\.netrc|credentials\\.json|token\\.json)/i,\n ),\n rule(\n 'DE002',\n 'API key extraction',\n 'Patterns matching API key theft',\n 'critical',\n 'data-exfiltration',\n /(?:API[_-]?KEY|SECRET[_-]?KEY|ACCESS[_-]?TOKEN|PRIVATE[_-]?KEY)\\s*[=:]/i,\n ),\n rule(\n 'DE003',\n 'Data upload',\n 'Uploading data to external services',\n 'high',\n 'data-exfiltration',\n /(?:curl|wget|fetch).*(?:POST|PUT|PATCH).*(?:pastebin|gist|transfer\\.sh|requestbin|webhook)/i,\n ),\n rule(\n 'DE004',\n 'Browser data theft',\n 'Accessing browser profiles or cookies',\n 'critical',\n 'data-exfiltration',\n /(?:\\.mozilla|\\.chrome|\\.config\\/google-chrome|Cookies|Login\\s+Data|Local\\s+State)/i,\n ),\n rule(\n 'DE005',\n 'Git credential theft',\n 'Accessing git credentials',\n 'high',\n 'data-exfiltration',\n /(?:git\\s+credential|\\.git-credentials|\\.gitconfig\\s+credential)/i,\n ),\n rule(\n 'DE006',\n 'Keychain access',\n 'Accessing system keychain',\n 'critical',\n 'data-exfiltration',\n /(?:security\\s+find-generic-password|security\\s+find-internet-password|keyring\\s+get)/i,\n ),\n\n // ── Privilege Escalation ────────────────────────────────────\n rule(\n 'PE001',\n 'Chmod dangerous',\n 'Setting dangerous file permissions',\n 'high',\n 'privilege-escalation',\n /chmod\\s+(?:777|666|a\\+[rwx]|o\\+[rwx])/i,\n ),\n rule(\n 'PE002',\n 'SUID/SGID',\n 'Setting SUID or SGID bits',\n 'critical',\n 'privilege-escalation',\n /chmod\\s+[ug]\\+s/i,\n ),\n rule(\n 'PE003',\n 'Docker escape',\n 'Container escape patterns',\n 'critical',\n 'privilege-escalation',\n /(?:--privileged|--cap-add\\s+SYS_ADMIN|--pid=host|nsenter)/i,\n ),\n rule(\n 'PE004',\n 'Kernel module',\n 'Loading kernel modules',\n 'critical',\n 'privilege-escalation',\n /(?:insmod|modprobe|rmmod)\\s+/i,\n ),\n\n // ── Filesystem Abuse ────────────────────────────────────────\n rule(\n 'FS001',\n 'System directory write',\n 'Writing to system directories',\n 'critical',\n 'filesystem',\n /(?:\\/etc\\/|\\/usr\\/|\\/bin\\/|\\/sbin\\/|C:\\\\Windows\\\\|C:\\\\Program Files)/i,\n ),\n rule(\n 'FS002',\n 'Hidden file creation',\n 'Creating hidden files',\n 'medium',\n 'filesystem',\n /(?:touch|mkdir|cp|mv)\\s+\\.[a-zA-Z]/,\n ),\n rule(\n 'FS003',\n 'Symlink attack',\n 'Creating symlinks to sensitive files',\n 'high',\n 'filesystem',\n /ln\\s+-s.*(?:\\/etc\\/passwd|\\/etc\\/shadow|\\.ssh|\\.env)/i,\n ),\n rule(\n 'FS004',\n 'Mass file operation',\n 'Recursive operations on broad paths',\n 'medium',\n 'filesystem',\n /(?:find|xargs|rm|chmod|chown)\\s+.*(?:\\/\\s|\\/\\*|-R\\s+\\/)/i,\n ),\n\n // ── Network Abuse ──────────────────────────────────────────\n rule(\n 'NA001',\n 'DNS exfiltration',\n 'Data exfiltration via DNS',\n 'high',\n 'network',\n /(?:dig|nslookup|host)\\s+.*\\$\\{?[A-Z_]+/i,\n ),\n rule(\n 'NA002',\n 'Reverse shell',\n 'Reverse shell patterns',\n 'critical',\n 'network',\n /(?:bash\\s+-i|\\/dev\\/tcp\\/|mkfifo|nc\\s+.*-e)/i,\n ),\n rule(\n 'NA003',\n 'Port scanning',\n 'Network scanning',\n 'medium',\n 'network',\n /(?:nmap|masscan|zmap)\\s+/i,\n ),\n rule(\n 'NA004',\n 'Proxy/tunnel',\n 'Creating network tunnels',\n 'high',\n 'network',\n /(?:ssh\\s+-[DRLW]|ngrok|chisel|bore)/i,\n ),\n\n // ── Obfuscation ─────────────────────────────────────────────\n rule(\n 'OB001',\n 'Hex encoding',\n 'Hex-encoded commands',\n 'medium',\n 'obfuscation',\n /\\\\x[0-9a-fA-F]{2}(?:\\\\x[0-9a-fA-F]{2}){3,}/,\n ),\n rule(\n 'OB002',\n 'String concatenation',\n 'Building commands via concatenation',\n 'medium',\n 'obfuscation',\n /(?:\\$\\{[A-Z]+\\}\\$\\{[A-Z]+\\}|['\"][a-z]+['\"]\\.['\"][a-z]+['\"])/i,\n ),\n rule(\n 'OB003',\n 'Unicode escape',\n 'Unicode-escaped commands',\n 'medium',\n 'obfuscation',\n /\\\\u[0-9a-fA-F]{4}(?:\\\\u[0-9a-fA-F]{4}){3,}/,\n ),\n\n // ── Supply Chain ────────────────────────────────────────────\n rule(\n 'SC001',\n 'Package install',\n 'Installing packages at runtime',\n 'medium',\n 'supply-chain',\n /(?:npm\\s+install|pip\\s+install|gem\\s+install|cargo\\s+install)\\s+(?!-)/,\n ),\n rule(\n 'SC002',\n 'Typosquatting patterns',\n 'Packages with suspicious names',\n 'low',\n 'supply-chain',\n /(?:npm\\s+install|pip\\s+install)\\s+(?:reqeusts|requets|reqests|lodahs|lodashe)/i,\n ),\n rule(\n 'SC003',\n 'Postinstall script',\n 'npm lifecycle scripts',\n 'medium',\n 'supply-chain',\n /(?:preinstall|postinstall|preuninstall|postuninstall)\\s*[\":]/i,\n ),\n rule(\n 'SC004',\n 'Registry override',\n 'Changing package registry',\n 'high',\n 'supply-chain',\n /(?:registry\\s*=|--registry\\s+)(?!https:\\/\\/registry\\.npmjs\\.org)/i,\n ),\n\n // ── Information Disclosure ──────────────────────────────────\n rule(\n 'ID001',\n 'Process listing',\n 'Listing running processes',\n 'low',\n 'info-disclosure',\n /(?:ps\\s+aux|top\\s+-b|tasklist)/i,\n ),\n rule(\n 'ID002',\n 'System information',\n 'Gathering system information',\n 'low',\n 'info-disclosure',\n /(?:uname\\s+-a|systeminfo|hostnamectl)/i,\n ),\n rule(\n 'ID003',\n 'Network enumeration',\n 'Listing network configuration',\n 'low',\n 'info-disclosure',\n /(?:ifconfig|ip\\s+addr|ipconfig|netstat\\s+-[at])/i,\n ),\n];\n\n/**\n * Get audit rules filtered by category.\n *\n * @remarks\n * Filters the built-in rule set by a specific category string such as\n * `\"prompt-injection\"`, `\"command-injection\"`, or `\"data-exfiltration\"`.\n *\n * @param category - Category name to filter by\n * @returns Array of rules matching the given category\n *\n * @example\n * ```typescript\n * const piRules = getRulesByCategory(\"prompt-injection\");\n * console.log(`${piRules.length} prompt injection rules`);\n * ```\n *\n * @public\n */\nexport function getRulesByCategory(category: string): AuditRule[] {\n return AUDIT_RULES.filter((r) => r.category === category);\n}\n\n/**\n * Get audit rules filtered by severity level.\n *\n * @remarks\n * Filters the built-in rule set by severity: `\"critical\"`, `\"high\"`,\n * `\"medium\"`, `\"low\"`, or `\"info\"`.\n *\n * @param severity - Severity level to filter by\n * @returns Array of rules matching the given severity\n *\n * @example\n * ```typescript\n * const criticalRules = getRulesBySeverity(\"critical\");\n * console.log(`${criticalRules.length} critical rules`);\n * ```\n *\n * @public\n */\nexport function getRulesBySeverity(severity: AuditSeverity): AuditRule[] {\n return AUDIT_RULES.filter((r) => r.severity === severity);\n}\n\n/**\n * Get all unique rule categories.\n *\n * @remarks\n * Extracts and deduplicates the category field from all built-in audit rules.\n *\n * @returns Array of unique category name strings\n *\n * @example\n * ```typescript\n * const categories = getCategories();\n * // [\"prompt-injection\", \"command-injection\", \"data-exfiltration\", ...]\n * ```\n *\n * @public\n */\nexport function getCategories(): string[] {\n return [...new Set(AUDIT_RULES.map((r) => r.category))];\n}\n","/**\n * Skills lock file management\n *\n * Shares the same canonical lock file as MCP.\n */\n\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nimport { simpleGit } from 'simple-git';\nimport type { LockEntry, SourceType } from '../../types.js';\nimport { readLockFile, updateLockFile } from '../lock-utils.js';\nimport { parseSource } from '../sources/parser.js';\n\nconst execFileAsync = promisify(execFile);\n\n/**\n * Record a skill installation in the lock file.\n *\n * @remarks\n * Creates or updates an entry in `lock.skills`. If the skill already exists,\n * the agent list is merged and `updatedAt` is refreshed while `installedAt` is preserved.\n *\n * @param skillName - Skill name\n * @param scopedName - Scoped name (may include marketplace scope)\n * @param source - Original source string\n * @param sourceType - Classified source type\n * @param agents - Provider IDs the skill was linked to\n * @param canonicalPath - Absolute path to the canonical installation\n * @param isGlobal - Whether this is a global installation\n * @param projectDir - Project directory (for project-scoped installs)\n * @param version - Version string or commit SHA\n *\n * @example\n * ```typescript\n * import { getCanonicalSkillsDir } from \"../paths/standard.js\";\n * import { join } from \"node:path\";\n *\n * await recordSkillInstall(\n * \"my-skill\", \"my-skill\", \"owner/repo\", \"github\",\n * [\"claude-code\"], join(getCanonicalSkillsDir(), \"my-skill\"), true,\n * );\n * ```\n *\n * @public\n */\nexport async function recordSkillInstall(\n skillName: string,\n scopedName: string,\n source: string,\n sourceType: SourceType,\n agents: string[],\n canonicalPath: string,\n isGlobal: boolean,\n projectDir?: string,\n version?: string,\n): Promise<void> {\n await updateLockFile((lock) => {\n const now = new Date().toISOString();\n const existing = lock.skills[skillName];\n\n lock.skills[skillName] = {\n name: skillName,\n scopedName: existing?.scopedName ?? scopedName,\n source: existing?.source ?? source,\n sourceType: existing?.sourceType ?? sourceType,\n version: version ?? existing?.version,\n installedAt: existing?.installedAt ?? now,\n updatedAt: now,\n agents: [...new Set([...(existing?.agents ?? []), ...agents])],\n canonicalPath,\n isGlobal: existing?.isGlobal ?? isGlobal,\n projectDir: existing?.projectDir ?? projectDir,\n };\n });\n}\n\n/**\n * Remove a skill entry from the lock file.\n *\n * @remarks\n * Deletes the skill's entry from `lock.skills` if it exists. Does not remove\n * any files from disk.\n *\n * @param skillName - Name of the skill to remove\n * @returns `true` if the entry was found and removed, `false` if not found\n *\n * @example\n * ```typescript\n * const removed = await removeSkillFromLock(\"my-skill\");\n * console.log(removed ? \"Removed\" : \"Not found\");\n * ```\n *\n * @public\n */\nexport async function removeSkillFromLock(skillName: string): Promise<boolean> {\n let removed = false;\n await updateLockFile((lock) => {\n if (!(skillName in lock.skills)) return;\n delete lock.skills[skillName];\n removed = true;\n });\n return removed;\n}\n\n/**\n * Get all skills tracked in the lock file.\n *\n * @remarks\n * Reads the lock file and returns the skills section as a record.\n *\n * @returns Record of skill name to lock entry\n *\n * @example\n * ```typescript\n * const skills = await getTrackedSkills();\n * for (const [name, entry] of Object.entries(skills)) {\n * console.log(`${name}: ${entry.source}`);\n * }\n * ```\n *\n * @public\n */\nexport async function getTrackedSkills(): Promise<Record<string, LockEntry>> {\n const lock = await readLockFile();\n return lock.skills;\n}\n\n/** Fetch the latest commit SHA for a GitHub/GitLab repo via ls-remote */\nasync function fetchLatestSha(repoUrl: string, ref?: string): Promise<string | null> {\n try {\n const git = simpleGit();\n const target = ref ?? 'HEAD';\n // Use --refs only for named refs (branches/tags), not for HEAD\n const args = target === 'HEAD' ? [repoUrl, 'HEAD'] : ['--refs', repoUrl, target];\n const result = await git.listRemote(args);\n const firstLine = result.trim().split('\\n')[0];\n if (!firstLine) return null;\n const sha = firstLine.split('\\t')[0];\n return sha ?? null;\n } catch {\n return null;\n }\n}\n\n/** Fetch the latest version for an npm package via npm view */\nasync function fetchLatestPackageVersion(packageName: string): Promise<string | null> {\n try {\n const { stdout } = await execFileAsync('npm', ['view', packageName, 'version']);\n return stdout.trim() || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Check if a skill has updates available by comparing the installed version\n * against the latest remote commit SHA.\n *\n * @remarks\n * Only supports GitHub, GitLab, and library (package-based) sources. Returns `\"unknown\"` for local,\n * package, or other source types.\n *\n * @param skillName - Name of the installed skill to check\n * @returns Object with update status, current version, and latest version\n *\n * @example\n * ```typescript\n * const update = await checkSkillUpdate(\"my-skill\");\n * if (update.hasUpdate) {\n * console.log(`Update available: ${update.currentVersion} -> ${update.latestVersion}`);\n * }\n * ```\n *\n * @public\n */\nexport async function checkSkillUpdate(skillName: string): Promise<{\n hasUpdate: boolean;\n currentVersion?: string;\n latestVersion?: string;\n status: 'up-to-date' | 'update-available' | 'unknown';\n}> {\n const lock = await readLockFile();\n const entry = lock.skills[skillName];\n if (!entry) {\n return { hasUpdate: false, status: 'unknown' };\n }\n\n // Only GitHub, GitLab, and library sources support remote checking\n if (\n entry.sourceType !== 'github' &&\n entry.sourceType !== 'gitlab' &&\n entry.sourceType !== 'library'\n ) {\n return {\n hasUpdate: false,\n currentVersion: entry.version,\n status: 'unknown',\n };\n }\n\n const parsed = parseSource(entry.source);\n if (!parsed.owner) {\n return {\n hasUpdate: false,\n currentVersion: entry.version,\n status: 'unknown',\n };\n }\n\n if (entry.sourceType === 'library') {\n const packageName = parsed.owner; // owner holds the package name for library type\n const latestVersion = await fetchLatestPackageVersion(packageName);\n if (!latestVersion) {\n return {\n hasUpdate: false,\n currentVersion: entry.version,\n status: 'unknown',\n };\n }\n const currentVersion = entry.version;\n const hasUpdate = !currentVersion || currentVersion !== latestVersion;\n return {\n hasUpdate,\n currentVersion: currentVersion ?? 'unknown',\n latestVersion,\n status: hasUpdate ? 'update-available' : 'up-to-date',\n };\n }\n\n if (!parsed.repo) {\n return {\n hasUpdate: false,\n currentVersion: entry.version,\n status: 'unknown',\n };\n }\n\n const host = parsed.type === 'gitlab' ? 'gitlab.com' : 'github.com';\n const repoUrl = `https://${host}/${parsed.owner}/${parsed.repo}.git`;\n const latestSha = await fetchLatestSha(repoUrl, parsed.ref);\n\n if (!latestSha) {\n return {\n hasUpdate: false,\n currentVersion: entry.version,\n status: 'unknown',\n };\n }\n\n const currentVersion = entry.version;\n const hasUpdate = !currentVersion || !latestSha.startsWith(currentVersion.slice(0, 7));\n\n return {\n hasUpdate,\n currentVersion: currentVersion ?? 'unknown',\n latestVersion: latestSha.slice(0, 12),\n status: hasUpdate ? 'update-available' : 'up-to-date',\n };\n}\n\n/**\n * Check for updates across all tracked skills.\n *\n * @remarks\n * Iterates over all skills in the lock file and checks each one concurrently\n * via {@link checkSkillUpdate}.\n *\n * @returns Object mapping skill names to their update status\n *\n * @example\n * ```typescript\n * const updates = await checkAllSkillUpdates();\n * for (const [name, status] of Object.entries(updates)) {\n * if (status.hasUpdate) {\n * console.log(`${name}: ${status.currentVersion} -> ${status.latestVersion}`);\n * }\n * }\n * ```\n *\n * @public\n */\nexport async function checkAllSkillUpdates(): Promise<\n Record<\n string,\n {\n hasUpdate: boolean;\n currentVersion?: string;\n latestVersion?: string;\n status: 'up-to-date' | 'update-available' | 'unknown';\n }\n >\n> {\n const lock = await readLockFile();\n const skillNames = Object.keys(lock.skills);\n\n const results: Record<\n string,\n {\n hasUpdate: boolean;\n currentVersion?: string;\n latestVersion?: string;\n status: 'up-to-date' | 'update-available' | 'unknown';\n }\n > = {};\n await Promise.all(\n skillNames.map(async (name) => {\n results[name] = await checkSkillUpdate(name);\n }),\n );\n\n return results;\n}\n","/**\n * Shared lock file utilities\n *\n * Single source of truth for reading/writing the canonical CAAMP lock file path.\n * Both MCP and skills lock modules import from here.\n */\n\nimport { existsSync } from 'node:fs';\nimport { mkdir, open, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';\nimport type { CaampLockFile } from '../types.js';\nimport { AGENTS_HOME, LOCK_FILE_PATH } from './paths/agents.js';\n\nconst LOCK_GUARD_PATH = `${LOCK_FILE_PATH}.lock`;\nconst STALE_LOCK_MS = 5_000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function removeStaleLock(): Promise<boolean> {\n try {\n const info = await stat(LOCK_GUARD_PATH);\n if (Date.now() - info.mtimeMs > STALE_LOCK_MS) {\n await rm(LOCK_GUARD_PATH, { force: true });\n return true;\n }\n } catch {\n // Lock file doesn't exist or can't be stat'd — not stale\n }\n return false;\n}\n\nasync function acquireLockGuard(retries = 40, delayMs = 25): Promise<void> {\n await mkdir(AGENTS_HOME, { recursive: true });\n\n for (let attempt = 0; attempt < retries; attempt += 1) {\n try {\n const handle = await open(LOCK_GUARD_PATH, 'wx');\n await handle.close();\n return;\n } catch (error) {\n if (\n !(error instanceof Error) ||\n !('code' in error) ||\n (error as NodeJS.ErrnoException).code !== 'EEXIST'\n ) {\n throw error;\n }\n // On first retry failure, check for stale lock from a crashed process\n if (attempt === 0) {\n const removed = await removeStaleLock();\n if (removed) continue;\n }\n await sleep(delayMs);\n }\n }\n\n throw new Error('Timed out waiting for lock file guard');\n}\n\nasync function releaseLockGuard(): Promise<void> {\n await rm(LOCK_GUARD_PATH, { force: true });\n}\n\nasync function writeLockFileUnsafe(lock: CaampLockFile): Promise<void> {\n const tmpPath = `${LOCK_FILE_PATH}.tmp-${process.pid}-${Date.now()}`;\n await writeFile(tmpPath, JSON.stringify(lock, null, 2) + '\\n', 'utf-8');\n await rename(tmpPath, LOCK_FILE_PATH);\n}\n\n/**\n * Read and parse the CAAMP lock file from disk.\n *\n * @remarks\n * Returns a default empty lock structure when the file does not exist or\n * cannot be parsed, ensuring callers always receive a valid object.\n *\n * @returns Parsed lock file contents\n *\n * @example\n * ```typescript\n * const lock = await readLockFile();\n * console.log(Object.keys(lock.mcpServers));\n * ```\n *\n * @public\n */\nexport async function readLockFile(): Promise<CaampLockFile> {\n try {\n if (!existsSync(LOCK_FILE_PATH)) {\n return { version: 1, skills: {}, mcpServers: {} };\n }\n const content = await readFile(LOCK_FILE_PATH, 'utf-8');\n return JSON.parse(content) as CaampLockFile;\n } catch {\n return { version: 1, skills: {}, mcpServers: {} };\n }\n}\n\n/**\n * Write the lock file atomically under a process lock guard.\n *\n * @remarks\n * Uses a file-system lock guard to prevent concurrent writes from multiple\n * CAAMP processes. The write itself is atomic (write-to-tmp then rename).\n *\n * @param lock - Lock file data to persist\n *\n * @example\n * ```typescript\n * const lock = await readLockFile();\n * lock.mcpServers[\"my-server\"] = entry;\n * await writeLockFile(lock);\n * ```\n *\n * @public\n */\nexport async function writeLockFile(lock: CaampLockFile): Promise<void> {\n await acquireLockGuard();\n try {\n await writeLockFileUnsafe(lock);\n } finally {\n await releaseLockGuard();\n }\n}\n\n/**\n * Safely read-modify-write the lock file under a process lock guard.\n *\n * @remarks\n * Acquires an exclusive file-system lock, reads the current lock file, applies\n * the updater callback, writes the result atomically, and releases the lock.\n * The updater may mutate the lock object in place.\n *\n * @param updater - Callback that modifies the lock object (may be async)\n * @returns The updated lock file contents after the write\n *\n * @example\n * ```typescript\n * const updated = await updateLockFile((lock) => {\n * lock.mcpServers[\"new-server\"] = entry;\n * });\n * ```\n *\n * @public\n */\nexport async function updateLockFile(\n updater: (lock: CaampLockFile) => void | Promise<void>,\n): Promise<CaampLockFile> {\n await acquireLockGuard();\n try {\n const lock = await readLockFile();\n await updater(lock);\n await writeLockFileUnsafe(lock);\n return lock;\n } finally {\n await releaseLockGuard();\n }\n}\n","/**\n * Default timeout in milliseconds for outbound HTTP requests.\n *\n * @public\n */\nexport const DEFAULT_FETCH_TIMEOUT_MS = 10_000;\n\ntype NetworkErrorKind = 'timeout' | 'http' | 'network';\n\n/**\n * Structured error for network failures with categorized kind.\n *\n * @remarks\n * Carries the original URL, a classification of the failure (`\"timeout\"`,\n * `\"http\"`, or `\"network\"`), and an optional HTTP status code for `\"http\"`\n * failures.\n *\n * @public\n */\nexport class NetworkError extends Error {\n /** Classification of the failure. */\n kind: NetworkErrorKind;\n /** URL that was being fetched. */\n url: string;\n /** HTTP status code (only present for `\"http\"` kind). */\n status?: number;\n\n constructor(message: string, kind: NetworkErrorKind, url: string, status?: number) {\n super(message);\n this.name = 'NetworkError';\n this.kind = kind;\n this.url = url;\n this.status = status;\n }\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === 'AbortError';\n}\n\n/**\n * Fetch a URL with an automatic timeout via `AbortSignal.timeout`.\n *\n * @remarks\n * Wraps the native `fetch` API to provide consistent timeout and error\n * handling. Abort errors are translated into `NetworkError` with kind\n * `\"timeout\"`, and other failures become `\"network\"` errors.\n *\n * @param url - URL to fetch\n * @param init - Optional `RequestInit` options forwarded to `fetch`\n * @param timeoutMs - Timeout in milliseconds (defaults to {@link DEFAULT_FETCH_TIMEOUT_MS})\n * @returns The `Response` object from the fetch call\n * @throws {@link NetworkError} on timeout or network failure\n *\n * @example\n * ```typescript\n * const response = await fetchWithTimeout(\"https://api.example.com/data\", undefined, 5000);\n * ```\n *\n * @public\n */\nexport async function fetchWithTimeout(\n url: string,\n init?: RequestInit,\n timeoutMs = DEFAULT_FETCH_TIMEOUT_MS,\n): Promise<Response> {\n try {\n return await fetch(url, {\n ...init,\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (error) {\n if (isAbortError(error)) {\n throw new NetworkError(`Request timed out after ${timeoutMs}ms`, 'timeout', url);\n }\n throw new NetworkError('Network request failed', 'network', url);\n }\n}\n\n/**\n * Assert that a `Response` has an OK status, throwing on failure.\n *\n * @remarks\n * Convenience guard that throws a `NetworkError` with kind `\"http\"` when the\n * response status is outside the 200-299 range.\n *\n * @param response - Fetch `Response` to validate\n * @param url - Original request URL (included in the error)\n * @returns The same `Response` if status is OK\n * @throws {@link NetworkError} when `response.ok` is `false`\n *\n * @example\n * ```typescript\n * const res = await fetchWithTimeout(url);\n * ensureOkResponse(res, url);\n * ```\n *\n * @public\n */\nexport function ensureOkResponse(response: Response, url: string): Response {\n if (!response.ok) {\n throw new NetworkError(\n `Request failed with status ${response.status}`,\n 'http',\n url,\n response.status,\n );\n }\n return response;\n}\n\n/**\n * Format a network error into a user-friendly message string.\n *\n * @remarks\n * Recognizes `NetworkError` instances and produces kind-specific messages\n * (timeout, HTTP status, generic network). Falls back to `Error.message` or\n * `String()` for unknown error types.\n *\n * @param error - The caught error value\n * @returns Human-readable error description\n *\n * @example\n * ```typescript\n * try {\n * await fetchWithTimeout(url);\n * } catch (err) {\n * console.error(formatNetworkError(err));\n * }\n * ```\n *\n * @public\n */\nexport function formatNetworkError(error: unknown): string {\n if (error instanceof NetworkError) {\n if (error.kind === 'timeout') {\n return 'Network request timed out. Please check your connection and try again.';\n }\n if (error.kind === 'http') {\n return `Marketplace request failed with HTTP ${error.status ?? 'unknown'}. Please try again shortly.`;\n }\n return 'Network request failed. Please check your connection and try again.';\n }\n\n if (error instanceof Error) return error.message;\n return String(error);\n}\n","/**\n * agentskills.in marketplace adapter\n *\n * Connects to the SkillsMP API for skill discovery.\n * GitHub is always the actual source for installation.\n */\n\nimport { ensureOkResponse, fetchWithTimeout } from '../network/fetch.js';\nimport type { MarketplaceAdapter, MarketplaceResult } from './types.js';\n\nconst API_BASE = 'https://www.agentskills.in/api/skills';\n\ninterface ApiSkill {\n id: string;\n name: string;\n description: string;\n author: string;\n scopedName: string;\n stars: number;\n forks: number;\n githubUrl: string;\n repoFullName: string;\n path: string;\n category?: string;\n hasContent: boolean;\n}\n\ninterface ApiResponse {\n skills: ApiSkill[];\n total: number;\n limit: number;\n offset: number;\n}\n\ninterface ScopedNameParts {\n author: string;\n name: string;\n}\n\nfunction parseScopedName(value: string): ScopedNameParts | null {\n const match = value.match(/^@([^/]+)\\/([^/]+)$/);\n if (!match) return null;\n return {\n author: match[1]!,\n name: match[2]!,\n };\n}\n\nfunction toResult(skill: ApiSkill): MarketplaceResult {\n return {\n name: skill.name,\n scopedName: skill.scopedName,\n description: skill.description,\n author: skill.author,\n stars: skill.stars,\n githubUrl: skill.githubUrl,\n repoFullName: skill.repoFullName,\n path: skill.path,\n source: 'agentskills.in',\n };\n}\n\n/**\n * Marketplace adapter for the agentskills.in API.\n *\n * @remarks\n * Implements the {@link MarketplaceAdapter} interface to search and retrieve\n * skills from the agentskills.in marketplace. GitHub remains the actual\n * source for skill installation — this adapter provides discovery only.\n *\n * @public\n */\nexport class SkillsMPAdapter implements MarketplaceAdapter {\n /** The marketplace identifier used in search results. */\n name = 'agentskills.in';\n\n /**\n * Search for skills by query string.\n *\n * @param query - Search query to match against skill names and descriptions.\n * @param limit - Maximum number of results to return.\n * @returns Array of marketplace results sorted by stars.\n */\n async search(query: string, limit = 20): Promise<MarketplaceResult[]> {\n const params = new URLSearchParams({\n search: query,\n limit: String(limit),\n sortBy: 'stars',\n });\n\n const url = `${API_BASE}?${params}`;\n const response = ensureOkResponse(await fetchWithTimeout(url), url);\n const data = (await response.json()) as ApiResponse;\n return data.skills.map(toResult);\n }\n\n /**\n * Look up a specific skill by its scoped name.\n *\n * @param scopedName - The scoped skill name (e.g. `\"@author/skill-name\"`).\n * @returns The matching marketplace result, or `null` if not found.\n */\n async getSkill(scopedName: string): Promise<MarketplaceResult | null> {\n const parts = parseScopedName(scopedName);\n const searchTerms = parts\n ? [parts.name, `${parts.author} ${parts.name}`, scopedName]\n : [scopedName];\n\n const seen = new Set<string>();\n for (const term of searchTerms) {\n if (seen.has(term)) continue;\n seen.add(term);\n\n const params = new URLSearchParams({\n search: term,\n limit: '50',\n sortBy: 'stars',\n });\n\n const url = `${API_BASE}?${params}`;\n const response = ensureOkResponse(await fetchWithTimeout(url), url);\n const data = (await response.json()) as ApiResponse;\n const match = data.skills.find(\n (s) => s.scopedName === scopedName || `@${s.author}/${s.name}` === scopedName,\n );\n if (match) {\n return toResult(match);\n }\n }\n\n return null;\n }\n}\n","/**\n * skills.sh marketplace adapter\n *\n * Connects to the skills.sh API for skill discovery.\n * Uses the Vercel Skills model where GitHub is the actual source.\n */\n\nimport { ensureOkResponse, fetchWithTimeout } from '../network/fetch.js';\nimport type { MarketplaceAdapter, MarketplaceResult } from './types.js';\n\nconst API_BASE = 'https://skills.sh/api';\n\ninterface SkillsShResult {\n name: string;\n author: string;\n description: string;\n repo: string;\n stars?: number;\n url: string;\n}\n\ninterface SkillsShResponse {\n results: SkillsShResult[];\n total: number;\n}\n\nfunction toResult(skill: SkillsShResult): MarketplaceResult {\n return {\n name: skill.name,\n scopedName: `@${skill.author}/${skill.name}`,\n description: skill.description,\n author: skill.author,\n stars: skill.stars ?? 0,\n githubUrl: skill.url,\n repoFullName: skill.repo,\n path: '',\n source: 'skills.sh',\n };\n}\n\n/**\n * Marketplace adapter for the skills.sh API.\n *\n * @remarks\n * Implements the {@link MarketplaceAdapter} interface to search and retrieve\n * skills from the skills.sh marketplace. Uses the Vercel Skills model where\n * GitHub is the actual source for installation.\n *\n * @public\n */\nexport class SkillsShAdapter implements MarketplaceAdapter {\n /** The marketplace identifier used in search results. */\n name = 'skills.sh';\n\n /**\n * Search for skills by query string.\n *\n * @param query - Search query to match against skill names.\n * @param limit - Maximum number of results to return.\n * @returns Array of marketplace results.\n */\n async search(query: string, limit = 20): Promise<MarketplaceResult[]> {\n const params = new URLSearchParams({\n q: query,\n limit: String(limit),\n });\n\n const url = `${API_BASE}/search?${params}`;\n const response = ensureOkResponse(await fetchWithTimeout(url), url);\n const data = (await response.json()) as SkillsShResponse;\n return data.results.map(toResult);\n }\n\n /**\n * Look up a specific skill by its scoped name.\n *\n * @param scopedName - The scoped skill name (e.g. `\"@author/skill-name\"`).\n * @returns The matching marketplace result, or `null` if not found.\n */\n async getSkill(scopedName: string): Promise<MarketplaceResult | null> {\n const results = await this.search(scopedName, 5);\n return results.find((r) => r.scopedName === scopedName) ?? null;\n }\n}\n","/**\n * Unified marketplace client\n *\n * Aggregates results from multiple marketplace adapters,\n * deduplicates, and sorts by relevance.\n */\n\nimport { SkillsMPAdapter } from './skillsmp.js';\nimport { SkillsShAdapter } from './skillssh.js';\nimport type { MarketplaceAdapter, MarketplaceResult } from './types.js';\n\n/**\n * Error thrown when all marketplace sources fail to respond.\n *\n * @remarks\n * Contains an array of per-adapter failure details so callers can report\n * which sources were unreachable and why.\n *\n * @public\n */\nexport class MarketplaceUnavailableError extends Error {\n /** Per-adapter failure messages. */\n details: string[];\n\n constructor(message: string, details: string[]) {\n super(message);\n this.name = 'MarketplaceUnavailableError';\n this.details = details;\n }\n}\n\n/**\n * Unified marketplace client that aggregates results from multiple marketplace adapters.\n *\n * Queries all configured marketplaces in parallel, deduplicates results by scoped name,\n * and sorts by star count.\n *\n * @remarks\n * Default adapters query agentskills.in and skills.sh. Custom adapters can\n * be injected via the constructor for testing or additional sources.\n *\n * @example\n * ```typescript\n * const client = new MarketplaceClient();\n * const results = await client.search(\"filesystem\");\n * for (const r of results) {\n * console.log(`${r.scopedName} (${r.stars} stars)`);\n * }\n * ```\n *\n * @public\n */\nexport class MarketplaceClient {\n /** Configured marketplace adapters. */\n private adapters: MarketplaceAdapter[];\n\n /**\n * Create a new marketplace client.\n *\n * @param adapters - Custom marketplace adapters (defaults to agentskills.in and skills.sh)\n *\n * @example\n * ```typescript\n * // Use default adapters\n * const client = new MarketplaceClient();\n *\n * // Use custom adapters\n * const client = new MarketplaceClient([myAdapter]);\n * ```\n */\n constructor(adapters?: MarketplaceAdapter[]) {\n this.adapters = adapters ?? [new SkillsMPAdapter(), new SkillsShAdapter()];\n }\n\n /**\n * Search all marketplaces and return deduplicated, sorted results.\n *\n * Queries all adapters in parallel and deduplicates by `scopedName`,\n * keeping the entry with the highest star count. Results are sorted by\n * stars descending.\n *\n * @param query - Search query string\n * @param limit - Maximum number of results to return (default: 20)\n * @returns Deduplicated and sorted marketplace results\n *\n * @example\n * ```typescript\n * const results = await client.search(\"code review\", 10);\n * ```\n */\n async search(query: string, limit = 20): Promise<MarketplaceResult[]> {\n const settled = await Promise.allSettled(\n this.adapters.map((adapter) => adapter.search(query, limit)),\n );\n\n const flat: MarketplaceResult[] = [];\n const failures: string[] = [];\n\n for (const [index, result] of settled.entries()) {\n const adapterName = this.adapters[index]?.name ?? 'unknown';\n\n if (result.status === 'fulfilled') {\n flat.push(...result.value);\n } else {\n const reason =\n result.reason instanceof Error ? result.reason.message : String(result.reason);\n failures.push(`${adapterName}: ${reason}`);\n }\n }\n\n if (flat.length === 0 && failures.length > 0) {\n throw new MarketplaceUnavailableError('All marketplace sources failed.', failures);\n }\n\n // Deduplicate by scopedName, keeping higher star count\n const seen = new Map<string, MarketplaceResult>();\n for (const result of flat) {\n const existing = seen.get(result.scopedName);\n if (!existing || result.stars > existing.stars) {\n seen.set(result.scopedName, result);\n }\n }\n\n // Sort by stars descending\n const deduplicated = Array.from(seen.values());\n deduplicated.sort((a, b) => b.stars - a.stars);\n\n return deduplicated.slice(0, limit);\n }\n\n /**\n * Get a specific skill by its scoped name from any marketplace.\n *\n * Tries each adapter in order and returns the first match.\n *\n * @param scopedName - Scoped skill name (e.g. `\"@author/my-skill\"`)\n * @returns The marketplace result, or `null` if not found in any marketplace\n *\n * @example\n * ```typescript\n * const skill = await client.getSkill(\"@anthropic/memory\");\n * ```\n */\n async getSkill(scopedName: string): Promise<MarketplaceResult | null> {\n const failures: string[] = [];\n\n for (const adapter of this.adapters) {\n try {\n const result = await adapter.getSkill(scopedName);\n if (result) return result;\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n failures.push(`${adapter.name}: ${reason}`);\n }\n }\n\n if (failures.length === this.adapters.length && this.adapters.length > 0) {\n throw new MarketplaceUnavailableError('All marketplace sources failed.', failures);\n }\n\n return null;\n }\n}\n","import type { MarketplaceResult } from '../marketplace/types.js';\n\n/**\n * Error codes used in skill recommendation validation.\n *\n * @remarks\n * These codes identify specific failure reasons when validating recommendation\n * criteria input. Each code maps to a human-readable error code string.\n *\n * @public\n */\nexport const RECOMMENDATION_ERROR_CODES = {\n QUERY_INVALID: 'E_SKILLS_QUERY_INVALID',\n NO_MATCHES: 'E_SKILLS_NO_MATCHES',\n SOURCE_UNAVAILABLE: 'E_SKILLS_SOURCE_UNAVAILABLE',\n CRITERIA_CONFLICT: 'E_SKILLS_CRITERIA_CONFLICT',\n} as const;\n\n/**\n * Union type of all recommendation error code string literals.\n *\n * @remarks\n * Derived from the values of {@link RECOMMENDATION_ERROR_CODES}. Used to type\n * the `code` field on validation issues.\n *\n * @public\n */\nexport type RecommendationErrorCode =\n (typeof RECOMMENDATION_ERROR_CODES)[keyof typeof RECOMMENDATION_ERROR_CODES];\n\n/**\n * Describes a single validation issue found in recommendation criteria.\n *\n * @remarks\n * Returned as part of {@link RecommendationValidationResult} when the input\n * criteria contain invalid or conflicting values.\n *\n * @public\n */\nexport interface RecommendationValidationIssue {\n /** The error code identifying the type of validation failure. */\n code: RecommendationErrorCode;\n /** The criteria field that caused the validation issue. */\n field: 'query' | 'mustHave' | 'prefer' | 'exclude';\n /** A human-readable description of the validation issue. */\n message: string;\n}\n\n/**\n * Result of validating recommendation criteria input.\n *\n * @remarks\n * When `valid` is true, the `issues` array is empty and the criteria can be\n * safely normalized for scoring. When `valid` is false, each issue describes\n * a specific problem that must be corrected.\n *\n * @public\n */\nexport interface RecommendationValidationResult {\n /** Whether the criteria passed all validation checks. */\n valid: boolean;\n /** List of validation issues found, empty when valid. */\n issues: RecommendationValidationIssue[];\n}\n\n/**\n * Raw user-provided criteria for skill recommendations.\n *\n * @remarks\n * All fields are optional, but at least one must be provided. String values\n * containing commas are tokenized into multiple terms. Arrays are flattened\n * and deduplicated during normalization.\n *\n * @public\n */\nexport interface RecommendationCriteriaInput {\n /** Free-text search query to match against skill metadata. */\n query?: string;\n /** Terms that a skill must match to be considered relevant. */\n mustHave?: string | string[];\n /** Terms that boost a skill's score when matched. */\n prefer?: string | string[];\n /** Terms that penalize a skill's score when matched. */\n exclude?: string | string[];\n}\n\n/**\n * Normalized and tokenized form of recommendation criteria.\n *\n * @remarks\n * Produced by {@link normalizeRecommendationCriteria} from raw\n * {@link RecommendationCriteriaInput}. All values are lowercased, deduplicated,\n * and sorted for deterministic scoring.\n *\n * @public\n */\nexport interface NormalizedRecommendationCriteria {\n /** The lowercased, trimmed query string. */\n query: string;\n /** Individual tokens extracted from the query string. */\n queryTokens: string[];\n /** Sorted, deduplicated list of required match terms. */\n mustHave: string[];\n /** Sorted, deduplicated list of preferred match terms. */\n prefer: string[];\n /** Sorted, deduplicated list of exclusion terms. */\n exclude: string[];\n}\n\n/**\n * String literal union of all reason codes emitted during skill scoring.\n *\n * @remarks\n * Each code corresponds to a specific scoring signal, such as matching a\n * required term, detecting a modern marker, or applying an exclusion penalty.\n * Used in {@link RecommendationReason} to explain score contributions.\n *\n * @public\n */\nexport type RecommendationReasonCode =\n | 'MATCH_TOPIC_GITBOOK'\n | 'HAS_GIT_SYNC'\n | 'HAS_API_WORKFLOW'\n | 'PENALTY_LEGACY_CLI'\n | 'MUST_HAVE_MATCH'\n | 'MISSING_MUST_HAVE'\n | 'PREFER_MATCH'\n | 'QUERY_MATCH'\n | 'STAR_SIGNAL'\n | 'METADATA_SIGNAL'\n | 'MODERN_MARKER'\n | 'LEGACY_MARKER'\n | 'EXCLUDE_MATCH';\n\n/**\n * A single reason contributing to a skill's recommendation score.\n *\n * @remarks\n * Each reason captures one scoring signal with an optional detail string\n * providing additional context such as match counts.\n *\n * @public\n */\nexport interface RecommendationReason {\n /** The reason code identifying the scoring signal. */\n code: RecommendationReasonCode;\n /** Optional detail providing additional context, such as match count. */\n detail?: string;\n}\n\n/**\n * Detailed breakdown of a skill's recommendation score by category.\n *\n * @remarks\n * Only populated when `includeDetails` is true in {@link RecommendationOptions}.\n * Each field represents the weighted contribution of a scoring category to the\n * total score.\n *\n * @public\n */\nexport interface RecommendationScoreBreakdown {\n /** Score contribution from must-have term matches. */\n mustHave: number;\n /** Score contribution from preferred term matches. */\n prefer: number;\n /** Score contribution from query token matches. */\n query: number;\n /** Score contribution from repository star count signal. */\n stars: number;\n /** Score contribution from metadata quality and source confidence. */\n metadata: number;\n /** Score contribution from modern vs legacy marker detection. */\n modernity: number;\n /** Penalty applied for matching excluded terms. */\n exclusionPenalty: number;\n /** The final aggregated recommendation score. */\n total: number;\n}\n\n/**\n * A single skill recommendation with its computed score and explanations.\n *\n * @remarks\n * Produced by {@link scoreSkillRecommendation} for each candidate skill.\n * Contains the raw score, human-readable reasons, tradeoff warnings, and\n * an optional detailed breakdown.\n *\n * @public\n */\nexport interface RankedSkillRecommendation {\n /** The marketplace skill result being scored. */\n skill: MarketplaceResult;\n /** The computed recommendation score, higher is better. */\n score: number;\n /** List of reasons explaining the score contributions. */\n reasons: RecommendationReason[];\n /** Human-readable tradeoff warnings for the skill. */\n tradeoffs: string[];\n /** Whether the skill matched one or more exclusion terms. */\n excluded: boolean;\n /** Optional detailed score breakdown by category. */\n breakdown?: RecommendationScoreBreakdown;\n}\n\n/**\n * Configuration options for the skill recommendation engine.\n *\n * @remarks\n * All fields are optional and have sensible defaults. Custom weights override\n * individual scoring factors while preserving defaults for unspecified weights.\n *\n * @public\n */\nexport interface RecommendationOptions {\n /** Maximum number of results to return from the ranked list. */\n top?: number;\n /** Whether to include detailed score breakdown in each result. */\n includeDetails?: boolean;\n /** Partial weight overrides for individual scoring factors. */\n weights?: Partial<RecommendationWeights>;\n /** Custom modern technology marker strings for modernity scoring. */\n modernMarkers?: string[];\n /** Custom legacy technology marker strings for modernity scoring. */\n legacyMarkers?: string[];\n}\n\n/**\n * Numeric weights controlling the recommendation scoring algorithm.\n *\n * @remarks\n * Each weight multiplies the corresponding match count or signal value.\n * Higher values increase the influence of that factor on the total score.\n * Penalty weights are subtracted from the total.\n *\n * @public\n */\nexport interface RecommendationWeights {\n /** Weight applied per must-have term match. */\n mustHaveMatch: number;\n /** Weight applied per preferred term match. */\n preferMatch: number;\n /** Weight applied per query token match. */\n queryTokenMatch: number;\n /** Multiplier for the logarithmic star count signal. */\n starsFactor: number;\n /** Boost for metadata quality and source confidence. */\n metadataBoost: number;\n /** Boost applied per modern technology marker match. */\n modernMarkerBoost: number;\n /** Penalty applied per legacy technology marker match. */\n legacyMarkerPenalty: number;\n /** Penalty applied per excluded term match. */\n excludePenalty: number;\n /** Penalty applied per missing must-have term. */\n missingMustHavePenalty: number;\n}\n\n/**\n * The complete result of a skill recommendation operation.\n *\n * @remarks\n * Contains the normalized criteria that were used for scoring and the\n * ranked list of skill recommendations sorted by descending score.\n *\n * @public\n */\nexport interface RecommendSkillsResult {\n /** The normalized criteria used for scoring. */\n criteria: NormalizedRecommendationCriteria;\n /** Skills ranked by recommendation score, highest first. */\n ranking: RankedSkillRecommendation[];\n}\n\nconst DEFAULT_WEIGHTS: RecommendationWeights = {\n mustHaveMatch: 10,\n preferMatch: 4,\n queryTokenMatch: 3,\n starsFactor: 2,\n metadataBoost: 2,\n modernMarkerBoost: 3,\n legacyMarkerPenalty: 3,\n excludePenalty: 25,\n missingMustHavePenalty: 20,\n};\n\nconst DEFAULT_MODERN_MARKERS = ['svelte 5', 'runes', 'lafs', 'slsa', 'drizzle', 'better-auth'];\nconst DEFAULT_LEGACY_MARKERS = [\n 'svelte 3',\n 'jquery',\n 'bower',\n 'legacy',\n 'book.json',\n 'gitbook-cli',\n];\n\n/**\n * Splits a comma-separated criteria string into normalized tokens.\n *\n * @remarks\n * Each token is trimmed and lowercased. Empty tokens are removed.\n * This is the core tokenization used by the recommendation engine to\n * process user-provided criteria values.\n *\n * @param value - The comma-separated string to tokenize\n * @returns An array of trimmed, lowercased, non-empty tokens\n *\n * @example\n * ```typescript\n * const tokens = tokenizeCriteriaValue(\"React, TypeScript, svelte\");\n * // returns [\"react\", \"typescript\", \"svelte\"]\n * ```\n *\n * @public\n */\nexport function tokenizeCriteriaValue(value: string): string[] {\n return value\n .split(',')\n .map((part) => part.trim().toLowerCase())\n .filter(Boolean);\n}\n\nfunction normalizeList(value: unknown): string[] {\n if (value === undefined) return [];\n\n if (!(typeof value === 'string' || Array.isArray(value))) return [];\n\n const source = Array.isArray(value) ? value : [value];\n const flattened = source.flatMap((item) =>\n typeof item === 'string' ? tokenizeCriteriaValue(item) : [],\n );\n return Array.from(new Set(flattened)).sort((a, b) => a.localeCompare(b));\n}\n\nfunction hasAnyCriteriaInput(input: RecommendationCriteriaInput): boolean {\n const query = typeof input.query === 'string' ? input.query.trim() : '';\n if (query.length > 0) return true;\n\n const lists = [input.mustHave, input.prefer, input.exclude];\n return lists.some((list) => normalizeList(list).length > 0);\n}\n\n/**\n * Validates recommendation criteria input for correctness and consistency.\n *\n * @remarks\n * Checks that all fields have valid types, that no terms appear in both\n * inclusion and exclusion lists, and that at least one criteria value is\n * provided. Returns a result with `valid: true` when all checks pass.\n *\n * @param input - The raw recommendation criteria to validate\n * @returns A validation result indicating success or listing all issues\n *\n * @example\n * ```typescript\n * const result = validateRecommendationCriteria({\n * query: \"gitbook\",\n * mustHave: \"api\",\n * exclude: \"legacy\",\n * });\n * if (!result.valid) {\n * console.error(result.issues);\n * }\n * ```\n *\n * @public\n */\nexport function validateRecommendationCriteria(\n input: RecommendationCriteriaInput,\n): RecommendationValidationResult {\n const issues: RecommendationValidationIssue[] = [];\n\n if (input.query !== undefined && typeof input.query !== 'string') {\n issues.push({\n code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,\n field: 'query',\n message: 'query must be a string',\n });\n }\n\n if (\n input.mustHave !== undefined &&\n !(typeof input.mustHave === 'string' || Array.isArray(input.mustHave))\n ) {\n issues.push({\n code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,\n field: 'mustHave',\n message: 'mustHave must be a string or string[]',\n });\n }\n\n if (\n input.prefer !== undefined &&\n !(typeof input.prefer === 'string' || Array.isArray(input.prefer))\n ) {\n issues.push({\n code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,\n field: 'prefer',\n message: 'prefer must be a string or string[]',\n });\n }\n\n if (\n input.exclude !== undefined &&\n !(typeof input.exclude === 'string' || Array.isArray(input.exclude))\n ) {\n issues.push({\n code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,\n field: 'exclude',\n message: 'exclude must be a string or string[]',\n });\n }\n\n const mustHave = normalizeList(input.mustHave);\n const prefer = normalizeList(input.prefer);\n const exclude = normalizeList(input.exclude);\n const conflict =\n mustHave.some((term) => exclude.includes(term)) ||\n prefer.some((term) => exclude.includes(term));\n if (conflict) {\n issues.push({\n code: RECOMMENDATION_ERROR_CODES.CRITERIA_CONFLICT,\n field: 'exclude',\n message: 'criteria terms cannot appear in both prefer/must-have and exclude',\n });\n }\n\n if (issues.length === 0 && !hasAnyCriteriaInput(input)) {\n issues.push({\n code: RECOMMENDATION_ERROR_CODES.QUERY_INVALID,\n field: 'query',\n message: 'at least one criteria value is required',\n });\n }\n\n return {\n valid: issues.length === 0,\n issues,\n };\n}\n\n/**\n * Normalizes raw recommendation criteria into a consistent tokenized form.\n *\n * @remarks\n * Lowercases all values, tokenizes comma-separated strings, deduplicates\n * terms, and sorts lists alphabetically. The result is deterministic for\n * equivalent inputs, ensuring consistent scoring behavior.\n *\n * @param input - The raw recommendation criteria to normalize\n * @returns Normalized criteria with tokenized, sorted, deduplicated terms\n *\n * @example\n * ```typescript\n * const criteria = normalizeRecommendationCriteria({\n * query: \"GitBook API\",\n * mustHave: \"sync, api\",\n * prefer: [\"modern\"],\n * });\n * // criteria.queryTokens => [\"api\", \"gitbook\"]\n * // criteria.mustHave => [\"api\", \"sync\"]\n * ```\n *\n * @public\n */\nexport function normalizeRecommendationCriteria(\n input: RecommendationCriteriaInput,\n): NormalizedRecommendationCriteria {\n const query = (input.query ?? '').trim().toLowerCase();\n return {\n query,\n queryTokens: query\n ? Array.from(new Set(tokenizeCriteriaValue(query.replace(/\\s+/g, ',')))).sort((a, b) =>\n a.localeCompare(b),\n )\n : [],\n mustHave: normalizeList(input.mustHave),\n prefer: normalizeList(input.prefer),\n exclude: normalizeList(input.exclude),\n };\n}\n\nfunction countMatches(haystack: string, needles: string[]): number {\n let count = 0;\n for (const needle of needles) {\n if (haystack.includes(needle)) {\n count += 1;\n }\n }\n return count;\n}\n\nfunction clampScore(value: number): number {\n return Number(value.toFixed(6));\n}\n\nfunction buildSearchText(skill: MarketplaceResult): string {\n return `${skill.name} ${skill.scopedName} ${skill.description} ${skill.author}`.toLowerCase();\n}\n\n/**\n * Computes a recommendation score for a single skill against normalized criteria.\n *\n * @remarks\n * Evaluates the skill across multiple scoring dimensions including must-have\n * matches, preferred term matches, query token matches, star count signals,\n * metadata quality, modern/legacy markers, and exclusion penalties. The total\n * score is the weighted sum of all dimensions. When `includeDetails` is true,\n * a full breakdown by category is attached to the result.\n *\n * @param skill - The marketplace skill result to score\n * @param criteria - The normalized recommendation criteria to score against\n * @param options - Optional scoring configuration including weights and markers\n * @returns A ranked recommendation with score, reasons, and tradeoffs\n *\n * @example\n * ```typescript\n * const criteria = normalizeRecommendationCriteria({ query: \"gitbook\" });\n * const ranked = scoreSkillRecommendation(marketplaceSkill, criteria, {\n * includeDetails: true,\n * });\n * console.log(ranked.score, ranked.reasons);\n * ```\n *\n * @public\n */\nexport function scoreSkillRecommendation(\n skill: MarketplaceResult,\n criteria: NormalizedRecommendationCriteria,\n options: RecommendationOptions = {},\n): RankedSkillRecommendation {\n const weights = { ...DEFAULT_WEIGHTS, ...options.weights };\n const modernMarkers = (options.modernMarkers ?? DEFAULT_MODERN_MARKERS).map((marker) =>\n marker.toLowerCase(),\n );\n const legacyMarkers = (options.legacyMarkers ?? DEFAULT_LEGACY_MARKERS).map((marker) =>\n marker.toLowerCase(),\n );\n const text = buildSearchText(skill);\n const reasons: RecommendationReason[] = [];\n const tradeoffs: string[] = [];\n\n const mustHaveMatches = countMatches(text, criteria.mustHave);\n const missingMustHave = Math.max(criteria.mustHave.length - mustHaveMatches, 0);\n const preferMatches = countMatches(text, criteria.prefer);\n const queryMatches = countMatches(text, criteria.queryTokens);\n const excludeMatches = countMatches(text, criteria.exclude);\n const modernMatches = countMatches(text, modernMarkers);\n const legacyMatches = countMatches(text, legacyMarkers);\n const metadataSignal = skill.description.trim().length >= 80 ? 1 : 0;\n const starsSignal = Math.log10(skill.stars + 1);\n const sourceConfidence =\n skill.source === 'agentskills.in' ? 1 : skill.source === 'skills.sh' ? 0.8 : 0.6;\n\n const mustHaveScore =\n mustHaveMatches * weights.mustHaveMatch - missingMustHave * weights.missingMustHavePenalty;\n const preferScore = preferMatches * weights.preferMatch;\n const queryScore = queryMatches * weights.queryTokenMatch;\n const starsScore = starsSignal * weights.starsFactor;\n const metadataScore = (metadataSignal + sourceConfidence) * weights.metadataBoost;\n const modernityScore =\n modernMatches * weights.modernMarkerBoost - legacyMatches * weights.legacyMarkerPenalty;\n const exclusionPenalty = excludeMatches * weights.excludePenalty;\n\n const hasGitbookTopic = text.includes('gitbook');\n const hasGitSync = text.includes('git sync') || (text.includes('git') && text.includes('sync'));\n const hasApiWorkflow =\n text.includes('api') && (text.includes('workflow') || text.includes('sync'));\n const hasLegacyCli = text.includes('gitbook-cli') || text.includes('book.json');\n\n const topicScore =\n (hasGitbookTopic ? 3 : 0) +\n (hasGitSync ? 2 : 0) +\n (hasApiWorkflow ? 2 : 0) -\n (hasLegacyCli ? 4 : 0);\n\n const total = clampScore(\n mustHaveScore +\n preferScore +\n queryScore +\n starsScore +\n metadataScore +\n modernityScore +\n topicScore -\n exclusionPenalty,\n );\n\n if (hasGitbookTopic) reasons.push({ code: 'MATCH_TOPIC_GITBOOK' });\n if (hasGitSync) reasons.push({ code: 'HAS_GIT_SYNC' });\n if (hasApiWorkflow) reasons.push({ code: 'HAS_API_WORKFLOW' });\n if (hasLegacyCli) reasons.push({ code: 'PENALTY_LEGACY_CLI' });\n\n if (mustHaveMatches > 0)\n reasons.push({ code: 'MUST_HAVE_MATCH', detail: String(mustHaveMatches) });\n if (missingMustHave > 0)\n reasons.push({ code: 'MISSING_MUST_HAVE', detail: String(missingMustHave) });\n if (preferMatches > 0) reasons.push({ code: 'PREFER_MATCH', detail: String(preferMatches) });\n if (queryMatches > 0) reasons.push({ code: 'QUERY_MATCH', detail: String(queryMatches) });\n if (starsSignal > 0) reasons.push({ code: 'STAR_SIGNAL' });\n if (metadataSignal > 0) reasons.push({ code: 'METADATA_SIGNAL' });\n if (modernMatches > 0) reasons.push({ code: 'MODERN_MARKER', detail: String(modernMatches) });\n if (legacyMatches > 0) reasons.push({ code: 'LEGACY_MARKER', detail: String(legacyMatches) });\n if (excludeMatches > 0) reasons.push({ code: 'EXCLUDE_MATCH', detail: String(excludeMatches) });\n\n if (missingMustHave > 0) tradeoffs.push('Missing one or more required criteria terms.');\n if (excludeMatches > 0) tradeoffs.push('Matches one or more excluded terms.');\n if (skill.stars < 10) tradeoffs.push('Low quality signal from repository stars.');\n if (hasLegacyCli) tradeoffs.push('Contains legacy GitBook CLI markers.');\n\n const result: RankedSkillRecommendation = {\n skill,\n score: total,\n reasons,\n tradeoffs,\n excluded: excludeMatches > 0,\n };\n\n if (options.includeDetails) {\n result.breakdown = {\n mustHave: clampScore(mustHaveScore),\n prefer: clampScore(preferScore),\n query: clampScore(queryScore),\n stars: clampScore(starsScore),\n metadata: clampScore(metadataScore),\n modernity: clampScore(modernityScore),\n exclusionPenalty: clampScore(exclusionPenalty),\n total,\n };\n }\n\n return result;\n}\n\n/**\n * Validates, normalizes, scores, and ranks a list of skills against criteria.\n *\n * @remarks\n * This is the primary entry point for the recommendation engine. It validates\n * the input criteria, throws on invalid input, then scores each skill and\n * returns them sorted by descending score. Ties are broken by star count, then\n * alphabetically by scoped name. Results can be limited via `options.top`.\n *\n * @param skills - The array of marketplace skill results to rank\n * @param criteriaInput - The raw recommendation criteria from the user\n * @param options - Optional configuration for scoring and result limiting\n * @returns The normalized criteria and ranked skill recommendations\n * @throws Error with `code` and `issues` properties when criteria are invalid\n *\n * @example\n * ```typescript\n * const result = recommendSkills(\n * marketplaceResults,\n * { query: \"gitbook\", mustHave: \"api\", exclude: \"legacy\" },\n * { top: 5, includeDetails: true },\n * );\n * for (const rec of result.ranking) {\n * console.log(rec.skill.name, rec.score);\n * }\n * ```\n *\n * @public\n */\nexport function recommendSkills(\n skills: MarketplaceResult[],\n criteriaInput: RecommendationCriteriaInput,\n options: RecommendationOptions = {},\n): RecommendSkillsResult {\n const validation = validateRecommendationCriteria(criteriaInput);\n if (!validation.valid) {\n const first = validation.issues[0];\n const error = new Error(first?.message ?? 'Invalid recommendation criteria') as Error & {\n code?: RecommendationErrorCode;\n issues?: RecommendationValidationIssue[];\n };\n error.code = first?.code;\n error.issues = validation.issues;\n throw error;\n }\n\n const criteria = normalizeRecommendationCriteria(criteriaInput);\n const ranking = skills\n .map((skill) => scoreSkillRecommendation(skill, criteria, options))\n .sort((a, b) => {\n if (b.score !== a.score) return b.score - a.score;\n if (b.skill.stars !== a.skill.stars) return b.skill.stars - a.skill.stars;\n return a.skill.scopedName.localeCompare(b.skill.scopedName);\n });\n\n return {\n criteria,\n ranking: typeof options.top === 'number' ? ranking.slice(0, Math.max(0, options.top)) : ranking,\n };\n}\n\n/**\n * Alias for {@link recommendSkills} providing a shorter function name.\n *\n * @remarks\n * This is a convenience alias that exposes the same recommendation engine\n * under the name `rankSkills` for backward compatibility.\n *\n * @public\n */\nexport const rankSkills = recommendSkills;\n","import { MarketplaceClient } from '../marketplace/client.js';\nimport {\n RECOMMENDATION_ERROR_CODES,\n type RecommendationCriteriaInput,\n type RecommendationOptions,\n type RecommendSkillsResult,\n recommendSkills as rankSkills,\n} from './recommendation.js';\n\n/**\n * Options for searching skills via marketplace APIs.\n *\n * @public\n */\nexport interface SearchSkillsOptions {\n /** Maximum number of results to return. */\n limit?: number;\n}\n\n/**\n * Options for the recommendation query combining ranking options with a result limit.\n *\n * @public\n */\nexport interface RecommendSkillsQueryOptions extends RecommendationOptions {\n /** Maximum number of results to return. */\n limit?: number;\n}\n\n/**\n * Format skill recommendation results for display or serialization.\n *\n * @remarks\n * In `\"human\"` mode, produces a numbered list with reasons and tradeoffs.\n * In `\"json\"` mode, returns a structured object suitable for machine consumption.\n *\n * @param result - The recommendation result to format\n * @param opts - Formatting options including output mode and detail level\n * @returns Formatted string for human mode, or a structured object for JSON mode\n *\n * @example\n * ```typescript\n * const result = await recommendSkills(\"testing\", { taskType: \"test-writing\" });\n * const output = formatSkillRecommendations(result, { mode: \"human\" });\n * console.log(output);\n * ```\n *\n * @public\n */\nexport function formatSkillRecommendations(\n result: RecommendSkillsResult,\n opts: { mode: 'human' | 'json'; details?: boolean },\n): string | Record<string, unknown> {\n const top = result.ranking;\n\n if (opts.mode === 'human') {\n if (top.length === 0) return 'No recommendations found.';\n const lines: string[] = ['Recommended skills:', ''];\n for (const [index, entry] of top.entries()) {\n const marker = index === 0 ? ' (Recommended)' : '';\n lines.push(`${index + 1}) ${entry.skill.scopedName}${marker}`);\n lines.push(\n ` why: ${entry.reasons.map((reason) => reason.code).join(', ') || 'score-based match'}`,\n );\n lines.push(` tradeoff: ${entry.tradeoffs[0] ?? 'none'}`);\n }\n lines.push('');\n lines.push(`CHOOSE: ${top.map((_, index) => index + 1).join(',')}`);\n return lines.join('\\n');\n }\n\n const options = top.map((entry, index) => ({\n rank: index + 1,\n scopedName: entry.skill.scopedName,\n score: entry.score,\n reasons: entry.reasons,\n tradeoffs: entry.tradeoffs,\n ...(opts.details\n ? {\n description: entry.skill.description,\n source: entry.skill.source,\n evidence: entry.breakdown ?? null,\n }\n : {}),\n }));\n\n return {\n query: result.criteria.query,\n recommended: options[0] ?? null,\n options,\n };\n}\n\n/**\n * Search for skills via marketplace APIs.\n *\n * @remarks\n * Queries the unified marketplace client and returns matching skill entries.\n * Throws with a coded error if the query is empty or the marketplace is unavailable.\n *\n * @param query - Search query string (must be non-empty)\n * @param options - Search options including result limit\n * @returns Array of marketplace skill entries matching the query\n *\n * @example\n * ```typescript\n * const results = await searchSkills(\"test runner\", { limit: 10 });\n * console.log(`Found ${results.length} skills`);\n * ```\n *\n * @public\n */\nexport async function searchSkills(query: string, options: SearchSkillsOptions = {}) {\n const trimmed = query.trim();\n if (!trimmed) {\n const error = new Error('query must be non-empty') as Error & { code?: string };\n error.code = RECOMMENDATION_ERROR_CODES.QUERY_INVALID;\n throw error;\n }\n\n const client = new MarketplaceClient();\n try {\n return await client.search(trimmed, options.limit ?? 20);\n } catch (error) {\n const wrapped = new Error(error instanceof Error ? error.message : String(error)) as Error & {\n code?: string;\n };\n wrapped.code = RECOMMENDATION_ERROR_CODES.SOURCE_UNAVAILABLE;\n throw wrapped;\n }\n}\n\n/**\n * Search and rank skills based on query and recommendation criteria.\n *\n * @remarks\n * Combines marketplace search with the recommendation engine to produce\n * scored, ranked skill suggestions. Throws if no matches are found.\n *\n * @param query - Search query string\n * @param criteria - Recommendation criteria (task type, context, preferences)\n * @param options - Options for limiting and tuning results\n * @returns Ranked recommendation results with scores and reasons\n *\n * @example\n * ```typescript\n * const result = await recommendSkills(\"testing\", { taskType: \"test-writing\" });\n * const best = result.ranking[0];\n * console.log(`Top pick: ${best.skill.scopedName} (score: ${best.score})`);\n * ```\n *\n * @public\n */\nexport async function recommendSkills(\n query: string,\n criteria: Omit<RecommendationCriteriaInput, 'query'>,\n options: RecommendSkillsQueryOptions = {},\n): Promise<RecommendSkillsResult> {\n const hits = await searchSkills(query, {\n limit: options.limit ?? Math.max((options.top ?? 3) * 5, 20),\n });\n const ranked = rankSkills(hits, { ...criteria, query }, options);\n\n if (ranked.ranking.length === 0) {\n const error = new Error('no matches found') as Error & { code?: string };\n error.code = RECOMMENDATION_ERROR_CODES.NO_MATCHES;\n throw error;\n }\n\n return ranked;\n}\n","/**\n * Library loader - loads a SkillLibrary from a directory or module.\n *\n * Two strategies:\n * 1. loadLibraryFromModule() - for libraries with an index.js exporting SkillLibrary\n * 2. buildLibraryFromFiles() - for plain directories with the right file structure\n */\n\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { basename, dirname, join } from 'node:path';\nimport type {\n SkillLibrary,\n SkillLibraryDispatchMatrix,\n SkillLibraryEntry,\n SkillLibraryManifest,\n SkillLibraryProfile,\n SkillLibraryValidationIssue,\n SkillLibraryValidationResult,\n} from './skill-library.js';\n\nconst require = createRequire(import.meta.url);\n\n/**\n * Load a SkillLibrary from a module (index.js) at the given root directory.\n *\n * @remarks\n * Uses `createRequire()` for CJS modules or dynamic `import()` for ESM.\n * Validates that the loaded module implements the SkillLibrary interface\n * by checking for required properties and methods.\n *\n * @param root - Absolute path to the library root (must contain index.js or package.json with main)\n * @returns A validated SkillLibrary instance\n * @throws If the module cannot be loaded or does not implement SkillLibrary\n *\n * @example\n * ```typescript\n * const library = loadLibraryFromModule(\"/home/user/.agents/libraries/cleocode-skills\");\n * console.log(`Loaded v${library.version} with ${library.listSkills().length} skills`);\n * ```\n *\n * @public\n */\nexport function loadLibraryFromModule(root: string): SkillLibrary {\n let mod: Record<string, unknown>;\n\n try {\n mod = require(root);\n } catch {\n throw new Error(`Failed to load skill library module from ${root}`);\n }\n\n // Validate required properties\n const requiredMethods = [\n 'listSkills',\n 'getSkill',\n 'getSkillPath',\n 'getSkillDir',\n 'readSkillContent',\n 'getCoreSkills',\n 'getSkillsByCategory',\n 'getSkillDependencies',\n 'resolveDependencyTree',\n 'listProfiles',\n 'getProfile',\n 'resolveProfile',\n 'listSharedResources',\n 'getSharedResourcePath',\n 'readSharedResource',\n 'listProtocols',\n 'getProtocolPath',\n 'readProtocol',\n 'validateSkillFrontmatter',\n 'validateAll',\n 'getDispatchMatrix',\n ];\n\n for (const method of requiredMethods) {\n if (typeof mod[method] !== 'function') {\n throw new Error(`Skill library at ${root} does not implement required method: ${method}`);\n }\n }\n\n if (!mod.version || typeof mod.version !== 'string') {\n throw new Error(`Skill library at ${root} is missing 'version' property`);\n }\n\n if (!mod.libraryRoot || typeof mod.libraryRoot !== 'string') {\n throw new Error(`Skill library at ${root} is missing 'libraryRoot' property`);\n }\n\n // Safe cast: all required methods and properties validated above\n return mod as unknown as SkillLibrary;\n}\n\n/**\n * Build a SkillLibrary from raw files in a directory.\n *\n * @remarks\n * Constructs a full SkillLibrary implementation by reading:\n * - `skills.json` for catalog entries\n * - `skills/manifest.json` for dispatch matrix\n * - `profiles/*.json` for profile definitions\n * - `skills/<name>/SKILL.md` for skill content\n * - `skills/_shared/*.md` for shared resources\n * - `protocols/*.md` or `skills/protocols/*.md` for protocol files\n *\n * @param root - Absolute path to the library root directory\n * @returns A SkillLibrary instance backed by filesystem reads\n * @throws If skills.json is not found at the root\n *\n * @example\n * ```typescript\n * const library = buildLibraryFromFiles(\"/home/user/.agents/libraries/cleocode-skills\");\n * const coreSkills = library.getCoreSkills();\n * console.log(`Core skills: ${coreSkills.map(s => s.name).join(\", \")}`);\n * ```\n *\n * @public\n */\nexport function buildLibraryFromFiles(root: string): SkillLibrary {\n const catalogPath = join(root, 'skills.json');\n if (!existsSync(catalogPath)) {\n throw new Error(`No skills.json found at ${root}`);\n }\n\n const catalogData = JSON.parse(readFileSync(catalogPath, 'utf-8'));\n const entries: SkillLibraryEntry[] = catalogData.skills ?? [];\n const version: string = catalogData.version ?? '0.0.0';\n\n // Load manifest\n const manifestPath = join(root, 'skills', 'manifest.json');\n let manifest: SkillLibraryManifest;\n if (existsSync(manifestPath)) {\n manifest = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n } else {\n manifest = {\n $schema: '',\n _meta: {},\n dispatch_matrix: { by_task_type: {}, by_keyword: {}, by_protocol: {} },\n skills: [],\n };\n }\n\n // Load profiles\n const profilesDir = join(root, 'profiles');\n const profiles = new Map<string, SkillLibraryProfile>();\n if (existsSync(profilesDir)) {\n for (const file of readdirSync(profilesDir)) {\n if (!file.endsWith('.json')) continue;\n try {\n const profile: SkillLibraryProfile = JSON.parse(\n readFileSync(join(profilesDir, file), 'utf-8'),\n );\n profiles.set(profile.name, profile);\n } catch {\n // Skip invalid profiles\n }\n }\n }\n\n // Build skill lookup map\n const skillMap = new Map<string, SkillLibraryEntry>();\n for (const entry of entries) {\n skillMap.set(entry.name, entry);\n }\n\n // ── Helper functions ──────────────────────────────────────────────\n\n function getSkillDir(name: string): string {\n const entry = skillMap.get(name);\n if (entry) {\n return dirname(join(root, entry.path));\n }\n return join(root, 'skills', name);\n }\n\n function resolveDeps(names: string[], visited = new Set<string>()): string[] {\n const result: string[] = [];\n for (const name of names) {\n if (visited.has(name)) continue;\n visited.add(name);\n\n const entry = skillMap.get(name);\n if (entry && entry.dependencies.length > 0) {\n result.push(...resolveDeps(entry.dependencies, visited));\n }\n result.push(name);\n }\n return result;\n }\n\n function resolveProfileByName(name: string, visited = new Set<string>()): string[] {\n if (visited.has(name)) return [];\n visited.add(name);\n\n const profile = profiles.get(name);\n if (!profile) return [];\n\n let skills: string[] = [];\n if (profile.extends) {\n skills = resolveProfileByName(profile.extends, visited);\n }\n skills.push(...profile.skills);\n\n // Resolve dependencies for all skills\n return resolveDeps([...new Set(skills)]);\n }\n\n function discoverFiles(dir: string, ext: string): string[] {\n if (!existsSync(dir)) return [];\n return readdirSync(dir)\n .filter((f) => f.endsWith(ext))\n .map((f) => basename(f, ext));\n }\n\n // ── Build the library object ──────────────────────────────────────\n\n const library: SkillLibrary = {\n version,\n libraryRoot: root,\n skills: entries,\n manifest,\n\n listSkills(): string[] {\n return entries.map((e) => e.name);\n },\n\n getSkill(name: string): SkillLibraryEntry | undefined {\n return skillMap.get(name);\n },\n\n getSkillPath(name: string): string {\n const entry = skillMap.get(name);\n if (entry) {\n return join(root, entry.path);\n }\n return join(root, 'skills', name, 'SKILL.md');\n },\n\n getSkillDir,\n\n readSkillContent(name: string): string {\n const skillPath = library.getSkillPath(name);\n if (!existsSync(skillPath)) {\n throw new Error(`Skill content not found: ${skillPath}`);\n }\n return readFileSync(skillPath, 'utf-8');\n },\n\n getCoreSkills(): SkillLibraryEntry[] {\n return entries.filter((e) => e.core);\n },\n\n getSkillsByCategory(category: SkillLibraryEntry['category']): SkillLibraryEntry[] {\n return entries.filter((e) => e.category === category);\n },\n\n getSkillDependencies(name: string): string[] {\n return skillMap.get(name)?.dependencies ?? [];\n },\n\n resolveDependencyTree(names: string[]): string[] {\n return resolveDeps(names);\n },\n\n listProfiles(): string[] {\n return [...profiles.keys()];\n },\n\n getProfile(name: string): SkillLibraryProfile | undefined {\n return profiles.get(name);\n },\n\n resolveProfile(name: string): string[] {\n return resolveProfileByName(name);\n },\n\n listSharedResources(): string[] {\n return discoverFiles(join(root, 'skills', '_shared'), '.md');\n },\n\n getSharedResourcePath(name: string): string | undefined {\n const resourcePath = join(root, 'skills', '_shared', `${name}.md`);\n return existsSync(resourcePath) ? resourcePath : undefined;\n },\n\n readSharedResource(name: string): string | undefined {\n const resourcePath = library.getSharedResourcePath(name);\n if (!resourcePath) return undefined;\n return readFileSync(resourcePath, 'utf-8');\n },\n\n listProtocols(): string[] {\n // Check root protocols/ first (root-level layout), fall back to skills/protocols/\n const rootProtocols = discoverFiles(join(root, 'protocols'), '.md');\n if (rootProtocols.length > 0) return rootProtocols;\n return discoverFiles(join(root, 'skills', 'protocols'), '.md');\n },\n\n getProtocolPath(name: string): string | undefined {\n // Check root protocols/ first, fall back to skills/protocols/\n const rootPath = join(root, 'protocols', `${name}.md`);\n if (existsSync(rootPath)) return rootPath;\n const skillsPath = join(root, 'skills', 'protocols', `${name}.md`);\n return existsSync(skillsPath) ? skillsPath : undefined;\n },\n\n readProtocol(name: string): string | undefined {\n const protocolPath = library.getProtocolPath(name);\n if (!protocolPath) return undefined;\n return readFileSync(protocolPath, 'utf-8');\n },\n\n validateSkillFrontmatter(name: string): SkillLibraryValidationResult {\n const entry = skillMap.get(name);\n if (!entry) {\n return {\n valid: false,\n issues: [{ level: 'error', field: 'name', message: `Skill not found: ${name}` }],\n };\n }\n\n const issues: SkillLibraryValidationIssue[] = [];\n\n if (!entry.name) {\n issues.push({ level: 'error', field: 'name', message: 'Missing name' });\n }\n if (!entry.description) {\n issues.push({ level: 'error', field: 'description', message: 'Missing description' });\n }\n if (!entry.version) {\n issues.push({ level: 'warn', field: 'version', message: 'Missing version' });\n }\n\n // Check SKILL.md exists\n const skillPath = join(root, entry.path);\n if (!existsSync(skillPath)) {\n issues.push({\n level: 'error',\n field: 'path',\n message: `SKILL.md not found at ${entry.path}`,\n });\n }\n\n return {\n valid: !issues.some((i) => i.level === 'error'),\n issues,\n };\n },\n\n validateAll(): Map<string, SkillLibraryValidationResult> {\n const results = new Map<string, SkillLibraryValidationResult>();\n for (const entry of entries) {\n results.set(entry.name, library.validateSkillFrontmatter(entry.name));\n }\n return results;\n },\n\n getDispatchMatrix(): SkillLibraryDispatchMatrix {\n return manifest.dispatch_matrix;\n },\n };\n\n return library;\n}\n","/**\n * Skill catalog - registry pattern for pluggable skill libraries.\n *\n * Projects MUST register their skill library via registerSkillLibrary() or\n * registerSkillLibraryFromPath(). CAAMP no longer auto-discovers from\n * ~/.agents/skill-library/ - explicit registration is required.\n *\n * All public functions delegate to the registered SkillLibrary instance.\n */\n\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { buildLibraryFromFiles, loadLibraryFromModule } from './library-loader.js';\nimport type {\n SkillLibrary,\n SkillLibraryDispatchMatrix,\n SkillLibraryEntry,\n SkillLibraryManifest,\n SkillLibraryProfile,\n SkillLibraryValidationResult,\n} from './skill-library.js';\n\n// ── Registry ────────────────────────────────────────────────────────\n\nlet _library: SkillLibrary | null = null;\n\n/**\n * Registers a SkillLibrary instance directly as the active catalog.\n *\n * @remarks\n * Replaces any previously registered library. This is the programmatic\n * registration path for when you already have a constructed SkillLibrary\n * instance. For path-based registration, use {@link registerSkillLibraryFromPath}.\n *\n * @param library - A SkillLibrary implementation to use as the catalog\n *\n * @example\n * ```typescript\n * const library = buildLibraryFromFiles(\"/path/to/skills\");\n * registerSkillLibrary(library);\n * ```\n *\n * @public\n */\nexport function registerSkillLibrary(library: SkillLibrary): void {\n _library = library;\n}\n\n/**\n * Registers a skill library by loading it from a directory path.\n *\n * @remarks\n * Tries two strategies in order: first attempts module-based loading if\n * the directory contains an `index.js`, then falls back to file-based\n * loading from raw files like `skills.json`. Replaces any previously\n * registered library on success.\n *\n * @param root - Absolute path to the skill library root directory\n * @throws Error if the library cannot be loaded from the given path\n *\n * @example\n * ```typescript\n * registerSkillLibraryFromPath(\"/home/user/.agents/skill-library\");\n * const skills = listSkills();\n * ```\n *\n * @public\n */\nexport function registerSkillLibraryFromPath(root: string): void {\n // Try module-based loading first (has index.js)\n const indexPath = join(root, 'index.js');\n if (existsSync(indexPath)) {\n _library = loadLibraryFromModule(root);\n return;\n }\n\n // Fall back to file-based loading (has skills.json)\n _library = buildLibraryFromFiles(root);\n}\n\n/**\n * Clears the registered skill library instance.\n *\n * @remarks\n * Resets the internal library reference to null. Primarily intended for\n * test isolation to ensure a clean state between test cases.\n *\n * @example\n * ```typescript\n * clearRegisteredLibrary();\n * // isCatalogAvailable() will now return false unless auto-discovery succeeds\n * ```\n *\n * @public\n */\nexport function clearRegisteredLibrary(): void {\n _library = null;\n}\n\n// ── Auto-discovery ──────────────────────────────────────────────────\n\n/**\n * Attempt to discover a skill library from well-known locations.\n *\n * Discovery order:\n * 1. CAAMP_SKILL_LIBRARY env var (path to library root)\n */\nfunction discoverLibrary(): SkillLibrary | null {\n // 1. Environment variable\n const envPath = process.env['CAAMP_SKILL_LIBRARY'];\n if (envPath && existsSync(envPath)) {\n try {\n const indexPath = join(envPath, 'index.js');\n if (existsSync(indexPath)) {\n return loadLibraryFromModule(envPath);\n }\n if (existsSync(join(envPath, 'skills.json'))) {\n return buildLibraryFromFiles(envPath);\n }\n } catch {\n // Fall through\n }\n }\n\n return null;\n}\n\n// ── Internal accessor ───────────────────────────────────────────────\n\nfunction getLibrary(): SkillLibrary {\n if (!_library) {\n const discovered = discoverLibrary();\n if (discovered) {\n _library = discovered;\n }\n }\n\n if (!_library) {\n throw new Error(\n 'No skill library registered. Register one with registerSkillLibraryFromPath() ' +\n 'or set the CAAMP_SKILL_LIBRARY environment variable.',\n );\n }\n\n return _library;\n}\n\n// ── Public API (delegates to registered library) ────────────────────\n\n/**\n * Checks whether a skill library is available for use.\n *\n * @remarks\n * Returns true if a library has been explicitly registered or can be\n * auto-discovered via the `CAAMP_SKILL_LIBRARY` environment variable.\n * Does not throw on failure; catches any errors from discovery and\n * returns false instead.\n *\n * @returns True if a skill library is registered or discoverable, false otherwise\n *\n * @example\n * ```typescript\n * if (isCatalogAvailable()) {\n * const skills = listSkills();\n * }\n * ```\n *\n * @public\n */\nexport function isCatalogAvailable(): boolean {\n try {\n getLibrary();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Returns all skill entries from the catalog.\n *\n * @remarks\n * Delegates to the registered skill library's `skills` property.\n * Throws if no library is registered or discoverable.\n *\n * @returns An array of all skill library entries\n *\n * @example\n * ```typescript\n * const allSkills = getSkills();\n * console.log(`Found ${allSkills.length} skills`);\n * ```\n *\n * @public\n */\nexport function getSkills(): SkillLibraryEntry[] {\n return getLibrary().skills;\n}\n\n/**\n * Returns the parsed skill library manifest.\n *\n * @remarks\n * The manifest contains library metadata, version info, and the dispatch\n * matrix. Delegates to the registered library instance.\n *\n * @returns The skill library manifest object\n *\n * @example\n * ```typescript\n * const manifest = getManifest();\n * console.log(manifest.version);\n * ```\n *\n * @public\n */\nexport function getManifest(): SkillLibraryManifest {\n return getLibrary().manifest;\n}\n\n/**\n * Lists all available skill names in the catalog.\n *\n * @remarks\n * Returns the names of all registered skills. Useful for discovery\n * and enumeration without loading full skill metadata.\n *\n * @returns An array of skill name strings\n *\n * @example\n * ```typescript\n * const names = listSkills();\n * // e.g., [\"ct-orchestrator\", \"ct-dev-workflow\", \"ct-validator\"]\n * ```\n *\n * @public\n */\nexport function listSkills(): string[] {\n return getLibrary().listSkills();\n}\n\n/**\n * Gets skill metadata by name from the catalog.\n *\n * @remarks\n * Looks up a skill by its unique name in the registered library.\n * Returns undefined if no skill with the given name exists.\n *\n * @param name - The unique skill name to look up\n * @returns The skill entry if found, or undefined\n *\n * @example\n * ```typescript\n * const skill = getSkill(\"ct-orchestrator\");\n * if (skill) {\n * console.log(skill.category);\n * }\n * ```\n *\n * @public\n */\nexport function getSkill(name: string): SkillLibraryEntry | undefined {\n return getLibrary().getSkill(name);\n}\n\n/**\n * Resolves the absolute path to a skill's SKILL.md file.\n *\n * @remarks\n * Combines the library root with the skill's relative path to produce\n * the absolute filesystem path to the SKILL.md file.\n *\n * @param name - The unique skill name to resolve\n * @returns The absolute path to the skill's SKILL.md file\n *\n * @example\n * ```typescript\n * const path = getSkillPath(\"ct-orchestrator\");\n * // e.g., \"/home/user/.agents/skill-library/skills/ct-orchestrator/SKILL.md\"\n * ```\n *\n * @public\n */\nexport function getSkillPath(name: string): string {\n return getLibrary().getSkillPath(name);\n}\n\n/**\n * Resolves the absolute path to a skill's directory.\n *\n * @remarks\n * Returns the parent directory of the skill's SKILL.md file,\n * which may contain additional resources referenced by the skill.\n *\n * @param name - The unique skill name to resolve\n * @returns The absolute path to the skill's directory\n *\n * @example\n * ```typescript\n * const dir = getSkillDir(\"ct-orchestrator\");\n * // e.g., \"/home/user/.agents/skill-library/skills/ct-orchestrator\"\n * ```\n *\n * @public\n */\nexport function getSkillDir(name: string): string {\n return getLibrary().getSkillDir(name);\n}\n\n/**\n * Reads a skill's SKILL.md content as a string.\n *\n * @remarks\n * Reads the full content of the skill's SKILL.md file from disk.\n * Throws if the skill does not exist or the file cannot be read.\n *\n * @param name - The unique skill name to read\n * @returns The full text content of the skill's SKILL.md file\n *\n * @example\n * ```typescript\n * const content = readSkillContent(\"ct-orchestrator\");\n * console.log(content.substring(0, 100));\n * ```\n *\n * @public\n */\nexport function readSkillContent(name: string): string {\n return getLibrary().readSkillContent(name);\n}\n\n/**\n * Returns all skills marked as core in the catalog.\n *\n * @remarks\n * Filters the skill list to only include entries where `core` is true.\n * Core skills are foundational capabilities that most agents need.\n *\n * @returns An array of core skill entries\n *\n * @example\n * ```typescript\n * const coreSkills = getCoreSkills();\n * console.log(`${coreSkills.length} core skills available`);\n * ```\n *\n * @public\n */\nexport function getCoreSkills(): SkillLibraryEntry[] {\n return getLibrary().getCoreSkills();\n}\n\n/**\n * Returns skills filtered by category.\n *\n * @remarks\n * Filters the skill list to only include entries matching the specified\n * category. Categories organize skills by their functional purpose.\n *\n * @param category - The category to filter by\n * @returns An array of skill entries in the specified category\n *\n * @example\n * ```typescript\n * const planningSkills = getSkillsByCategory(\"planning\");\n * ```\n *\n * @public\n */\nexport function getSkillsByCategory(category: SkillLibraryEntry['category']): SkillLibraryEntry[] {\n return getLibrary().getSkillsByCategory(category);\n}\n\n/**\n * Gets the direct dependency names for a skill.\n *\n * @remarks\n * Returns only the immediate dependencies, not transitive ones.\n * Use {@link resolveDependencyTree} for the full transitive closure.\n *\n * @param name - The unique skill name to query dependencies for\n * @returns An array of direct dependency skill names\n *\n * @example\n * ```typescript\n * const deps = getSkillDependencies(\"ct-task-executor\");\n * // e.g., [\"ct-orchestrator\"]\n * ```\n *\n * @public\n */\nexport function getSkillDependencies(name: string): string[] {\n return getLibrary().getSkillDependencies(name);\n}\n\n/**\n * Resolves the full dependency tree for a set of skill names.\n *\n * @remarks\n * Performs transitive dependency resolution, returning all skills that\n * must be installed for the given set of skills to function correctly.\n * Handles circular dependencies gracefully.\n *\n * @param names - The skill names to resolve dependencies for\n * @returns A deduplicated array of all required skill names including transitive dependencies\n *\n * @example\n * ```typescript\n * const allDeps = resolveDependencyTree([\"ct-task-executor\", \"ct-validator\"]);\n * // includes all transitive dependencies\n * ```\n *\n * @public\n */\nexport function resolveDependencyTree(names: string[]): string[] {\n return getLibrary().resolveDependencyTree(names);\n}\n\n/**\n * Lists all available profile names in the catalog.\n *\n * @remarks\n * Profiles are named collections of skills that can be installed together.\n * Returns just the profile names; use {@link getProfile} for full details.\n *\n * @returns An array of profile name strings\n *\n * @example\n * ```typescript\n * const profiles = listProfiles();\n * // e.g., [\"default\", \"minimal\", \"full\"]\n * ```\n *\n * @public\n */\nexport function listProfiles(): string[] {\n return getLibrary().listProfiles();\n}\n\n/**\n * Gets a profile definition by name from the catalog.\n *\n * @remarks\n * Returns the full profile definition including its skill list and\n * any `extends` references. Returns undefined if no profile with\n * the given name exists.\n *\n * @param name - The unique profile name to look up\n * @returns The profile definition if found, or undefined\n *\n * @example\n * ```typescript\n * const profile = getProfile(\"default\");\n * if (profile) {\n * console.log(profile.skills);\n * }\n * ```\n *\n * @public\n */\nexport function getProfile(name: string): SkillLibraryProfile | undefined {\n return getLibrary().getProfile(name);\n}\n\n/**\n * Resolves a profile to its full skill list including inherited skills.\n *\n * @remarks\n * Follows `extends` chains and resolves all transitive dependencies,\n * returning the complete list of skills needed for the profile.\n *\n * @param name - The profile name to resolve\n * @returns A deduplicated array of all skill names required by the profile\n *\n * @example\n * ```typescript\n * const skills = resolveProfile(\"default\");\n * // includes all skills from extended profiles and their dependencies\n * ```\n *\n * @public\n */\nexport function resolveProfile(name: string): string[] {\n return getLibrary().resolveProfile(name);\n}\n\n/**\n * Lists all available shared resource names in the catalog.\n *\n * @remarks\n * Shared resources are files in the `_shared` directory that multiple\n * skills can reference. Returns just the resource names.\n *\n * @returns An array of shared resource name strings\n *\n * @example\n * ```typescript\n * const resources = listSharedResources();\n * // e.g., [\"testing-framework-config.md\", \"error-handling.md\"]\n * ```\n *\n * @public\n */\nexport function listSharedResources(): string[] {\n return getLibrary().listSharedResources();\n}\n\n/**\n * Gets the absolute path to a shared resource file.\n *\n * @remarks\n * Resolves the filesystem path for a shared resource by name.\n * Returns undefined if the resource does not exist.\n *\n * @param name - The shared resource name to resolve\n * @returns The absolute path to the resource file, or undefined if not found\n *\n * @example\n * ```typescript\n * const path = getSharedResourcePath(\"testing-framework-config.md\");\n * ```\n *\n * @public\n */\nexport function getSharedResourcePath(name: string): string | undefined {\n return getLibrary().getSharedResourcePath(name);\n}\n\n/**\n * Reads a shared resource file's content as a string.\n *\n * @remarks\n * Reads the full content of a shared resource file from disk.\n * Returns undefined if the resource does not exist.\n *\n * @param name - The shared resource name to read\n * @returns The text content of the resource, or undefined if not found\n *\n * @example\n * ```typescript\n * const content = readSharedResource(\"testing-framework-config.md\");\n * if (content) {\n * console.log(content);\n * }\n * ```\n *\n * @public\n */\nexport function readSharedResource(name: string): string | undefined {\n return getLibrary().readSharedResource(name);\n}\n\n/**\n * Lists all available protocol names in the catalog.\n *\n * @remarks\n * Protocols define standardized interaction patterns for agent workflows.\n * Returns just the protocol names; use {@link readProtocol} for content.\n *\n * @returns An array of protocol name strings\n *\n * @example\n * ```typescript\n * const protocols = listProtocols();\n * // e.g., [\"research\", \"implementation\", \"contribution\"]\n * ```\n *\n * @public\n */\nexport function listProtocols(): string[] {\n return getLibrary().listProtocols();\n}\n\n/**\n * Gets the absolute path to a protocol file.\n *\n * @remarks\n * Resolves the filesystem path for a protocol by name.\n * Returns undefined if the protocol does not exist.\n *\n * @param name - The protocol name to resolve\n * @returns The absolute path to the protocol file, or undefined if not found\n *\n * @example\n * ```typescript\n * const path = getProtocolPath(\"research\");\n * ```\n *\n * @public\n */\nexport function getProtocolPath(name: string): string | undefined {\n return getLibrary().getProtocolPath(name);\n}\n\n/**\n * Reads a protocol file's content as a string.\n *\n * @remarks\n * Reads the full content of a protocol file from disk.\n * Returns undefined if the protocol does not exist.\n *\n * @param name - The protocol name to read\n * @returns The text content of the protocol, or undefined if not found\n *\n * @example\n * ```typescript\n * const content = readProtocol(\"research\");\n * if (content) {\n * console.log(content);\n * }\n * ```\n *\n * @public\n */\nexport function readProtocol(name: string): string | undefined {\n return getLibrary().readProtocol(name);\n}\n\n/**\n * Validates a single skill's frontmatter against the schema.\n *\n * @remarks\n * Checks that the skill's SKILL.md file has valid YAML frontmatter\n * with all required fields. Returns a validation result with any errors.\n *\n * @param name - The skill name to validate\n * @returns A validation result indicating success or listing errors\n *\n * @example\n * ```typescript\n * const result = validateSkillFrontmatter(\"ct-orchestrator\");\n * if (!result.valid) {\n * console.error(result.errors);\n * }\n * ```\n *\n * @public\n */\nexport function validateSkillFrontmatter(name: string): SkillLibraryValidationResult {\n return getLibrary().validateSkillFrontmatter(name);\n}\n\n/**\n * Validates all skills in the catalog and returns results per skill.\n *\n * @remarks\n * Runs frontmatter validation on every skill in the library.\n * Returns a map keyed by skill name with validation results for each.\n *\n * @returns A map of skill names to their validation results\n *\n * @example\n * ```typescript\n * const results = validateAll();\n * for (const [name, result] of results) {\n * if (!result.valid) console.error(`${name}: invalid`);\n * }\n * ```\n *\n * @public\n */\nexport function validateAll(): Map<string, SkillLibraryValidationResult> {\n return getLibrary().validateAll();\n}\n\n/**\n * Gets the dispatch matrix from the skill library manifest.\n *\n * @remarks\n * The dispatch matrix maps protocol types to their corresponding skills,\n * enabling the orchestrator to route tasks to the correct executor.\n *\n * @returns The dispatch matrix object from the manifest\n *\n * @example\n * ```typescript\n * const matrix = getDispatchMatrix();\n * console.log(matrix);\n * ```\n *\n * @public\n */\nexport function getDispatchMatrix(): SkillLibraryDispatchMatrix {\n return getLibrary().getDispatchMatrix();\n}\n\n/**\n * Returns the skill library version string.\n *\n * @remarks\n * The version follows semver and corresponds to the version declared\n * in the library's manifest file.\n *\n * @returns The library version string\n *\n * @example\n * ```typescript\n * const version = getVersion();\n * // e.g., \"1.0.0\"\n * ```\n *\n * @public\n */\nexport function getVersion(): string {\n return getLibrary().version;\n}\n\n/**\n * Returns the absolute path to the skill library root directory.\n *\n * @remarks\n * This is the base directory from which all skill paths are resolved.\n * Set during library registration.\n *\n * @returns The absolute path to the library root\n *\n * @example\n * ```typescript\n * const root = getLibraryRoot();\n * // e.g., \"/home/user/.agents/skill-library\"\n * ```\n *\n * @public\n */\nexport function getLibraryRoot(): string {\n return getLibrary().libraryRoot;\n}\n","/**\n * Local skill discovery\n *\n * Scans directories for SKILL.md files and parses their frontmatter.\n */\n\nimport { existsSync } from 'node:fs';\nimport { readdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport matter from 'gray-matter';\nimport type { SkillEntry, SkillMetadata } from '../../types.js';\n\n/**\n * Parse a SKILL.md file and extract its frontmatter metadata.\n *\n * @remarks\n * Reads the file, parses YAML frontmatter via `gray-matter`, and maps the\n * fields to a {@link SkillMetadata} object. Returns `null` if the file cannot\n * be read or lacks required `name` and `description` fields.\n *\n * @param filePath - Absolute path to the SKILL.md file\n * @returns Parsed metadata, or `null` if invalid\n *\n * @example\n * ```typescript\n * const meta = await parseSkillFile(\"/path/to/SKILL.md\");\n * if (meta) {\n * console.log(`${meta.name}: ${meta.description}`);\n * }\n * ```\n *\n * @public\n */\nexport async function parseSkillFile(filePath: string): Promise<SkillMetadata | null> {\n try {\n const content = await readFile(filePath, 'utf-8');\n const { data } = matter(content);\n\n if (!data.name || !data.description) {\n return null;\n }\n\n const allowedTools = data['allowed-tools'] ?? data.allowedTools;\n\n return {\n name: String(data.name),\n description: String(data.description),\n license: data.license ? String(data.license) : undefined,\n compatibility: data.compatibility ? String(data.compatibility) : undefined,\n metadata: data.metadata as Record<string, string> | undefined,\n allowedTools:\n typeof allowedTools === 'string'\n ? allowedTools.split(/\\s+/)\n : Array.isArray(allowedTools)\n ? allowedTools.map(String)\n : undefined,\n version: data.version ? String(data.version) : undefined,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Discover a single skill at a given directory path.\n *\n * @remarks\n * Checks for a `SKILL.md` file in the directory and parses its metadata.\n *\n * @param skillDir - Absolute path to a skill directory (containing SKILL.md)\n * @returns Skill entry with metadata, or `null` if no valid SKILL.md exists\n *\n * @example\n * ```typescript\n * import { getCanonicalSkillsDir } from \"../paths/standard.js\";\n * import { join } from \"node:path\";\n *\n * const skill = await discoverSkill(join(getCanonicalSkillsDir(), \"my-skill\"));\n * if (skill) {\n * console.log(`Found: ${skill.name}`);\n * }\n * ```\n *\n * @public\n */\nexport async function discoverSkill(skillDir: string): Promise<SkillEntry | null> {\n const skillFile = join(skillDir, 'SKILL.md');\n if (!existsSync(skillFile)) return null;\n\n const metadata = await parseSkillFile(skillFile);\n if (!metadata) return null;\n\n return {\n name: metadata.name,\n scopedName: metadata.name,\n path: skillDir,\n metadata,\n };\n}\n\n/**\n * Scan a directory for skill subdirectories, each containing a SKILL.md file.\n *\n * @remarks\n * Iterates over directories and symlinks in `rootDir` and calls\n * {@link discoverSkill} on each.\n *\n * @param rootDir - Absolute path to a skills root directory to scan\n * @returns Array of discovered skill entries\n *\n * @example\n * ```typescript\n * import { getCanonicalSkillsDir } from \"../paths/standard.js\";\n *\n * const skills = await discoverSkills(getCanonicalSkillsDir());\n * console.log(`Found ${skills.length} skills`);\n * ```\n *\n * @public\n */\nexport async function discoverSkills(rootDir: string): Promise<SkillEntry[]> {\n if (!existsSync(rootDir)) return [];\n\n const entries = await readdir(rootDir, { withFileTypes: true });\n const skills: SkillEntry[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;\n\n const skillDir = join(rootDir, entry.name);\n const skill = await discoverSkill(skillDir);\n if (skill) {\n skills.push(skill);\n }\n }\n\n return skills;\n}\n\n/**\n * Discover skills across multiple directories.\n *\n * @remarks\n * Scans each directory with {@link discoverSkills} and deduplicates results by\n * skill name, keeping the first occurrence found.\n *\n * @param dirs - Array of absolute paths to skills directories to scan\n * @returns Deduplicated array of discovered skill entries\n *\n * @example\n * ```typescript\n * const skills = await discoverSkillsMulti([\"/home/user/.agents/skills\", \"./project-skills\"]);\n * console.log(`Found ${skills.length} unique skills`);\n * ```\n *\n * @public\n */\nexport async function discoverSkillsMulti(dirs: string[]): Promise<SkillEntry[]> {\n const all: SkillEntry[] = [];\n const seen = new Set<string>();\n\n for (const dir of dirs) {\n const skills = await discoverSkills(dir);\n for (const skill of skills) {\n if (!seen.has(skill.name)) {\n seen.add(skill.name);\n all.push(skill);\n }\n }\n }\n\n return all;\n}\n","/**\n * SKILL.md validator\n *\n * Validates skill files against the Agent Skills standard.\n */\n\nimport { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport matter from 'gray-matter';\n\n/**\n * A single validation issue found during SKILL.md validation.\n *\n * @example\n * ```typescript\n * const issue: ValidationIssue = {\n * level: \"error\",\n * field: \"name\",\n * message: \"Missing required field: name\",\n * };\n * ```\n *\n * @public\n */\nexport interface ValidationIssue {\n /** Severity: `\"error\"` causes validation failure, `\"warning\"` does not. */\n level: 'error' | 'warning';\n /** The field or section that triggered the issue. */\n field: string;\n /** Human-readable description of the issue. */\n message: string;\n}\n\n/**\n * Result of validating a SKILL.md file against the Agent Skills standard.\n *\n * @example\n * ```typescript\n * const result = await validateSkill(\"/path/to/SKILL.md\");\n * if (!result.valid) {\n * for (const issue of result.issues) {\n * console.log(`[${issue.level}] ${issue.field}: ${issue.message}`);\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface ValidationResult {\n /** Whether the skill passed validation (no error-level issues). */\n valid: boolean;\n /** All issues found during validation. */\n issues: ValidationIssue[];\n /** Parsed frontmatter metadata, or `null` if parsing failed. */\n metadata: Record<string, unknown> | null;\n}\n\nconst RESERVED_NAMES = [\n 'anthropic',\n 'claude',\n 'google',\n 'openai',\n 'microsoft',\n 'cursor',\n 'windsurf',\n 'codex',\n 'gemini',\n 'copilot',\n];\n\nconst NAME_PATTERN = /^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/;\nconst MAX_NAME_LENGTH = 64;\nconst MAX_DESCRIPTION_LENGTH = 1024;\nconst WARN_BODY_LINES = 500;\nconst WARN_DESCRIPTION_LENGTH = 50;\n\n/**\n * Validate a SKILL.md file against the Agent Skills standard.\n *\n * @remarks\n * Checks for required frontmatter fields (`name`, `description`), validates\n * naming conventions, enforces length limits, checks for reserved names,\n * and warns about long skill bodies.\n *\n * @param filePath - Absolute path to the SKILL.md file to validate\n * @returns Validation result with issues and parsed metadata\n *\n * @example\n * ```typescript\n * const result = await validateSkill(\"/path/to/SKILL.md\");\n * console.log(result.valid ? \"Valid\" : `${result.issues.length} issues found`);\n * ```\n *\n * @public\n */\nexport async function validateSkill(filePath: string): Promise<ValidationResult> {\n const issues: ValidationIssue[] = [];\n\n if (!existsSync(filePath)) {\n return {\n valid: false,\n issues: [{ level: 'error', field: 'file', message: 'File does not exist' }],\n metadata: null,\n };\n }\n\n const content = await readFile(filePath, 'utf-8');\n\n // Check for frontmatter\n if (!content.startsWith('---')) {\n issues.push({\n level: 'error',\n field: 'frontmatter',\n message: 'Missing YAML frontmatter (file must start with ---)',\n });\n return { valid: false, issues, metadata: null };\n }\n\n let data: Record<string, unknown>;\n let body: string;\n\n try {\n const parsed = matter(content);\n data = parsed.data as Record<string, unknown>;\n body = parsed.content;\n } catch (err) {\n issues.push({\n level: 'error',\n field: 'frontmatter',\n message: `Invalid YAML frontmatter: ${err instanceof Error ? err.message : String(err)}`,\n });\n return { valid: false, issues, metadata: null };\n }\n\n // Required: name\n if (!data.name) {\n issues.push({ level: 'error', field: 'name', message: 'Missing required field: name' });\n } else {\n const name = String(data.name);\n\n if (name.length > MAX_NAME_LENGTH) {\n issues.push({\n level: 'error',\n field: 'name',\n message: `Name too long (${name.length} chars, max ${MAX_NAME_LENGTH})`,\n });\n }\n\n if (!NAME_PATTERN.test(name)) {\n issues.push({\n level: 'error',\n field: 'name',\n message: 'Name must be lowercase letters, numbers, and hyphens only',\n });\n }\n\n if (RESERVED_NAMES.includes(name.toLowerCase())) {\n issues.push({\n level: 'error',\n field: 'name',\n message: `Name \"${name}\" is reserved`,\n });\n }\n\n if (/<[^>]+>/.test(name)) {\n issues.push({\n level: 'error',\n field: 'name',\n message: 'Name must not contain XML/HTML tags',\n });\n }\n }\n\n // Required: description\n if (!data.description) {\n issues.push({\n level: 'error',\n field: 'description',\n message: 'Missing required field: description',\n });\n } else {\n const desc = String(data.description);\n\n if (desc.length > MAX_DESCRIPTION_LENGTH) {\n issues.push({\n level: 'error',\n field: 'description',\n message: `Description too long (${desc.length} chars, max ${MAX_DESCRIPTION_LENGTH})`,\n });\n }\n\n if (desc.length < WARN_DESCRIPTION_LENGTH) {\n issues.push({\n level: 'warning',\n field: 'description',\n message: `Description is short (${desc.length} chars). Consider adding more detail.`,\n });\n }\n\n if (/<[^>]+>/.test(desc)) {\n issues.push({\n level: 'error',\n field: 'description',\n message: 'Description must not contain XML/HTML tags',\n });\n }\n }\n\n // Body checks\n const bodyLines = body.trim().split('\\n').length;\n if (bodyLines > WARN_BODY_LINES) {\n issues.push({\n level: 'warning',\n field: 'body',\n message: `Body is long (${bodyLines} lines). Consider splitting into multiple skills.`,\n });\n }\n\n if (!body.trim()) {\n issues.push({\n level: 'warning',\n field: 'body',\n message: 'Empty skill body. Add instructions for the AI agent.',\n });\n }\n\n const hasErrors = issues.some((i) => i.level === 'error');\n\n return {\n valid: !hasErrors,\n issues,\n metadata: data,\n };\n}\n"],"mappings":"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,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA;AAAA,EAGA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA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","names":["existsSync","join","join","existsSync","stat","readdir","existsSync","lstatSync","cp","mkdir","rm","symlink","join","join","existsSync","mkdir","cp","stat","lstatSync","rm","symlink","existsSync","cp","mkdir","rm","homedir","basename","dirname","join","join","getPiAgentDir","homedir","join","mkdir","dirname","rm","cp","existsSync","result","basename","deepMerge","mkdir","dirname","existsSync","readFile","writeFile","existsSync","readFile","writeFile","existsSync","readFile","writeFile","existsSync","readFile","deepMerge","writeFile","existsSync","readFile","writeFile","existsSync","readFile","deepMerge","writeFile","existsSync","stat","existsSync","stat","existsSync","existsSync","existsSync","readFile","existsSync","readFile","rule","readdir","join","existsSync","mkdir","open","readFile","rename","rm","stat","writeFile","stat","rm","mkdir","open","writeFile","rename","existsSync","readFile","currentVersion","hasUpdate","API_BASE","toResult","recommendSkills","existsSync","basename","dirname","join","require","getSkillDir","existsSync","join","join","existsSync","existsSync","readdir","readFile","join","existsSync","readFile","matter"]}