/** * Repository operations — clone, pull, scan, resolve, link. * * Remote repos are cloned to a shared global cache (~/.skaile/cache/sources//). * Projects reference the cache via symlinks at .skaile/cache/sources//. * Local repos (path:) are used directly. Linked repos override remote URLs. * * Supports partial clone + sparse checkout for large repos. */ import type { AssetRef, CatalogEntry } from "./models.js"; import { type ProvenanceCandidate, type ProvenanceIndex } from "./walker.js"; import type { OverrideEntry, SourceDeclaration, StoreEntry } from "./workspace-config.js"; /** * The global source-clone cache: `/sources` (override the root with * `SKAILE_CACHE_DIR`). On first call it migrates both legacy clone roots into it: * the whole of `~/.skaile/repos/*`, and the own-`.git` (clone) entries of * `~/.skaile/sources/*`. The non-clone remainder of `~/.skaile/sources` is the * sidecar repo and is migrated separately by `getSidecarRoot()`. * @docLink packages/core/api-reference#get-global-cache-dir */ export declare function getGlobalCacheDir(): string; /** * Canonical clone-cache key for a source git URL: `//` * (lowercased host; `.git` suffix, trailing slash, and protocol/scp prefix * stripped). This is the SSOT for the clone-cache path under * `getGlobalCacheDir()` — every writer and reader of that cache MUST derive its * key here. * * It exists because the old "last path segment" slug collided: both * `github.com/orgA/ai-assets` and `github.com/orgB/ai-assets` mapped to * `ai-assets`, so the wrong repo was served from cache silently. Keying by the * full `//` triple makes that collision impossible. * * Non-GitHub or unparseable URLs (local paths, `file://`, exotic hosts) fall * back to the last path segment so nothing throws. * * Userinfo is dropped, so `https://x-access-token:@host/o/r` keys the * same clone as the plain url — and the token never becomes a directory name * or leaks through a message that names the key (`install` reports failures as * `source: (…)`). * * @docLink packages/core/api-reference#source-cache-key */ export declare function sourceCacheKey(url: string): string; /** * Short repo name (``) for a cache key produced by {@link sourceCacheKey} * (or a raw URL). Used where a single human-facing label or library name is * wanted instead of the full `//` triple. */ export declare function sourceShortName(cacheKeyOrUrl: string): string; /** * Collision-free, filesystem-safe flat slug for a source URL — the basename of * its curated store manifest (`~/.skaile/store/manifests/.yaml`). Derived * from the full {@link sourceCacheKey} (`//`) with path * separators encoded, so `orgA/cli` and `orgB/cli` never collide on the trailing * repo name (which {@link sourceShortName} would). Keyed identically by the * `init` writer, the `source add`/`sync` overlay reader, and the resolver. */ export declare function storeManifestSlug(url: string): string; /** * Clone a remote git repository using partial clone (treeless) with sparse checkout. * Falls back to a regular shallow clone if the server does not support partial clones. * * @param url - Remote git URL * @param branch - Branch to clone * @param dest - Local destination directory * @returns `true` on success, `false` on failure * @docLink packages/core/api-reference#clone-repo */ export declare function cloneRepo(url: string, branch: string, dest: string): boolean; /** Result of {@link pullRepoResult}: success flag plus git's reason on failure. */ export interface PullResult { ok: boolean; error?: string; } /** * Pull the latest commits for a cloned repository, reporting git's own failure * reason. Same strategy as {@link pullRepo} — of which this is the detailed * form — so callers can surface *why* a refresh failed instead of swallowing it. * * @param dest - Local repo directory * @param branch - Branch to pull * @returns `{ ok: true }`, or `{ ok: false, error }` carrying the git message * @docLink packages/core/api-reference#pull-repo-result */ export declare function pullRepoResult(dest: string, branch: string): PullResult; /** * Pull the latest commits for a cloned repository. * On shallow clone divergence, falls back to `git fetch --depth=1` + `git reset --hard`. * * @param dest - Local repo directory * @param branch - Branch to pull * @returns `true` on success, `false` on failure * @docLink packages/core/api-reference#pull-repo */ export declare function pullRepo(dest: string, branch: string): boolean; /** * Return the current HEAD commit SHA for a local repository. * * @param repoDir - Absolute path to the cloned repository * @returns Full commit SHA string, or `null` on failure * @docLink packages/core/api-reference#get-repo-commit */ export declare function getRepoCommit(repoDir: string): string | null; /** * Check out a specific tag, branch, or commit SHA in a local repository. * Fetches the ref from `origin` first, then attempts `FETCH_HEAD` checkout. * * @param repoDir - Absolute path to the cloned repository * @param pin - Tag, branch name, or commit SHA * @returns `true` on success, `false` on failure * @docLink packages/core/api-reference#checkout-pin */ export declare function checkoutPin(repoDir: string, pin: string): boolean; /** * Map of repo name → local filesystem path for development overrides. * Stored in `.skaile/links.yaml`; used to redirect a named repo to a local clone. * @docLink packages/core/api-reference#link-config */ export interface LinkConfig { [repoName: string]: string; } /** * Read the dev-link map from `.skaile/links.yaml` in the project directory. * Returns an empty object when no links file exists. * * @param projectDir - Absolute path to the project root * @returns `LinkConfig` map (may be empty) * @docLink packages/core/api-reference#read-links */ export declare function readLinks(projectDir: string): LinkConfig; /** * Persist the dev-link map to `.skaile/links.yaml` in the project directory. * * @param projectDir - Absolute path to the project root * @param links - Updated `LinkConfig` to write * @docLink packages/core/api-reference#write-links */ export declare function writeLinks(projectDir: string, links: LinkConfig): void; /** * Register a local filesystem path as a dev override for a named repository. * The path is stored in `.skaile/links.yaml` and takes priority over the remote URL at runtime. * * @param projectDir - Absolute path to the project root * @param repoName - Name of the repository to override * @param localPath - Local path (absolute or relative to `projectDir`) to use instead * @throws When the resolved path does not exist * @docLink packages/core/api-reference#link-repo */ export declare function linkRepo(projectDir: string, repoName: string, localPath: string): void; /** * Remove the dev-link override for a named repository, reverting it to the remote URL. * * @param projectDir - Absolute path to the project root * @param repoName - Name of the repository to unlink * @returns `true` if the link existed and was removed, `false` if no link was found * @docLink packages/core/api-reference#unlink-repo */ export declare function unlinkRepo(projectDir: string, repoName: string): boolean; /** * Ensure a repository is available locally, cloning or symlinking as needed. * * Resolution order: * 1. Linked override (`.skaile/links.yaml`) — highest priority * 2. Local path (`decl.path`) — direct filesystem reference; a bare path must * already exist, but a url-backed managed cache dir is cloned in-place on miss * 3. Remote URL (`decl.url`) — cloned to the shared global cache, symlinked into `.skaile/cache/sources/` * * Freshness: a **managed cache clone** (a url-backed dir under * {@link getGlobalCacheDir}) is refreshed on every call when no pin is given — * both for a bare `url:` declaration and for the `path:`-into-the-cache form * that `AssetManager` builds from `sources:`, so `install` never serves a stale * source. A failed refresh warns and keeps the cached commit. A user-owned * checkout (bare `path:`, dev link, factory tree) is never touched. * * Pin support: when `opts.pin` is provided, the tag/commit is checked out and * the clone is never pulled. * * @param decl - Repository declaration from `skaile.yaml` * @param name - Logical repository name (cache key) * @param reposDir - Project-local source-clone directory (`.skaile/cache/sources/`) * @param opts - Optional: `pin` for a specific commit/tag, `projectDir` for link lookup * @returns Absolute path to the local repository directory * @throws When the repository cannot be resolved or cloned * @docLink packages/core/api-reference#ensure-repo */ export declare function ensureRepo(decl: SourceDeclaration, name: string, reposDir: string, opts?: { pin?: string; projectDir?: string; }): string; /** * Resolve a scanned source's canonical publisher: the declared `publisher` in * the root `skaile.manifest.yaml`, else the GitHub org from the `origin` remote. * Returns `undefined` when neither is available, so callers can fall back to the * repo config key. * * Single source of truth shared by the source/library scanner (`scanRepo`) and * the index/discovery side (`LocalCatalogSource.sync`) so both key an asset * identically. The org fallback is lowercased to its GitHub-shaped slug * (GitHub orgs are case-insensitive — a cased remote like `Acme` must not split * from the lowercase index ref). A declared manifest publisher has its single * leading `@` (the scope sigil, added automatically by the ref formatter) * stripped so the scan/display refs match the decode path — `@acme` would * otherwise render as `@@acme` and be un-addable. * * @param repoDir - Absolute path to the scanned repository root * @returns The publisher slug (no `@` sigil) or `undefined` * @docLink packages/core/api-reference#resolve-source-publisher */ export declare function resolveSourcePublisher(repoDir: string): string | undefined; /** * Scan a repository directory and return all discovered asset catalog entries. * Delegates to `scanDirectory` from `manifest.ts`, then stamps the `repo` slug * derived once from the root's git remote onto every entry (the GitHub * coordinate is a property of the scanned root, not of individual assets). * * The `publisher` field is overridden with the source's declared canonical * publisher (`skaile.yaml` `publisher` / GitHub org) so refs emitted from * filename-convention scans match the provenance index `resolveAll` builds. * Without this, a source whose declared publisher differs from its config slug * yields refs (`name@`) that resolution can't find (`name@`). * * @param repoDir - Absolute path to the repository root * @param repoName - Repository config key; fallback publisher and `repository` field * @returns Array of `CatalogEntry` objects found in the repository * @docLink packages/core/api-reference#scan-repo */ export declare function scanRepo(repoDir: string, repoName: string): Promise; /** * Resolve a single asset reference to its `CatalogEntry` by scanning declared repositories. * * Search order: * 1. Explicit `@repository` qualifier on the ref — only that repo is searched. * 2. `opts.preferRepo` — searched first when set (repo affinity for transitive deps). * 3. All declared repos in declaration order. * * @param ref - Parsed asset reference to resolve * @param repositories - Repository declarations from `skaile.yaml` * @param reposDir - Project-local source-clone directory (`.skaile/cache/sources/`) * @param opts - Optional: `projectDir` for link lookup, `preferRepo` for affinity * @returns Matching `CatalogEntry`, or `null` if not found in any repository * @docLink packages/core/api-reference#resolve-asset */ export declare function resolveAsset(ref: AssetRef, repositories: Record, reposDir: string, opts?: { projectDir?: string; preferRepo?: string; }): Promise; /** * Hard error raised when two or more candidates for the same canonical ref * `/:@` disagree on their content sha256 and no * `overrides[]` entry resolves the conflict. The message walks the dep chain * back to the user-declared root. * @docLink packages/core/api-reference#canonical-ref-conflict-error */ export declare class CanonicalRefConflictError extends Error { ref: string; candidates: ProvenanceCandidate[]; depChain: string[]; constructor(ref: string, candidates: ProvenanceCandidate[], depChain: string[]); } /** * Result of a full transitive dependency resolution via `resolveAll`. * @docLink packages/core/api-reference#resolve-result */ export interface ResolveResult { /** Resolved candidates in leaf-first order. */ resolved: ProvenanceCandidate[]; /** Refs that no source / store could provide. */ missing: string[]; /** "/:" → parent ref ("direct" if user-declared). */ resolvedBy: Map; /** Overrides that were applied (lock-side bookkeeping); canonical refs. */ overridesApplied: Set; } /** Minimal store fetcher contract used by the resolver for the catalog branch. */ export interface StoreFetcher { getInstallManifest(storeUrl: string, ref: string): Promise<{ sourceUrl: string; commit: string; sha256: string; files: Array<{ path: string; sha256: string; }>; } | null>; getCanonicalDigest(storeUrl: string, ref: string): Promise<{ sha256: string; } | null>; /** * Enumerate the versions a store can serve for a version-less ref * (`kind:@/name`). Optional — when present, the resolver uses it to * discover candidate versions for a store-only asset whose dep ref is bare or * ranged (no source clone to derive a version hint from). Absent ⇒ the * resolver falls back to the source-derived hint only (legacy behaviour). */ listVersions?(storeUrl: string, ref: string): Promise; } /** Options for {@link resolveAll}. */ export interface ResolveOpts { /** Source-side candidate index built by the walker. */ provenanceIndex: ProvenanceIndex; /** Conflict-resolution overrides (with required reason). */ overrides: OverrideEntry[]; /** Trusted store catalogs. */ stores: StoreEntry[]; /** Lazy store fetcher; only used when `stores[]` is non-empty. */ storeFetcher?: StoreFetcher; projectDir?: string; } /** * Resolve all dependencies against a content-hash-verified candidate set drawn * from every source clone and (optionally) every store. * * Per dep ref: gather source candidates, optionally extend with store * candidates, filter by the SemVer/SHA pin, pick the highest matching version, * then detect conflicts — divergent sha256 for the same * `(publisher, kind, name, version)` is a hard {@link CanonicalRefConflictError} * unless an `overrides[]` entry pins a source. A cheap `getCanonicalDigest` * cross-check runs when both a source and a store produced a candidate at the * chosen version. * * @param deps - Top-level canonical asset ref strings (`kind:@/name[#pin]`). * @param opts - Provenance index, overrides, stores, and an optional store fetcher. * @returns `ResolveResult` with resolved candidates, missing refs, provenance map, applied overrides. * @docLink packages/core/api-reference#resolve-all */ export declare function resolveAll(deps: string[], opts: ResolveOpts): Promise; /** Coerce a version string to a semver-comparable form (synthetic 0.0.0-sha.X is valid semver). */ export declare function coerceVersion(v: string): string; /** Match a candidate version against a pin (SemVer range, exact, 40-char SHA, or absent). */ export declare function matchPin(pin: string | undefined, version: string): boolean; /** * Status summary for a repository — local HEAD, remote HEAD, and how far behind. * @docLink packages/core/api-reference#repo-status */ export interface RepoStatus { /** Logical name of the repository. */ name: string; /** Whether the repo is local, remote (cloned), or linked (dev override). */ kind: "local" | "remote" | "linked"; /** Current local HEAD commit SHA. */ localHead: string; /** Remote HEAD commit SHA (fetched during status check). */ remoteHead: string; /** Number of commits the local clone is behind the remote. */ behind: number; /** `true` when `behind === 0`. */ upToDate: boolean; /** Absolute path the link points to (linked repos only). */ linkedTo?: string; /** Human-readable error string when the status check failed. */ error?: string; } /** * Check whether a repository clone is up to date with its remote. * Fetches the last 50 commits to compare local HEAD with `origin/`. * * @param decl - Repository declaration from `skaile.yaml` * @param name - Logical repository name * @param reposDir - Project-local source-clone directory (`.skaile/cache/sources/`) * @param projectDir - Optional project root for link lookup * @returns `RepoStatus` describing the current state * @docLink packages/core/api-reference#check-repo-status */ export declare function checkRepoStatus(decl: SourceDeclaration, name: string, reposDir: string, projectDir?: string): RepoStatus; //# sourceMappingURL=repo-manager.d.ts.map