import type { ContextFile } from "../capability/context-file.js"; import type { ExtensionModule } from "../capability/extension-module.js"; import { type Rule } from "../capability/rule.js"; import type { Skill } from "../capability/skill.js"; import type { LoadContext, LoadResult, SourceMeta } from "../capability/types.js"; import type { MCPRequestIdFormat } from "../mcp/types.js"; import { type ConfiguredThinkingLevel } from "@oh-my-pi/pi-tui/thinking"; /** * Standard paths for each config source. */ export declare const SOURCE_PATHS: { readonly native: { readonly userBase: string; readonly userAgent: string; readonly projectDir: string; }; readonly claude: { readonly userBase: ".claude"; readonly userAgent: ".claude"; readonly projectDir: ".claude"; }; readonly codex: { readonly userBase: ".codex"; readonly userAgent: ".codex"; readonly projectDir: ".codex"; }; readonly gemini: { readonly userBase: ".gemini"; readonly userAgent: ".gemini"; readonly projectDir: ".gemini"; }; readonly opencode: { readonly userBase: ".config/opencode"; readonly userAgent: ".config/opencode"; readonly projectDir: ".opencode"; }; readonly cursor: { readonly userBase: ".cursor"; readonly userAgent: ".cursor"; readonly projectDir: ".cursor"; }; readonly windsurf: { readonly userBase: ".codeium/windsurf"; readonly userAgent: ".codeium/windsurf"; readonly projectDir: ".windsurf"; }; readonly cline: { readonly userBase: ".cline"; readonly userAgent: ".cline"; readonly projectDir: null; }; readonly github: { readonly userBase: null; readonly userAgent: null; readonly projectDir: ".github"; }; readonly vscode: { readonly userBase: ".vscode"; readonly userAgent: ".vscode"; readonly projectDir: ".vscode"; }; }; export type SourceId = keyof typeof SOURCE_PATHS; /** * Resolve a user-level path for a source without the `~/` opt-in gate. * Only for callers that hold their own explicit opt-in (a per-capability * `skills.enable*User` / `commands.enable*User` toggle); everything else * goes through {@link getUserPath}. */ export declare function resolveUserPath(ctx: LoadContext, source: SourceId, subpath: string): string | null; /** * Get user-level path for a source, or null when its `~/` config is not * opted in (see {@link isUserSourceEnabled}). */ export declare function getUserPath(ctx: LoadContext, source: SourceId, subpath: string): string | null; /** * Get project-level path for a source (cwd only). */ export declare function getProjectPath(ctx: LoadContext, source: SourceId, subpath: string): string | null; /** * Resolve GitHub Copilot CLI's user-global config root. Copilot stores per-user * instructions/prompts/agents/MCP under `~/.copilot`, relocatable via the * `COPILOT_HOME` env var (mirrors Copilot CLI's `--config-dir`). Falls back to * `/.copilot` when the override is unset. */ export declare function resolveCopilotHome(home: string): string; /** * Create source metadata for an item. */ export declare function createSourceMeta(provider: string, filePath: string, level: "user" | "project", origin?: string, pluginName?: string): SourceMeta; export declare function parseBoolean(value: unknown): boolean | undefined; /** * Parse an MCP `requestIdFormat` value. Unrecognized values are dropped so a typo * degrades to the default integer ids rather than reaching a transport. */ export declare function parseRequestIdFormat(value: unknown): MCPRequestIdFormat | undefined; /** * Parse a comma-separated string into an array of trimmed, non-empty strings. */ export declare function parseCSV(value: string): string[]; /** * Parse a value that may be an array of strings or a comma-separated string. * Returns undefined if the result would be empty. */ export declare function parseArrayOrCSV(value: unknown): string[] | undefined; interface RuleMarkdownOptions { ruleName?: string; stripNamePattern?: RegExp; } /** Build a canonical rule from Markdown, including explicitly loaded disabled files. */ export declare function buildRuleFromMarkdown(name: string, content: string, filePath: string, source: SourceMeta, options?: RuleMarkdownOptions): Rule; /** Build a discovered rule from Markdown, returning null when its frontmatter disables it. */ export declare function discoverRuleFromMarkdown(name: string, content: string, filePath: string, source: SourceMeta, options?: RuleMarkdownOptions): Rule | null; /** * Parse model field into a prioritized list. */ export declare function parseModelList(value: unknown): string[] | undefined; /** Parsed agent fields from frontmatter (excludes source/filePath/systemPrompt) */ export interface ParsedAgentFields { name: string; description: string; tools?: string[]; spawns?: string[] | "*"; model?: string[]; output?: unknown; thinkingLevel?: ConfiguredThinkingLevel; autoloadSkills?: string[]; readSummarize?: boolean; blocking?: boolean; /** `true` = keep `yield` and the output schema when `task.structured` is off. */ structured?: boolean; /** `true` = prewalk into the default target; string = prewalk into that model pattern. */ prewalk?: boolean | string; /** `true` = advise with the default advisor-role model; string = advise with that model pattern. */ advisor?: boolean | string; } /** * Parse agent fields from frontmatter. * Returns null if required fields (name, description) are missing. */ export declare function parseAgentFields(frontmatter: Record): ParsedAgentFields | null; export interface ScanSkillsFromDirOptions { dir: string; providerId: string; level: "user" | "project"; requireDescription?: boolean; /** * When true, treat a `SKILL.md` sitting directly under `dir` as a single skill in addition to * scanning `//SKILL.md` children. Matches the Claude plugin manifest convention * that lets a skill path point at a directory containing `SKILL.md` directly (e.g. * `"skills": ["./"]`), where the frontmatter `name` determines the invocation name and the * directory basename is the fallback. Default `false` preserves the strict child-scan * semantic every non-Claude provider relies on. */ includeSelf?: boolean; /** * Registry/CLI origin of the plugin root supplying these skills, forwarded * to {@link SourceMeta.origin} so user-scope gating can tell omp's own * installs (`omp`, `plugin-dir`) from the foreign Claude tree (`claude`). */ origin?: string; /** * Plugin name supplying these skills, forwarded to {@link SourceMeta.pluginName} * so `skillNamespace` in `extensibility/skills.ts` can namespace by plugin * identity instead of parsing a path segment that may only hold a version * (Claude Code's own plugin cache layout). */ pluginName?: string; } export declare function compareSkillOrder(aName: string, aPath: string, bName: string, bPath: string): number; export declare function scanSkillsFromDir(_ctx: LoadContext, options: ScanSkillsFromDirOptions): Promise>; /** * Recursively expand environment variables in an object. */ export declare function expandEnvVarsDeep(obj: T, extraEnv?: Record): T; /** * Load files from a directory matching extensions. * Uses native glob for fast filesystem scanning with gitignore support. */ export declare function loadFilesFromDir(_ctx: LoadContext, dir: string, provider: string, level: "user" | "project", options: { /** File extensions to match (without dot) */ extensions?: string[]; /** Transform file to item (return null to skip) */ transform: (name: string, content: string, path: string, source: SourceMeta) => T | null; /** Whether to recurse into subdirectories (default: false) */ recursive?: boolean; /** Registry/CLI origin forwarded to {@link SourceMeta.origin} (see {@link createSourceMeta}). */ origin?: string; }): Promise>; /** * Calculate depth of target directory relative to current working directory. * Depth is the number of directory levels from cwd to target. * - Positive depth: target is above cwd (parent/ancestor) * - Zero depth: target is cwd * - This uses path splitting to count directory levels */ export declare function calculateDepth(cwd: string, targetDir: string, separator: string): number; /** * Load standalone context files (e.g. AGENTS.md, CLAUDE.md) by walking up from * cwd. Shared across providers whose files live in project root rather than * config directories (which their own providers handle). * * When a repository is nested below the user's home directory, continue past * the Git root to discover workspace-level files, but stop before loading the * home directory's own copy as project context. A repository rooted at the * home directory itself is not "nested below" it, so the home-level file * remains project context. */ export declare function loadStandaloneContextFiles(ctx: LoadContext, providerId: string, fileName: string): Promise>; /** * Discover extension module entry points in a directory. * * Discovery rules: * 1. Direct files: `extensions/*.ts` or `*.js` → load * 2. Subdirectory with index: `extensions//index.ts` or `index.js` → load * 3. Subdirectory with package.json: `extensions//package.json` with "omp"/"pi" field → load declared paths * * No recursion beyond one level. Complex packages must use package.json manifest. * Uses native glob for fast filesystem scanning with gitignore support. */ export declare function discoverExtensionModulePaths(_ctx: LoadContext, dir: string): Promise; /** * Derive a stable extension name from a path. */ export declare function getExtensionNameFromPath(extensionPath: string): string; /** * Build ExtensionModule items from discovered user/project paths. * Shared across providers that expose extension modules via user + project dirs. */ export declare function buildExtensionModuleItems(providerId: string, userPaths: string[], projectPaths: string[]): ExtensionModule[]; /** * Entry for an installed Claude Code plugin. */ export interface ClaudePluginEntry { /** Claude registry scope; project and local entries are restricted to their project path. */ scope?: "user" | "project" | "local"; installPath: string; version: string; installedAt: string; lastUpdated: string; gitCommitSha?: string; enabled?: boolean; /** Project root recorded by Claude for a project-bound installation. */ projectPath?: string; } /** * Claude Code installed_plugins.json registry format. */ export interface ClaudePluginsRegistry { version: number; plugins: Record; } /** * Resolved plugin root for loading. */ export interface ClaudePluginRoot { /** Plugin ID (e.g., "simpleclaude-core@simpleclaude") */ id: string; /** Marketplace name */ marketplace: string; /** Plugin name */ plugin: string; /** Version string */ version: string; /** Absolute path to plugin root */ path: string; /** Whether this is a user or project scope plugin */ scope: "user" | "project"; /** Registry or explicit CLI source that supplied this root. */ origin: "claude" | "omp" | "plugin-dir"; } /** * Parse Claude Code installed_plugins.json content. */ export declare function parseClaudePluginsRegistry(content: string): ClaudePluginsRegistry | null; /** Project discovery never examines home or shared temporary roots, or their ancestors. */ export declare function getProjectDiscoveryCeilings(): ReadonlySet; /** The current user's config roots are never project anchors. */ export declare function isUserConfigRoot(root: string): boolean; /** * An inactive default user layout (`/agent/`) is not a project anchor above a git * root or without a repo. `configDirName` is the project config dir the caller would load from. */ export declare function isForeignUserConfigRoot(root: string, configDirName: string): Promise; /** * Resolve the active project registry path by walking up from `cwd`. * * Walk order: * 1. Walk up from `cwd` looking for the nearest directory containing `.omp/`. * The first match returns `/.omp/plugins/installed_plugins.json`. * 2. If no `.omp/` is found, rescan from `cwd` upward looking for `.git`. * The git root is used as an anchor: `/.omp/plugins/installed_plugins.json`. * 3. Stop before home or a temporary root in either pass. If neither anchor is * found below that ceiling, return `null` — no project context is active. * Inactive `.omp/agent/` layouts are skipped only above the nearest git root, * or throughout an unanchored walk, so repo-local agent directories stay project scope. * * This is the single source of truth for "active project root" used by install, * uninstall, list, upgrade, discovery, and doctor. Deterministic for a given `cwd`. */ export declare function resolveActiveProjectRegistryPath(cwd: string): Promise; /** * Like resolveActiveProjectRegistryPath, but falls back to `/.omp/plugins/installed_plugins.json` * when no project anchor (.omp/ or .git/) is found. * * Use this when the caller accepts an explicit --scope project so that installing into a freshly * bootstrapped directory (no .omp/ or .git/ yet) works: writeInstalledPluginsRegistry auto-creates * the directory tree on first write. * * Returns undefined when cwd is a discovery ceiling or user config root, so the * fallback never creates a shared temporary registry or aliases the user registry. */ export declare function resolveOrDefaultProjectRegistryPath(cwd: string): Promise; /** Register a process-global plugin cache invalidator called whenever plugin roots are cleared. */ export declare function registerPluginCacheInvalidator(invalidator: () => void): void; /** A registered plugin root (see {@link isSupersededPluginPath}) and the id of its plugin. */ export interface RegisteredPluginRoot { root: string; pluginId: string; } /** The registered plugin root that holds `filePath`, if any. */ export declare function registeredPluginRootOf(filePath: string): RegisteredPluginRoot | undefined; /** * Whether `filePath` lies in a plugin install that the registries, as read for `cwd`, have replaced * with another install of the same plugin: an upgrade that kept the old version's directory, or a * project-scope install that now shadows a user-scope one. The cached roots for `cwd` are read again * first when a registry file changed on disk since they were read. A path in no registered plugin * root, or in a plugin the registries no longer install at all, is not superseded. */ export declare function isSupersededPluginPath(filePath: string, home: string, cwd?: string): Promise; /** * List all installed Claude Code plugin roots from its active plugin cache and * ~/.omp/plugins/installed_plugins.json, plus the nearest project registry when present. * * Results are cached per Claude and OMP config directories, project registry, and canonical active project. */ export declare function listClaudePluginRoots(home: string, cwd?: string): Promise<{ roots: ClaudePluginRoot[]; warnings: string[]; }>; /** * Clear the plugin roots cache (useful for testing or when plugins change). */ export declare function clearClaudePluginRootsCache(): void; /** * Invalidate fs caches for installed-plugin registry files and reset the * in-memory plugin roots cache. Used by MarketplaceManager clients after * installing/uninstalling/enabling/disabling plugins. */ export declare function clearPluginRootsAndCaches(extraPaths?: readonly string[]): void; /** * Populate the module-level plugin roots cache for sync consumers. * Call during session initialization, after dir resolution completes * but before any LSP config is read. */ export declare function preloadPluginRoots(home: string, cwd?: string): Promise; /** * Get pre-loaded plugin roots synchronously. * Returns empty array if preloadPluginRoots() hasn't been called. */ export declare function getPreloadedPluginRoots(): readonly ClaudePluginRoot[]; /** * Inject synthetic plugin roots from --plugin-dir paths. * These are prepended to the cache with highest precedence (before OMP/Claude entries). * Must be called before any listClaudePluginRoots() access. */ export declare function injectPluginDirRoots(home: string, dirs: string[], cwd?: string): Promise; export {};