/** * @skaile/asset-manager — programmatic AI asset management API. * * Manages project-local repositories, dependency resolution, deployment, * lock files, patches, and contribution workflows. * * Usage: * import { AssetManager } from "@skaile/workspaces/asset-manager" * const am = new AssetManager({ projectDir: "/path/to/project" }) * await am.install() */ import type { AssetRef, CatalogEntry, DriverTarget, LockFile, StoreFetcher } from "@skaile/workspaces/core"; import type { AssetFetcher } from "@skaile/workspaces/library"; import type { HistoryEntry } from "./history.js"; /** * Normalize a git remote URL to a comparable identity so the same repo declared * in different syntaxes (`https://…`, `git@host:…`, trailing `/`, `.git` suffix, * case) compares equal. Used to dedupe the implicit factory source against an * explicit user declaration. */ export declare function normalizeRepoUrl(url: string): string; /** * Asset kinds that are never **copied into a driver target dir** as files — * bundles resolve to their members, mcp-servers are wired at session startup, * and the mixin kinds are resolved at session creation. Mirrors the skip set in * `deployAll`, and says nothing about whether store bytes are fetched: * `mcp-server` IS staged into the store cache (see `stageStoreAssets`), because * its wiring reads a real `MCP.md` off disk. */ export declare const NON_DEPLOYABLE_KINDS: Set; /** * Parse a canonical lock key (`:@/#`) into its * parts. The lock key is a canonical asset ref, so this delegates to the one ref * parser; `AssetRef.pin` is exposed as `version` so the callers' destructuring is * unchanged. Stays total — callers rely on `{}`-then-`continue`, but `parseAssetRef` * throws, so a genuinely malformed key is caught and returned as `{}`. * * A **content-sha pseudo-version** key (`#0.0.0-sha.`, the version every * manifest-less source gets) fails `parseAssetRef`'s canonical-pin check. Dropping * it to `{}` would strip the identity, so the install reconcile / state machine * could never act on such an asset (it silently survives a remove-from-yaml). Fall * back to parsing the bare identity, keeping the raw pin as `version`. * * @internal Exported for unit tests; not part of the stable public surface. */ export declare function parseCanonicalLockKey(key: string): { publisher?: string; kind?: string; name?: string; version?: string; }; /** * Bare asset name of a soft-dep target ref (`@pub/name@ver` → `name`); splits the * version off the *last* `@` since the leading one is the scope sigil. The * present-check matches on this bare name only (under-reports — the safe direction). */ export declare function softDepName(ref: string): string; /** * Parse a ref for lookup, tolerating a **missing publisher** (`kind:name`, the * shape `listDeployed` emits and the strict `parseAssetRef` rejects). Returns * `null` when neither kind nor name is recoverable. Unlike {@link parseRefLoose} * (publisher-required, only strips a `#pin`), this keeps the publisher optional. * * @internal Exported for unit tests; not part of the stable public surface. */ export declare function parseRefTolerant(ref: string): AssetRef | null; /** * Reconciliation state of a declared dependency relative to the lock + disk. * `invalid` flags a `dependencies:` entry that isn't a parseable canonical ref — * surfaced (not silently dropped) so a `skaile.yaml` typo is visible. */ export type DeclaredState = "declared" | "installed" | "drift" | "invalid"; /** * A top-level dependency declared in `skaile.yaml`, annotated with its * reconciliation {@link DeclaredState}. Transitive (lock-only) deps are never * included — {@link AssetManager.listDeclared} walks the declared set only. */ export interface DeclaredAsset { /** The canonical declared ref exactly as written in `skaile.yaml`. */ ref: string; kind: string; name: string; state: DeclaredState; } /** * A global declared dependency from `~/.skaile/skaile.yaml`, reconciled against * **one** backend's global deploy dir. The same ref yields one entry per global * backend (see {@link AssetManager.globalState}) so an asset can read `installed` * for `claude-code` and `drift`/`declared` for `codex` simultaneously. */ export interface BackendDeclaredAsset extends DeclaredAsset { /** The global backend this state was reconciled against. */ backend: DriverTarget; } /** * A body-link soft-dependency edge: asset `from` references target `to` in its * body (not its typed `requires`). `toRef` is the parseable `skaile add` ref when * the target is in the index, omitted otherwise. Surfaced read-only — never * auto-installed (see {@link AssetManager.softDepsOf} / {@link AssetManager.reportSoftDeps}). */ export interface SoftDepEdge { from: string; to: string; toRef?: string; } /** * A soft-dependency target that would be left orphaned by removing some assets: * it is still present in the project, came in as `from`'s soft dependency, and * nothing else still needs it (neither a soft-dep nor a typed `requires`). * Report-only — {@link AssetManager.orphanedSoftDepsAfterRemoving} never removes * it; the caller prompts (default No). `targetRef` is the parseable `skaile * remove` ref when the target is in the index. */ export interface OrphanedSoftDep { from: string; target: string; targetRef?: string; } /** * Thrown by {@link AssetManager.listDeclared} when the project has no * `skaile.yaml`. The CLI maps this to a `skaile init` hint. */ export declare class MissingManifestError extends Error { readonly projectDir: string; constructor(projectDir: string); } export type { HistoryEntry } from "./history.js"; export { appendHistory, clearHistory, getRecentHistory, loadHistory, } from "./history.js"; export type { DeployOptions } from "./installer.js"; export { createScaffold, deployAll, removeAsset } from "./installer.js"; export type { RenderMcpConfigOptions, RenderMcpConfigResult, SkippedMcpServer, } from "./mcp-config.js"; export { renderMcpServerConfig } from "./mcp-config.js"; export type { Log } from "./renderers.js"; export { renderAgentToFramework } from "./renderers.js"; /** * Metadata about a repository declared in `skaile.yaml`. * * @docLink packages/asset-manager/concepts#asset-manager */ export interface RepoInfo { /** Repository name as declared in `skaile.yaml`. */ name: string; /** How the repo is sourced: cloned from a URL, mounted from a local path, or symlinked via `skaile repo link`. */ kind: "local" | "remote" | "linked"; /** Remote URL (present when `kind === "remote"`). */ url?: string; /** Filesystem path (present when `kind === "local"` or `kind === "linked"`). */ path?: string; /** Git branch configured for this repository. */ branch: string; /** Whether the repository has been cloned/resolved locally. */ cloned: boolean; } /** * Live sync status for a repository. * * @docLink packages/asset-manager/concepts#asset-manager */ export interface RepoStatusInfo { /** Repository name as declared in `skaile.yaml`. */ name: string; /** How the repo is sourced. */ kind: "local" | "remote" | "linked"; /** Number of commits the local clone is behind the remote. */ behind: number; /** `true` when the local clone matches the remote HEAD. */ upToDate: boolean; /** Error message when status check failed. */ error?: string; } /** * Result of a full `skaile install` or `skaile add` run. * * @docLink packages/asset-manager/concepts#asset-manager */ export interface InstallResult { /** Asset refs (`kind:name`) successfully deployed in this run. */ deployed: string[]; /** Asset refs (`kind:name`) removed because they are no longer in the resolved set. */ removed: string[]; /** Asset refs that could not be resolved in any configured repository. */ missing: string[]; /** Whether `skaile.lock.yaml` was written or updated. */ lockWritten: boolean; /** * npm packages installed as a side effect of resolving runtime asset peer * deps (connectors / mounts declared in skaile.yaml). Empty when nothing * needed installing or when the runtime-deps step was skipped. */ npmInstalled?: string[]; /** * npm packages that the runtime-deps step tried but failed to install. * Non-fatal — listed for telemetry and CLI reporting. */ npmFailed?: string[]; } /** * An installed asset whose source repository is behind the remote. * * @docLink packages/asset-manager/concepts#asset-manager */ export interface OutdatedEntry { /** Asset kind (`skill`, `agent`, `prompt`, `flow`, `contract`). */ kind: string; /** Asset name as registered in the catalog. */ name: string; /** Repository that provides this asset. */ publisher: string; /** Version string recorded in the lock file at install time. */ currentVersion: string; /** Number of commits the local repository is behind the remote. */ behind: number; } /** * A node in the resolved dependency tree (returned by `AssetManager.tree()`). * * @docLink packages/asset-manager/concepts#asset-manager */ export interface DependencyNode { /** Asset ref (`kind:name`) or `"project"` for the root node. */ ref: string; /** Transitive dependencies resolved by this node. */ children: DependencyNode[]; } /** * A missing dependency detected by `AssetManager.doctor()`. * * @docLink packages/asset-manager/concepts#asset-manager */ export interface DepIssue { /** Kind of the asset that has the unsatisfied requirement. */ assetKind: string; /** Name of the asset that has the unsatisfied requirement. */ assetName: string; /** Kind of the missing dependency. */ depKind: string; /** Name of the missing dependency. */ depName: string; /** `true` when the missing dependency exists in a configured repository but has not been installed. */ inRepos: boolean; } /** * A single deployed asset shown in the workspace overview. * * @docLink packages/asset-manager/concepts#asset-manager */ export interface OverviewEntry { /** Asset kind (`skill`, `agent`, `prompt`, `flow`, `contract`). */ kind: string; /** Asset name. */ name: string; /** Domain the asset belongs to (from the lock file; `"unknown"` when unresolvable). */ domain: string; /** Repository that provides this asset. */ publisher: string; /** Version string from the lock file. */ version: string; /** * Sync status relative to the source repository. * - `"synced"` — local clone matches remote HEAD * - `"outdated"` — local clone is behind the remote * - `"local"` — sourced from a local path, no remote to compare * - `"error"` — status check failed * - `"unknown"` — repository not found or lock entry missing */ syncStatus: string; } /** * Aggregated workspace overview returned by `AssetManager.overview()`. * * @docLink packages/asset-manager/concepts#asset-manager */ export interface OverviewResult { /** Entries grouped by domain, sorted alphabetically. Each group is sorted by kind then name. */ byDomain: Map; /** Total number of deployed assets. */ total: number; /** Sync status for each configured repository. */ repos: RepoStatusInfo[]; } /** * Options for constructing an {@link AssetManager}. * * @docLink packages/asset-manager/concepts#asset-manager */ export interface AssetManagerOptions { /** Absolute or relative path to the workspace root (must contain `skaile.yaml`). */ projectDir: string; /** * Agent framework to target for asset deployment. * Determines which framework directories (``.claude/``, ``.omp/``, etc.) are used. * Defaults to `"claude-code"`. */ driverTarget?: DriverTarget; /** * When `true`, the manager operates in **global cross-backend** mode: config * SSOT, lock, cache, and history root at `~/.skaile` (not the project), the * `dependencies:` manifest edits target `~/.skaile/skaile.yaml`, and deploy * fans out to every backend in {@link backends}. When `false` (default), * everything is project-scoped against the single {@link driverTarget}. */ global?: boolean; /** * Global-mode backend set the deploy fans out to. When omitted in global mode, * resolved from `~/.skaile/skaile.yaml` `global_backends:` else auto-detection * (`resolveGlobalBackends`). Ignored in project mode. A `--backend` filter * narrows this to a subset. */ backends?: DriverTarget[]; /** * Catalog read-protocol fetcher used to resolve store-published assets. When * omitted, one is built per call from the configured stores * (`RemoteCatalogSource`). Injected mainly by tests to stay network-free. */ storeFetcher?: StoreFetcher; /** * Byte fetcher used to fetch a store asset's files at its pinned commit before * deploy. When omitted, the production GitHub fetcher is used. Injected by * tests to stay network-free. */ assetFetcher?: AssetFetcher; /** * Overrides the global `catalog.url` used as a store fallback when the project * declares no `stores:`. Pass `""` to disable the fallback entirely. When * omitted, the URL is read from `~/.skaile/config.yaml` (`resolveConfig`). */ catalogUrl?: string; /** * Overrides the index SQLite path read by {@link AssetManager.reportSoftDeps} * (default `~/.skaile/index.db` via `resolveIndexPath`). Injected by tests to * read a seeded index without touching the user's real one. */ indexPath?: string; } /** Canonical git URL identity for the bundled factory source. Never fetched — * the bytes live inside the package (`factory-assets/`); the URL is only the * match key the provenance walker and `candidateToCatalogEntry` key off. */ export declare const FACTORY_SOURCE_URL = "https://github.com/skaile-ai/ai-assets"; /** * Programmatic API for managing AI assets in a Skaile workspace. * * `AssetManager` wraps repository management, catalog search, asset installation, * lock-file tracking, patch workflows, and workspace diagnostics. The CLI delegates * all `skaile catalog`, `skaile repo`, `skaile install`, `skaile add`, and * `skaile init` commands to this class. * * @example * ```typescript * import { AssetManager } from "@skaile/workspaces/asset-manager"; * * const am = new AssetManager({ projectDir: "/my-project" }); * * // Install all dependencies declared in skaile.yaml * const result = await am.install(); * console.log(`Deployed: ${result.deployed.join(", ")}`); * * // Add a single skill (resolves transitive deps, updates skaile.yaml) * const deployed = am.add("skill:my-skill"); * * // Search the catalog * const skills = am.search("code review", "skill"); * ``` * * @docLink packages/asset-manager/concepts#asset-manager */ export declare class AssetManager { /** Resolved absolute path to the workspace root. */ readonly projectDir: string; /** Agent framework driver target used for all deploy operations. */ readonly driverTarget: DriverTarget; /** Whether this instance operates in global-install mode. */ readonly global: boolean; /** Explicit global-backend override (global mode only); else auto-resolved. */ private readonly backendsOverride?; /** Injected catalog fetcher (tests / custom); falls back to a built one. */ private readonly injectedStoreFetcher?; /** Injected byte fetcher for store staging; falls back to GitHub. */ private readonly injectedAssetFetcher?; /** Injected global catalog URL override for the no-project-store fallback. */ private readonly catalogUrlOverride?; /** Injected index SQLite path override (tests); else the default index.db. */ private readonly indexPathOverride?; constructor(opts: AssetManagerOptions); /** * Root for this manager's **state** — config SSOT, lock, cache, history. * `~/.skaile` in global mode (the cross-backend SSOT), else the project dir. * Deploy dirs are unaffected; they come from `DRIVER_TARGETS[target]`. */ private get stateRoot(); /** * The backends a global deploy fans out to: the explicit `backends` override * (a `--backend` filter), else `resolveGlobalBackends()`. In project mode this * is exactly `[driverTarget]`, so the shared fan-out loop degenerates to the * single-target path. */ private targetBackends; private get storeCacheDir(); private get reposDir(); /** * Project-scoped cache dirs rooted at `.skaile/cache/` (`store`, `sources`) * relative to `projectDir`. Reading is a side effect: it triggers lazy * migration of the legacy `.skaile/{store-cache,repos}` dirs. */ get projectCacheDirs(): { store: string; sources: string; }; private get lockPath(); private get patchDir(); /** Deploy options for one backend. In global mode, `driverTarget` is the * fan-out target (not the constructor's `driverTarget`, which is project-only). */ private deployOptsFor; /** * Render the project's declared MCP servers into the driver target's own * native MCP config (`/.mcp.json` for `claude-code`), so the coding * agent sees the same servers skaile's agent loop wires up. * * Project scope only: a `--global` install has no project-scope config to * write, and the coding agent's user scope is a large user-state file this * must not touch. Never fails an install — a render error is warned and * swallowed. */ private renderDriverMcpConfig; private loadConfig; /** Build provenance-index clones from the resolved source declarations. */ private buildClones; /** * Bridge a resolved {@link ProvenanceCandidate} to a {@link CatalogEntry} for * the deploy pipeline. `source` is the absolute path to the asset's primary * manifest (or its directory) inside the clone; `publisher` is set to the * clone slug so `deployAll`'s repositories lookup resolves local-vs-remote. */ private candidateToCatalogEntry; /** * Resolve refs the source/store pass could not against the user's registered * **libraries** (local authoring places). Each library dir is scanned on disk * under its declared publisher (its root `skaile.manifest.yaml`, falling back * to the kebab-cased library name); a matching asset becomes a deployable * {@link CatalogEntry} whose `source` points at the on-disk manifest. This is * the consume side of `library create` — a freshly authored asset is * deployable with `skaile add :@/` without publishing. * * Pure local read (no index, no network) and mirrors how `findSkills` * discovers library content. Library assets are deliberately NOT written to * the lock (a local authoring place is not a reproducible source); they are * re-discovered on every `add`/`install`, so reconcile never treats them as * stale. * * @returns the matched deploy entries plus the set of `missing` refs covered. */ private resolveLibraryEntries; /** A candidate's stable identity key (`/:@`). */ private candidateKey; /** * Resolve `deps`, falling back to the global `catalog.url` store when the * project declares no store and the first pass leaves something missing. * * The first pass uses the project stores only, so locally-resolvable refs * never trigger a network probe. The fallback retry is best-effort: any error * (offline, store down) keeps the first pass's result rather than regressing. */ private resolveWithStores; /** The catalog fetcher to use: an injected one, else built from `stores`. */ private storeFetcherFor; /** * The global `catalog.url` as a single-store fallback, or `[]` when no * project store should be synthesized — `local` sentinel, already-declared, * or no URL. Honours an explicit `catalogUrl` injection (`""` disables it). */ private globalStoreFallback; /** * Build deployable catalog entries from resolved candidates. Store-resolved * candidates (no local clone bytes) are fetched + SHA-verified into the * project store-cache first, then their entry points at the staged bytes so * the existing copy-deploy path works. Source-backed candidates keep the * clone-relative entry. */ private buildCatalogEntries; /** * Fetch + SHA-verify the bytes of every store-resolved candidate that needs * bytes on disk into `.skaile/cache/store/`, reusing the same verified-fetch * path the pointer-only `library.install` uses (`installFromManifest` flat * mode). That is the deployable kinds plus `mcp-server` — see the skip guard. * * @returns Map of candidate key → staged primary-manifest path. */ private stageStoreAssets; /** Repo-relative path of a candidate's primary manifest (un-stripped). */ private primaryManifestRel; /** * The primary-manifest path relative to a staged asset dir. Takes the kind's * manifest (e.g. `SKILL.md`), then strips the same common-dir prefix * `installFromManifest` flat mode removed on write. */ private stagedManifestRel; /** * `true` when a candidate's primary manifest is present on a local source * clone — its `sourceUrl` matches a registered source AND the manifest file * exists on disk at the expected path. A URL match alone is not enough: a * store can pin a different commit (or a subtree not bundled) of a repo that * is also a registered source, in which case the bytes must be fetched from * the store rather than read from the clone. (Only the primary manifest is * probed — a partially-cloned source is deployed as-is, matching the existing * clone-deploy path.) */ private candidateHasLocalBytes; /** Build a copy-deploy catalog entry pointing at staged store bytes. */ private stagedCandidateToEntry; /** * Install all dependencies declared in `skaile.yaml`. * * Steps: clone/pull repositories → resolve transitive dependencies → reconcile * (remove stale assets) → deploy new assets → write `skaile.lock.yaml` → record history. * * @param opts - Optional installation options. * @param opts.locked - When `true`, restore the exact versions recorded in the existing * lock file instead of resolving from source. Throws if no lock file is found. * @param opts.link - When `true`, symlink local-path assets instead of copying them * (the author dev-loop). Machine-local / not for committing; a no-op for * store/factory sources. Defaults to copy. * @returns {@link InstallResult} summarising what was deployed, what was missing, and any collisions. */ install(opts?: { locked?: boolean; link?: boolean; }): Promise; /** * Ensure all source clones are present/up to date. Returns an early * {@link InstallResult} describing the failed source on the first error, or * `null` when every clone succeeds. */ private ensureSourceClones; /** * Best-effort twin of {@link ensureSourceClones} for the read-mostly entry * points (`add` / `lock`): same declarations and the same pins — a pinned * source must never be pulled off its pin — but a clone failure is swallowed * so resolution still runs against whatever is on disk. */ private ensureSourceClonesQuiet; /** * Remove stale assets — those recorded in the old lock but absent from the * newly resolved set. Returns the list of removed refs. */ private reconcileRemovedAssets; /** Append `add`/`remove` history entries for a completed install reconcile. */ private recordInstallHistory; /** Read the lock, returning null on a legacy v1 lock instead of throwing. */ private tryReadLock; /** Build a store fetcher from the configured stores, or undefined when none. */ private buildStoreFetcher; /** * Resolve runtime assets and install any missing npm peer deps. * * - Required deps: installed via `bun add --optional`. Failure is recorded * in `npmFailed` but does not throw — install() proceeds so users get a * useful error message rather than a hung command. * - Optional deps: not installed automatically (would force every consumer * to pull every connector's deps). Future: add a flag to opt in. */ private installRuntimeAssetDeps; private installLocked; /** * Add a single asset (and its transitive dependencies) to the workspace. * * Resolves `ref`, deploys the asset and all requirements, then records the ref * in `skaile.yaml` `dependencies` so subsequent `skaile install` runs re-deploy it. * * @param ref - Asset ref in `kind:name` or `repo/kind:name` form (e.g. `skill:my-skill`). * @param opts - Optional deploy options. * @param opts.link - When `true`, symlink local-path assets instead of copying them * (the author dev-loop). Machine-local / not for committing; a no-op for * store/factory sources. Defaults to copy. * @returns Array of asset refs that were deployed in this call. * @throws When the asset cannot be found in any configured repository. */ add(ref: string, opts?: { link?: boolean; }): Promise; /** * Report the body-link **soft dependencies** of the given assets that are not * already present in the project. * * Looks up each ref's persisted `softDeps` in the local index * (`~/.skaile/index.db`, populated by `skaile source sync`) and returns the * edges whose target is neither a declared dependency nor deployed on disk. It * does **not** install anything and never touches the resolver / lock / * reconcile path: callers surface the result as a hint, the user opts in with * a follow-up `skaile add`. Inspects exactly the passed `refs` — soft deps of * their transitively-deployed dependencies are not surfaced here. * * Opening the index runs its idempotent migrations, so this is not a pure * read — but it is **best-effort**: any failure (legacy/corrupt index, a * removed-backend guard) yields `[]`, so it can never fail a primary `add` * that already succeeded. * * @param refs - Asset refs to inspect (canonical, `kind:@pub/name`, or `kind:name`). * @returns One edge per absent soft-dep target (deduped by target). `toRef` is * the parseable `skaile add` ref, omitted when the target isn't in the index. */ reportSoftDeps(refs: string[]): Promise>; /** * All body-link soft dependencies of the given assets — present or not. * * Unlike {@link reportSoftDeps} (which filters to *absent* targets so it can * nudge a follow-up `skaile add`), this returns the asset's full inferred * soft-dep set for display surfaces (`skaile info` / `tree` / `why`). Read-only * and best-effort: any index failure yields `[]`. Targets are deduped. * * @param refs - Asset refs to inspect (canonical, `kind:@pub/name`, or `kind:name`). * @returns One {@link SoftDepEdge} per soft-dep target. `toRef` is the parseable * ref, omitted when the target isn't in the index. */ softDepsOf(refs: string[]): Promise; /** * Reverse lookup: refs (canonical ids) of cached assets whose persisted * `softDeps` include `targetRef`. Scans the whole index, not just present * assets. Read-only and best-effort (`[]` on any index failure). * * @param targetRef - Canonical soft-dep target ref (`/@`). * @returns Def ids that declare `targetRef` as a soft dependency. */ softDepDependents(targetRef: string): Promise; /** * Compute which soft-dependency targets would be left **orphaned** by removing * `refs`: targets still present in the project that came in as one of `refs`' * soft dependencies and that **nothing else still needs** — neither another * present asset's soft-dep ({@link softDepDependents}) nor a typed `requires`. * * Pure read — never removes anything. The CLI / manage surfaces each result as * a prompt (default No) so the user opts in to an extra `skaile remove`. Safe to * call before or after the primary remove: assets being removed are excluded * from the "needed by others" scan, so a soft target referenced only by the * removed asset is still reported as an orphan. * * @param refs - Asset refs being removed (canonical, `kind:@pub/name`, or `kind:name`). * @returns One {@link OrphanedSoftDep} per orphaned target (deduped). */ orphanedSoftDepsAfterRemoving(refs: string[]): Promise; /** * Open the local index, run `fn` against its cached asset defs, and close it. * Best-effort: no index (user never ran `source sync`), a missing file, or any * open/guard failure yields `fallback` — so a read surface can never throw or * create an empty `index.db` as a side effect. */ private withIndexDefs; /** * Build soft-dep edges for `refs` from cached defs. With `onlyAbsent`, targets * already present in the project are skipped (the `reportSoftDeps` nudge); else * the full inferred set is returned (the `softDepsOf` display). Deduped by target. */ private collectSoftDeps; /** * Names of assets already in the project — declared deps + deployed on disk. * Bare name only (see {@link softDepName}): a same-named asset of a different * publisher/kind counts as present, so the report under-reports rather than * nags about something already installed. */ private presentRefNames; /** * Present asset refs — declared `skaile.yaml` deps (canonical refs as written) * plus deployed assets as `kind:name`. Used by the soft-dep present-check and * the orphan "needed by others" scan. */ private presentRefs; /** * Remove a deployed asset and unregister it from `skaile.yaml`. * * Deliberately does NOT re-render the driver target's MCP config: the render * step is async (the declaration resolver is), and making this synchronous * method async would break every external caller. A removed MCP server's * generated entry is dropped by the next {@link AssetManager.install} — see * {@link renderMcpServerConfig}. * * @param ref - Asset ref in `kind:name` form (e.g. `skill:my-skill`). * @returns `true` if the asset was found and removed, `false` if it was not deployed. */ remove(ref: string): boolean; /** * Drop every lock entry matching a removed asset's identity, version-agnostic so * a content-sha pseudo-version key is pruned too. Symmetric with {@link add}'s * lock-merge — stops a stale entry reporting a phantom "removed" on next install. */ private pruneLockEntry; /** * Search the catalog across all configured repositories. * * @param query - Substring to match against entry names and descriptions (case-insensitive). * Omit to return all entries. * @param kind - Filter by asset kind (`"skill"`, `"agent"`, `"prompt"`, `"flow"`, `"contract"`). * Omit to return all kinds. * @returns Array of matching {@link CatalogEntry} objects from all accessible repositories. */ search(query?: string, kind?: string): Promise; /** * On-disk root + config slug for every declared (and the implicit factory) * source that has a local clone. Companion to {@link search}: callers that * need to relate a scanned entry back to its source root (e.g. to stamp * `DOMAIN.md`-derived domain grouping) can't recover the root from the flat * {@link CatalogEntry} list `search` returns. */ searchRepoRoots(): { repoName: string; root: string; }[]; /** * Look up a single asset's full catalog entry. * * Accepts a bare `kind:name` ref (no publisher) in addition to the fully-qualified * `kind:@publisher/name` form — {@link listDeployed} emits publisher-less refs and * {@link doctor} passes them straight to this method. * * @param ref - Asset ref in `kind:name`, `kind:@publisher/name`, or `repo/kind:name` form. * @returns The matching {@link CatalogEntry}, or `null` when not found. */ info(ref: string): Promise; /** * List all assets currently deployed in the workspace (or global cache). * * Scans the framework deploy directories rather than the lock file, so it * also returns assets that were installed manually or outside of `skaile install`. * * @param kind - Filter by asset kind. Omit to return all kinds. * @returns Shallow {@link CatalogEntry} objects (description and version will be empty). */ listDeployed(kind?: string): CatalogEntry[]; /** * List the **declared** top-level dependencies from `skaile.yaml`, each tagged * with a reconciliation {@link DeclaredState}: * * - `declared` — in `skaile.yaml`, not yet resolved into the lock. * - `installed` — declared + locked, and (for deployable kinds) materialized on * disk. An `mcp-server` is instead checked against the declarations the runner * would actually wire (store-staged or materialized `MCP.md`); the remaining * non-deployable kinds ({@link NON_DEPLOYABLE_KINDS}) count as installed once * locked — they are resolved at session start with no on-disk footprint here. * - `drift` — declared + locked but the deployed files are missing on disk. * - `invalid` — the `dependencies:` entry isn't a parseable canonical ref. Only * surfaced in the unfiltered listing (a `kind` filter can't classify it). * * Transitive (lock-only) deps are excluded by construction — only the declared * set is walked, so a lock entry with no matching `dependencies:` ref never shows. * * @param kind - Filter by asset kind. Omit to return all kinds. * @returns One {@link DeclaredAsset} per declared dep (after the optional kind filter). * @throws {MissingManifestError} when the project has no `skaile.yaml`. * @throws {Error} with a `migrate-skaile-manifest` hint on legacy `repositories:` / `ai_resources:` keys. */ listDeclared(kind?: string): Promise; /** * Per-backend reconciliation of the **global** declared deps * (`~/.skaile/skaile.yaml`) against each global backend's deploy dir. The * global twin of {@link listDeclared}: the same `declared` / `installed` / * `drift` / `invalid` vocabulary, but one row **per (ref, backend)** so an * asset can be `installed` for `claude-code` and `drift`/`declared` for * `codex` at once. * * Reads the SSOT + the global lock (`~/.skaile/skaile.lock.yaml`) — construct * the manager with `global: true`. Unlike `listDeclared`, a missing SSOT is * **not** an error: it yields an empty list (a fresh machine has no globals). * * @param opts.backend - Reconcile against this one backend only (the * `--backend` filter); omit for every backend in {@link targetBackends}. * @param opts.kind - Filter by asset kind. * @returns One {@link BackendDeclaredAsset} per declared dep × backend. * @throws {Error} on legacy keys in `~/.skaile/skaile.yaml` (migration hint). */ globalState(opts?: { backend?: DriverTarget; kind?: string; }): BackendDeclaredAsset[]; /** * Build an aggregated workspace overview with domain grouping and sync status. * * Combines deployed assets, lock file metadata, and live repository status * into a single structured result. Used by `skaile status`. * * @returns {@link OverviewResult} with entries grouped by domain and per-repo sync status. */ overview(): OverviewResult; /** Collect live sync status for every configured repository, keyed by name. */ private buildRepoStatuses; /** * Build the resolved dependency tree from the lock file. * * @returns Root {@link DependencyNode} with `ref === "project"` whose children * are the directly-declared dependencies, each with their own transitive subtrees. * Returns a single node with `ref === "(no lock file)"` when no lock exists. */ tree(): DependencyNode; /** * Explain why an asset is installed by tracing its dependency chain in the lock file. * * @param ref - Asset ref in `kind:name` form. * @returns Chain of refs from the asset back to `"direct (skaile.yaml)"`, or `[]` when * the asset is not in the lock file. */ why(ref: string): string[]; /** * List installed assets whose source repository is behind the remote. * * Only remote repositories (not local paths) are checked for staleness. * * @returns Array of {@link OutdatedEntry} objects, one per asset in a repository * that has commits not yet pulled locally. */ outdated(): OutdatedEntry[]; /** * Produce a unified diff between the deployed asset and its catalog source. * * Useful for detecting manual edits to deployed files or verifying that a patch * was applied correctly. * * @param ref - Asset ref in `kind:name` form. * @returns Unified diff string, or `null` when there are no differences or the * asset is not deployed/resolvable. */ diff(ref: string): Promise; /** * Resolve all declared dependencies and write (or overwrite) `skaile.lock.yaml`. * * Performs repository sync and full dependency resolution without deploying assets. * Use this to regenerate the lock file after manually editing `skaile.yaml`. * * @returns The written {@link LockFile} data. */ lock(): Promise; /** * Extract a deployed asset into a patch working directory for editing. * * Copies the deployed asset to `.skaile/patches/-/` so changes * can be made there and then committed as a patch file via `patchCommit`. * * @param ref - Asset ref in `kind:name` form. * @returns Absolute path to the patch working directory. * @throws When the asset cannot be found in the catalog. */ patch(ref: string): Promise; /** * Generate a `.patch` file from changes made in the patch working directory. * * Diffs the patch working directory against the original catalog source and * writes the result to `.skaile/patches/-.patch`. * * @param ref - Asset ref in `kind:name` form. * @returns Absolute path to the generated `.patch` file. * @throws When the patch working directory does not exist or no differences are found. */ patchCommit(ref: string): Promise; /** * Apply the committed patch file to the repository clone and open a contribution branch. * * Applies `.skaile/patches/-.patch` to the repository clone, creates a * `patch/-` branch, and stages a commit — ready to push via `contribPush`. * * @param ref - Asset ref in `kind:name` form. * @throws When the asset is not found, has no repository, or the patch file is missing. */ patchSubmit(ref: string): Promise; /** * Delete the patch working directory and patch file for an asset. * * @param ref - Asset ref in `kind:name` form. */ patchRemove(ref: string): void; /** * Scan deployed assets for unsatisfied transitive dependencies. * * For each deployed asset, checks that all entries in its `requires` list are * also deployed. Returns a list of issues with a flag indicating whether the * missing dependency can be resolved from a configured repository. * * @returns Array of {@link DepIssue} objects. An empty array means the workspace is healthy. */ doctor(): Promise; /** * Create a minimal asset scaffold directory (delegates to {@link createScaffold}). * * @param name - Asset name (also used as the directory name). * @param kind - Asset kind: `skill` | `agent` | `bundle` | `flow` | `prompt` | `contract`. * @param destDir - Parent directory to create the asset in. * @returns `{ ok: true, path }` on success, `{ ok: false, path: errorMessage }` if the * directory already exists. */ create(name: string, kind: string, destDir: string): { ok: boolean; path: string; }; /** * Return recent asset action history entries, newest first. * * @param limit - Maximum number of entries to return. Defaults to `20`. * @returns Array of {@link HistoryEntry} objects in reverse-chronological order. */ history(limit?: number): HistoryEntry[]; /** * Erase all asset action history for this workspace. */ clearHistory(): void; /** * Remove deployed assets tracked by the lock file and report unmanaged ones. * * With `opts.all`, also deletes `.skaile/history.yaml`, `.skaile/patches/`, * `.skaile/cache/sources/`, and `skaile.lock.yaml` — effectively resetting the * workspace to a pre-install state. * * @param opts - Optional cleanup options. * @param opts.all - When `true`, remove all Skaile-managed state in addition to deployed assets. * @returns Object with `removed` (successfully cleaned refs), `skipped` (with reasons), * and `unmanaged` (deployed assets not in the lock file). */ clean(opts?: { all?: boolean; }): { removed: string[]; skipped: Array<{ ref: string; reason: string; }>; unmanaged: string[]; }; /** * Remove the deployed dir of every asset tracked by the lock; missing dirs * still count as removed. Returns the list of cleaned refs. */ private cleanLockedDeployedAssets; /** * Report deployed assets not tracked by the lock file. Deployed assets are * keyed by :; lock canonical refs are mapped to that shape. */ private collectUnmanagedAssets; /** Delete all Skaile-managed state: history file, patches/, cache/sources/, lock. */ private removeAllManagedState; /** * Add a dependency ref to the manifest `dependencies:` if not already present. * Project mode edits `/skaile.yaml`; global mode edits the * user-scope SSOT `~/.skaile/skaile.yaml`, **bootstrapping** the file (and its * parent dir) when absent so `skaile add --global` works on a fresh machine. */ private addDependencyToConfig; /** * Remove a dependency ref from the manifest `dependencies:`. Targets the same * file as {@link addDependencyToConfig} (`~/.skaile/skaile.yaml` in global mode). */ private removeDependencyFromConfig; private resolveRepoDir; } export { installAgent } from "./install-agent.js"; export type { AgentRenderer, AgentRenderInput, AgentRenderResult } from "./renderers.js"; export { AGENT_RENDERERS, claudeCodeRenderer, codexRenderer, driverTargetSupportsAgents, ompRenderer, } from "./renderers.js"; export type { AbilityRef, ConnectorRef, ContractRef, FragmentContext, FragmentId, } from "./fragments.js"; export { BUILT_IN_FRAGMENTS, loadPromptExtensions, resolveFragments } from "./fragments.js"; //# sourceMappingURL=index.d.ts.map