/** * XDG-compliant path resolution for CLEO V2. * * Global data directory is resolved via env-paths (XDG on Linux, platform * conventions on macOS and Windows): * Linux: ~/.local/share/cleo * macOS: ~/Library/Application Support/cleo * Windows: %LOCALAPPDATA%\cleo * * Environment variables: * CLEO_HOME - Override global installation directory * CLEO_DIR - Project data directory (default: .cleo) * * @epic T4454 * @task T4458 */ import { AsyncLocalStorage } from 'node:async_hooks'; /** * Async context payload set by the spawn adapter when launching a subagent * inside a git worktree (ADR-041 §D3). * * @remarks * When `worktreeScope.run(scope, fn)` is active, `getProjectRoot()` returns * `scope.worktreeRoot` instead of walking ancestors. All DB path functions * that delegate to `getProjectRoot()` therefore direct their I/O to the * worktree's `.cleo/` directory, closing the T335 worktree-leak root cause. * * For processes that were spawned with `CLEO_WORKTREE_ROOT` in their * environment (but where AsyncLocalStorage is not in scope), callers should * populate the store via: * ```ts * worktreeScope.run( * { worktreeRoot: process.env.CLEO_WORKTREE_ROOT, projectHash: process.env.CLEO_PROJECT_HASH }, * () => { ... } * ); * ``` * * @task T380 * @public */ export interface WorktreeScope { /** * Absolute path to the worktree directory (value of `CLEO_WORKTREE_ROOT`). */ worktreeRoot: string; /** * Project hash used to scope the worktree under the XDG worktree root * (value of `CLEO_PROJECT_HASH`). */ projectHash: string; } /** * AsyncLocalStorage instance that carries the active {@link WorktreeScope} * for the current async execution context. * * @remarks * Set by the spawn adapter (or any caller that wants to redirect CLEO path * resolution to a worktree directory) before invoking subagent logic. * `getProjectRoot()` checks this store BEFORE the `CLEO_ROOT` env-var and * ancestor-walk, so scoped callers transparently receive the worktree root. * * Callers outside a worktree context receive `undefined` from * `worktreeScope.getStore()` and fall through to the existing resolution * order unchanged. * * @example * ```ts * import { worktreeScope } from '@cleocode/core/paths'; * * worktreeScope.run( * { worktreeRoot: '/path/to/worktree', projectHash: 'abc123' }, * async () => { * const root = getProjectRoot(); // returns '/path/to/worktree' * } * ); * ``` * * @task T380 * @public */ export declare const worktreeScope: AsyncLocalStorage; /** * Run `fn` inside a `worktreeScope.run()` AsyncLocalStorage frame derived * from the `CLEO_WORKTREE_ROOT` and `CLEO_PROJECT_HASH` environment variables. * * This is the env-var → AsyncLocalStorage bridge that lets cleo binaries * spawned from `cleo orchestrate spawn` (which exports those env vars in * the worker shell) honour the orchestrator's authoritative worktree root. * * Without this bridge, `getProjectRoot()`'s ALS check at step 0 always * returns `undefined` for subprocesses, falling through to env-var or * walk-up — which created the rogue `.cleo/` dirs that T1864 was * filed to prevent. * * Belongs in `@cleocode/core` (not `@cleocode/cleo`) per AGENTS.md * Package-Boundary Check: cleo CLI is a thin wrapper; ALS plumbing is * runtime substrate. * * @param fn - Callback to invoke (synchronously or asynchronously) within * the worktree scope. The return value is forwarded to the caller. * @returns The return value of `fn`, whether or not a worktree scope is active. * * @example * ```ts * import { runWithWorktreeScopeFromEnv } from '@cleocode/core/internal'; * * // In the CLI entrypoint: * runWithWorktreeScopeFromEnv(() => runMain(main)); * ``` * * @task T1873 * @related ADR-041 §D3 worktree scope, ADR-055 worktree-by-default */ export declare function runWithWorktreeScopeFromEnv(fn: () => T): T; /** * Check if a CLEO project is initialized at the given root. * Checks for tasks.db. * * @param projectRoot - Absolute path to check; defaults to the resolved project root * @returns True if .cleo/ and tasks.db exist at the given root * * @remarks * A project is considered initialized when both the .cleo/ directory and * the tasks.db SQLite database file are present. * * @example * ```typescript * if (isProjectInitialized('/my/project')) { * console.log('CLEO project found'); * } * ``` */ export declare function isProjectInitialized(projectRoot?: string): boolean; /** * Get the global CLEO home directory. * Respects CLEO_HOME env var; otherwise uses the OS-appropriate data path * via env-paths (XDG_DATA_HOME on Linux, Library/Application Support on macOS, * %LOCALAPPDATA% on Windows). * * @returns Absolute path to the global CLEO data directory * * @remarks * Delegates to `getPlatformPaths().data` which uses the `env-paths` package * for XDG-compliant path resolution across operating systems. * * @example * ```typescript * const home = getCleoHome(); // e.g. "/home/user/.local/share/cleo" * ``` */ export declare function getCleoHome(): string; /** * Get the global CLEO templates directory. * * @returns Absolute path to the templates directory under CLEO home * * @remarks * Returns `{cleoHome}/templates` where CLEO-INJECTION.md and other global * templates are stored. * * @deprecated Compose the SSoT registry entry's `installPath` against * {@link getCleoHome} instead — e.g. * `join(getCleoHome(), getTemplateById('cleo-injection').installPath)` * resolves to the same file via the * {@link import('./templates/registry.js').getTemplateById} registry surface. * T9879 rewired every internal caller; this directory accessor remains as a * back-compat shim and is targeted for removal in v2026.7.0 (see * `.cleo/deprecations.yml`). * * @example * ```typescript * const dir = getCleoTemplatesDir(); // e.g. "/home/user/.local/share/cleo/templates" * ``` */ export declare function getCleoTemplatesDir(): string; /** * Get the global CLEO schemas directory. * * @returns Absolute path to the schemas directory under CLEO home * * @remarks * Returns `{cleoHome}/schemas`. Note that schemas are typically read at * runtime from the npm package root, not this global directory. * * @example * ```typescript * const dir = getCleoSchemasDir(); * ``` */ export declare function getCleoSchemasDir(): string; /** * Get the global CLEO docs directory. * * @returns Absolute path to the docs directory under CLEO home * * @remarks * Returns `{cleoHome}/docs` for global documentation storage. * * @example * ```typescript * const dir = getCleoDocsDir(); * ``` */ export declare function getCleoDocsDir(): string; /** * Get the project CLEO data directory (relative). * Respects CLEO_DIR env var, defaults to ".cleo". * * @param cwd - Optional working directory; when provided, returns absolute path * @returns Relative or absolute path to the project's .cleo directory * * @remarks * If `cwd` is provided, delegates to `getCleoDirAbsolute`. Otherwise returns * the `CLEO_DIR` env var or the default ".cleo" relative path. * * @example * ```typescript * const rel = getCleoDir(); // ".cleo" * const abs = getCleoDir('/project'); // "/project/.cleo" * ``` */ export declare function getCleoDir(cwd?: string): string; /** * Get the absolute path to the project CLEO directory. * * **@deprecated** Since T10297. Use {@link resolveProjectByCwd} + * {@link resolveCanonicalCleoDir} instead. This function remains as a * compatibility shim that delegates to the new resolution chain. * * Migration: * ```typescript * // Before (deprecated): * const cleoDir = getCleoDirAbsolute(cwd); * * // After (T10297): * const project = resolveProjectByCwd(cwd); * if (!project) throw new Error('Not in a CLEO project'); * const cleoDir = resolveCanonicalCleoDir(project.projectId); * ``` * * @param cwd - Optional anchor for project-root resolution; if omitted, uses * the canonical {@link getProjectRoot} chain (worktreeScope > CLEO_ROOT > * CLEO_DIR absolute > gitlink walk > ancestor walk). * @param opts.bootstrap - When `true`, fall back to a cwd-relative `.cleo` * resolution if {@link getProjectRoot} throws. ONLY for `cleo init` callers * that CREATE the project root and therefore cannot rely on ancestor walk. * Defaults to `false` — every other caller MUST resolve through an existing * project root or the error propagates. See council verdict D009 (T9803). * @returns Absolute path to the project's `.cleo` directory * * @remarks * **SSoT for every `.cleo/` path in CLEO** (T9685). All path-derived helpers * (`getTaskPath`, `getConfigPath`, `getSessionsPath`, `getLogPath`, * `getBackupDir`, `getAgentOutputsAbsolute`, `getManifestPath`, etc.) flow * through this function — fixing project-root resolution here cascades to * every consumer. * * **Resolution order** (matches the wider {@link getProjectRoot} chain): * 1. `CLEO_DIR` env var with an absolute value — returned verbatim. * 2. Delegates to {@link resolveProjectByCwd} + {@link resolveCanonicalCleoDir} * for canonical project resolution via `project-info.json` (T10297). * 3. If `cwd` is omitted, resolution runs against `getProjectRoot()` directly. * * **Root-cause fix (T9803 · council verdict D009)**: when `getProjectRoot()` * throws (no project root in scope), the previous implementation silently * fell back to `/.cleo` — which synthesized orphan `.cleo/` directories * inside git worktrees that any subsequent `mkdirSync` call would * materialize. The 25+ leaked `.cleo/` directories inside * `.claude/worktrees/*` documented in the T9801 forensic audit were created * via this path. The fix re-throws unless the caller explicitly passes * `{ bootstrap: true }`, which only `initProject()` (line 737, init.ts) * legitimately needs. * * @example * ```typescript * // Project root → /repo * getCleoDirAbsolute(); // "/repo/.cleo" * getCleoDirAbsolute('/repo/packages/x'); // "/repo/.cleo" (was "/repo/packages/x/.cleo" before T9685) * * // Worktree without a project — THROWS (was silent orphan synthesis pre-T9803) * getCleoDirAbsolute('/tmp/random-dir'); // throws E_NOT_FOUND * * // Bootstrap (cleo init creating a new project) * getCleoDirAbsolute('/tmp/new-project', { bootstrap: true }); // "/tmp/new-project/.cleo" * ``` * * @deprecated Migrate to {@link resolveProjectByCwd} + {@link resolveCanonicalCleoDir} * @task T11009 */ export declare function getCleoDirAbsolute(cwd?: string, opts?: { bootstrap?: boolean; }): string; /** * Resolve the canonical `.cleo` directory for a project given its `projectId`. * * Looks up the project in the global `nexus.db` registry (`project_registry` * table) to find the project's root path, then returns the `.cleo/` directory * under that root. * * This is the core-level wrapper around the `@cleocode/paths` implementation. * Unlike the zero-dep leaf package version (which returns `null`), this wrapper * throws `E_PROJECT_NOT_FOUND` when the projectId is not in the nexus registry, * matching the contract expected by CLEO's higher-level subsystems. * * @param projectId - The canonical registry project ID, or a legacy alias * registered in `project_id_aliases`. * @returns Absolute path to the `.cleo/` directory. * @throws {CleoError} `ExitCode.NOT_FOUND` (`E_PROJECT_NOT_FOUND`) when the * projectId is not found in the nexus registry. * * @public * @task T11018 */ export declare function resolveCanonicalCleoDir(projectId: string): string; /** * Resolve the canonical projectId from a working directory. * * First reads `.cleo/project-info.json` from CWD or an ancestor directory (AC2). * Falls back to nexus registry `project_registry.project_path` match when no * local `project-info.json` is found (AC3). * * @param cwd - Optional working directory. Defaults to `process.cwd()`. * @returns The canonical projectId string (AC4). * @throws {CleoError} `ExitCode.NEXUS_PROJECT_NOT_FOUND` (E_CLEO_NEXUS_PROJECT_NOT_FOUND) * with a remediation hint when no CLEO project is found (AC5). * * @example * ```typescript * const projectId = resolveProjectByCwd('/repo/packages/core'); * // "a1b2c3d4e5f6" * ``` * * @public * @task T11013 */ export declare function resolveProjectByCwd(cwd?: string): string; /** * Resolve the absolute `.cleo/` directory for a working directory — the single * canonical chokepoint for project-scoped path resolution (T11262). * * This replaces the lossy two-step `resolveCanonicalCleoDir(resolveProjectByCwd(cwd))` * pattern, which derived a projectId from `cwd` and then re-resolved that id to * a path **only** through the nexus `project_registry` — throwing for any * project that exists on disk but is not registered (the dominant failure mode * in tests and fresh checkouts). Resolution here derives the `.cleo` directory * **directly**, with one well-defined precedence and no id round-trip: * * 1. Active worktree scope (spawn adapters, T380/ADR-041) — wins over all else. * 2. Absolute `CLEO_DIR` override — pins the `.cleo` dir for the whole process * (test harnesses, `--cleo-dir` CLI). * 3. Nearest ancestor containing a `.cleo/` directory — presence of `.cleo/` * identifies the project root (`/.cleo`); no `project-info.json` or * nexus registration required. Bounded by the `$HOME`/`/` guard. * 4. Cross-project nexus `project_registry` lookup by `cwd` — for callers * whose `cwd` is not under a `.cleo/` tree but is registered. * 5. Throw `E_NO_PROJECT` with a remediation hint. * * @param cwd - Optional working directory. Defaults to `process.cwd()`. * @returns Absolute path to the resolved `.cleo/` directory. * @throws {CleoError} `ExitCode.NEXUS_PROJECT_NOT_FOUND` when no project resolves. * * @public * @task T11262 */ export declare function resolveCleoDir(cwd?: string): string; /** * Non-throwing variant of {@link resolveCleoDir}. * * Resolves the canonical `.cleo/` directory for `cwd`, returning `null` instead * of throwing `E_NO_PROJECT` when no CLEO project can be resolved. Use this for * genuinely-OPTIONAL `.cleo/` lookups — callers that must degrade gracefully * when invoked outside a CLEO project (e.g. loading the optional * `project-context.json` tier of the variable-substitution resolver, or any * best-effort `.cleo`-resident enrichment). For required resolution that should * fail loudly, use {@link resolveCleoDir}. * * Only the terminal `E_NO_PROJECT` (`NEXUS_PROJECT_NOT_FOUND`) is swallowed; * any other error (e.g. a forbidden-walkup guard) still propagates. * * @param cwd - Optional working directory. Defaults to `process.cwd()`. * @returns Absolute path to the resolved `.cleo/` directory, or `null`. * * @public * @task T11280 */ export declare function tryResolveCleoDir(cwd?: string): string | null; /** * Validate that a candidate project root directory is a legitimate CLEO * project root and not a stray parent `.cleo/` directory that happened to * be found by the walk-up algorithm. * * ## Primary path (T1864 — project-info.json contract) * * A candidate is **accepted** when `.cleo/project-info.json` exists and * parses as JSON with a non-empty `projectId` string field. This is the * canonical form written by `cleo init` and is the only form that guarantees * the directory is a proper CLEO project rather than a stray `.cleo/` left by * an old installation or a git worktree that auto-created its own `.cleo/`. * * ## Legacy fallback (backwards-compatibility) * * Projects initialized before `project-info.json` was introduced are still * accepted when **both** of the following are true: * 1. `.cleo/` exists in `candidate` * 2. `.git/` exists as a sibling of `.cleo/` in `candidate` * * A one-time stderr warning is emitted (guarded by `_legacyFallbackWarned`) * so operators know to re-run `cleo init` to upgrade the project metadata. * * **Breaking change vs. prior implementation**: bare `package.json` alone is * no longer sufficient. The old check `existsSync(gitDir) || existsSync(pkgJson)` * accepted any Node.js package directory as a valid project root, which caused * the monorepo-package bug where sub-packages inside `packages/` created their * own empty `.cleo/` databases. * * @param candidate - Absolute path to the directory being considered as the * project root (parent of the `.cleo/` directory). * @returns `true` when the candidate is a recognised CLEO project root. * * @example * ```typescript * // Project root with project-info.json — valid (primary path) * validateProjectRoot('/home/user/myproject'); // true * * // Legacy project root with .git/ but no project-info.json — valid + warning * validateProjectRoot('/home/user/legacy-project'); // true (+ stderr warning) * * // Stray .cleo in home dir with no markers — invalid * validateProjectRoot('/home/user'); // false * ``` * * @task T1463 * @task T1864 */ export declare function validateProjectRoot(candidate: string): boolean; /** * Assert that the given directory is an initialized CLEO project root, * throwing a `CleoError` if it is not. * * This guard MUST be called before any operation that writes into `.cleo/` * (e.g. creating audit directories, session journals) to prevent workers * running inside git worktrees from auto-creating empty `.cleo/` directories * that diverge from the real project database. * * @param projectRoot - Absolute path to the directory that should contain * `.cleo/project-info.json` (or the legacy `.cleo/ + .git/` marker pair). * @throws {CleoError} `ExitCode.CONFIG_ERROR` with code `E_NOT_INITIALIZED` * when `validateProjectRoot(projectRoot)` returns `false`. * * @example * ```typescript * assertProjectInitialized(projectRoot); * await mkdir(join(projectRoot, '.cleo', 'audit'), { recursive: true }); * ``` * * @task T1864 */ export declare function assertProjectInitialized(projectRoot: string): void; /** * Get the project root by walking ancestor directories for `.cleo/` or `.git/`. * * Stops at the **first** ancestor directory that contains either sentinel * directory and never drifts past it — even when multiple nested projects * exist above the starting directory. * * Resolution order: * 1. `CLEO_ROOT` env var — bypasses walk entirely (CI / test override) * 2. `CLEO_DIR` env var (absolute path only) — derives project root from dirname * 3. Walk ancestors from `cwd` (or `process.cwd()`) toward filesystem root: * - `.cleo/` found with a sibling `.git/` or `package.json` → accept as root * - `.cleo/` found but **no** sibling marker → skip (stray/parent `.cleo/`) * - `.git/` found (without `.cleo/` sibling) → throw `E_NOT_INITIALIZED` * 4. Filesystem root reached without a valid root → throw `E_INVALID_PROJECT_ROOT` * (if at least one `.cleo/` was skipped) or `E_NO_PROJECT` (none found) * * @param cwd - Optional starting directory; defaults to `process.cwd()` * @returns Absolute path to the project root directory (parent of `.cleo/`) * @throws {CleoError} `ExitCode.CONFIG_ERROR` (`E_NOT_INITIALIZED`) when a * `.git/` is found but no `.cleo/` is present at that level. * @throws {CleoError} `ExitCode.CONFIG_ERROR` (`E_INVALID_PROJECT_ROOT`) when * one or more `.cleo/` directories are found but none have the required sibling * markers (`.git/` or `package.json`). This prevents accidental operations on * the wrong project when a stray parent `.cleo/` exists higher in the filesystem. * @throws {CleoError} `ExitCode.NOT_FOUND` (`E_NO_PROJECT`) when neither * sentinel is found in any ancestor. * * @remarks * `CLEO_ROOT` is an absolute-path escape hatch for environments where the * working directory is unrelated to the project (CI tmpdirs, monorepo scripts, * test harnesses). When set it is returned as-is without scanning ancestors. * * `CLEO_DIR` set to an absolute path (e.g. `/project/.cleo`) also bypasses * the walk: the project root is derived as its `dirname`. This preserves * backward compatibility for test harnesses that use `CLEO_DIR` to pin the * project root. * * NEVER auto-creates `.cleo/`. Project initialisation is an explicit opt-in * via `cleo init`. * * @example * ```typescript * // Running from packages/core inside the monorepo: * const root = getProjectRoot(); // "/mnt/projects/cleocode" * ``` */ export declare function getProjectRoot(cwd?: string): string; /** * Resolve an optional caller-provided root, falling back to canonical project root. * * Use this in CORE-layer functions that accept an optional `opts.root` (or * `projectRoot`) parameter where the pre-T9580 pattern was * `opts.root ?? process.cwd()`. Centralizing the fallback through this helper * ensures the canonical 5-tier {@link getProjectRoot} chain * (worktreeScope > CLEO_ROOT > CLEO_DIR > gitlink walk-up > ancestor walk) * runs whenever the caller does not pin an explicit root — so an invocation * from a monorepo sub-directory never silently materializes a rogue * `/.cleo/` tree. * * The caller-provided path is trusted as-is (no validation, no normalisation): * orchestrate spawn already hands callers an absolute, canonical root, and * forcing a re-walk would change semantics for explicit overrides. * * @param maybeRoot - Optional absolute path provided by the caller. When it * is a non-empty string it is returned verbatim. When `undefined`, `null`, * or the empty string the helper falls through to {@link getProjectRoot}. * @returns The resolved project root. * * @example * ```ts * // Before (T9580 anti-pattern): * const root = opts.root ?? process.cwd(); * * // After: * const root = resolveOrCwd(opts.root); * ``` * * @task T9584 * @related T9580 audit, T9581, T9582, T9583 */ export declare function resolveOrCwd(maybeRoot?: string | null): string; /** * Resolve a project-relative path to an absolute path. * * @param relativePath - Path to resolve (relative, absolute, or tilde-prefixed) * @param cwd - Optional working directory for project root resolution * @returns Absolute resolved path * * @remarks * Returns absolute paths unchanged. Expands leading tilde (`~/`) to the user's * home directory. Resolves other relative paths against the project root. * * @example * ```typescript * resolveProjectPath('src/index.ts'); // "/project/src/index.ts" * resolveProjectPath('~/notes.md'); // "/home/user/notes.md" * resolveProjectPath('/absolute/path'); // "/absolute/path" * ``` */ export declare function resolveProjectPath(relativePath: string, cwd?: string): string; /** * Get the path to the project's tasks.db file (SQLite database). * @deprecated Use getTaskAccessor() from './store/data-accessor.js' instead. This function * returns the database file path for legacy compatibility, but all task data access * should go through the DataAccessor interface to ensure proper SQLite interaction. * Example: * // OLD (deprecated): * const taskPath = getTaskPath(cwd); * const data = await readJsonFile(taskPath); * // NEW (correct): * const accessor = await getTaskAccessor(cwd); * const data = await accessor.queryTasks({}); * * @param cwd - Optional working directory for path resolution * @returns Absolute path to the tasks.db file * * @remarks * Returns `{cleoDir}/tasks.db`. Prefer `getTaskAccessor()` for actual data access. * * @example * ```typescript * const dbPath = getTaskPath('/project'); * ``` * * @task T11009 — rewired to _resolveCleoDir (T10297 projectId-based resolution) */ export declare function getTaskPath(cwd?: string): string; /** * Get the path to the project's config.json file. * * @param cwd - Optional working directory for path resolution * @returns Absolute path to the project config.json * * @remarks * Returns `{cleoDir}/config.json`. * * @example * ```typescript * const configPath = getConfigPath('/project'); * ``` */ export declare function getConfigPath(cwd?: string): string; /** * Get the path to the project's sessions.json file. * * @param cwd - Optional working directory for path resolution * @returns Absolute path to the sessions.json file * * @remarks * Returns `{cleoDir}/sessions.json`. * * @example * ```typescript * const sessionsPath = getSessionsPath('/project'); * ``` */ export declare function getSessionsPath(cwd?: string): string; /** * Get the path to the project's archive file. * * @param cwd - Optional working directory for path resolution * @returns Absolute path to the tasks-archive.json file * * @remarks * Returns `{cleoDir}/tasks-archive.json` where archived tasks are stored. * * @example * ```typescript * const archivePath = getArchivePath('/project'); * ``` */ export declare function getArchivePath(cwd?: string): string; /** * Get the path to the project's log file. * Canonical structured runtime log path (pino). * * @param cwd - Optional working directory for path resolution * @returns Absolute path to the cleo.log file * * @remarks * Returns `{cleoDir}/logs/cleo.log`. Used by pino for structured JSON logging. * * @example * ```typescript * const logPath = getLogPath('/project'); * ``` * * @task T4644 */ export declare function getLogPath(cwd?: string): string; /** * Get the backup directory for operational backups. * * @param cwd - Optional working directory for path resolution * @returns Absolute path to the operational backups directory * * @remarks * Returns `{cleoDir}/backups/operational`. * * @example * ```typescript * const backupDir = getBackupDir('/project'); * ``` */ export declare function getBackupDir(cwd?: string): string; /** * Get the global config file path. * * @returns Absolute path to the global config.json in CLEO home * * @remarks * Returns `{cleoHome}/config.json` for global CLEO configuration. * * @example * ```typescript * const globalConfig = getGlobalConfigPath(); * ``` */ export declare function getGlobalConfigPath(): string; /** * Get the Global Justfile Hub directory. * * The hub stores cross-project recipe libraries agents can run in ANY project * (cleo-bootstrap, rcasd-init, schema-validate, lint-standard). Both humans * (via editor) and the meta Cleo Chef Agent write recipes here. * * @returns Absolute path to the global-recipes directory under CLEO home * * @remarks * Returns `{cleoHome}/global-recipes`. Created by `ensureGlobalHome()`. * * @example * ```typescript * const dir = getCleoGlobalRecipesDir(); * // Linux: "/home/user/.local/share/cleo/global-recipes" * ``` */ export declare function getCleoGlobalRecipesDir(): string; /** * Get the absolute path to the primary global justfile. * * @returns Absolute path to `{cleoHome}/global-recipes/justfile` * * @remarks * This is the single-file entry point for the Justfile Hub. Additional * domain-specific justfiles live alongside it in the same directory. * * @example * ```typescript * const path = getCleoGlobalJustfilePath(); * ``` */ export declare function getCleoGlobalJustfilePath(): string; /** * Get the Global Pi Extensions Hub directory. * * Houses the Pi extensions that drive the CleoOS UI and tools: * orchestrator.ts (Conductor Loop), project-manager.ts (TUI dashboard), * tilldone.ts (work visualization), cant-bridge.ts (CANT runtime), * stage-guide.ts (before_agent_start hook). * * @returns Absolute path to the pi-extensions directory under CLEO home * * @remarks * Returns `{cleoHome}/pi-extensions`. Pi is configured to load extensions * from this directory via settings.json or the PI extension path setting. * * @example * ```typescript * const dir = getCleoPiExtensionsDir(); * // Linux: "/home/user/.local/share/cleo/pi-extensions" * ``` */ export declare function getCleoPiExtensionsDir(): string; /** * Get the Global CANT Workflows Hub directory. * * Stores compiled and parsed `.cant` workflows that agents can invoke * globally across projects. Project-local agents still live in `.cleo/agents/`. * * @returns Absolute path to the cant-workflows directory under CLEO home * * @remarks * Returns `{cleoHome}/cant-workflows`. Used by the CANT runtime bridge * to resolve globally-available workflow definitions. * * @example * ```typescript * const dir = getCleoCantWorkflowsDir(); * ``` */ export declare function getCleoCantWorkflowsDir(): string; /** * Get the Global CLEO Agents directory. * * Holds globally-available CANT agent definitions (`.cant` files). * Project-local agents still live in `{projectRoot}/.cleo/agents/`. * * @returns Absolute path to the agents directory under CLEO home * * @remarks * Returns `{cleoHome}/agents`. Loaded when `cleo agent start ` resolves * agent IDs that aren't found in the project-local registry. * * @example * ```typescript * const dir = getCleoGlobalAgentsDir(); * ``` */ export declare function getCleoGlobalAgentsDir(): string; /** * Get the Global CLEO CANT Agents directory. * * Holds globally-available `.cant` persona files seeded from * `@cleocode/agents/seed-agents/` during postinstall. `cleo agent start ` * and `cleo orchestrate spawn` resolve agent IDs that aren't found in the * project-local registry against this directory. * * Project-local CANT agents still live in `{projectRoot}/.cleo/cant/agents/`. * * @returns Absolute path to the `cant/agents` directory under CLEO home * * @remarks * Returns `{cleoHome}/cant/agents` — e.g. `~/.local/share/cleo/cant/agents` * on Linux. This is the target of both the npm postinstall seed hook (W2-5) * and the `cleo agent install --global` CLI command. * * @example * ```typescript * const dir = getCleoGlobalCantAgentsDir(); * // Linux: "/home/user/.local/share/cleo/cant/agents" * ``` * * @task T889 / T897 / W2-5 */ export declare function getCleoGlobalCantAgentsDir(): string; /** * Get the agent outputs directory (relative path) from config or default. * * Config lookup priority: * 1. config.agentOutputs.directory * 2. config.research.outputDir (deprecated) * 3. config.directories.agentOutputs (deprecated) * 4. Default: '.cleo/agent-outputs' * * @param cwd - Optional working directory for path resolution * @returns Relative or absolute path to the agent outputs directory * * @remarks * Checks config fields in priority order: `agentOutputs.directory`, `research.outputDir`, * `directories.agentOutputs`. Falls back to `.cleo/agent-outputs`. * * @example * ```typescript * const dir = getAgentOutputsDir('/project'); * ``` * * @task T4700 */ export declare function getAgentOutputsDir(cwd?: string): string; /** * Get the absolute path to the agent outputs directory. * * @param cwd - Optional working directory for path resolution * @returns Absolute path to the agent outputs directory * * @remarks * Resolves the output of `getAgentOutputsDir()` against the project root * if it is not already absolute. * * @example * ```typescript * const absDir = getAgentOutputsAbsolute('/project'); * ``` * * @task T4700 */ export declare function getAgentOutputsAbsolute(cwd?: string): string; /** * Get the absolute path to the legacy agent-outputs file. * @deprecated The flat-file manifest is retired per ADR-027. Use pipeline_manifest via `cleo manifest` CLI. * * Checks config.agentOutputs.manifestFile for custom filename, * defaults to the legacy filename. * * @param cwd - Optional working directory for path resolution * @returns Absolute path to the legacy agent-outputs manifest file * * @remarks * Checks `config.agentOutputs.manifestFile` for a custom filename, * defaults to the legacy filename in the agent outputs directory. * * @example * ```typescript * const manifestPath = getManifestPath('/project'); * ``` * * @task T4700 */ export declare function getManifestPath(cwd?: string): string; /** * Get the absolute path to the MANIFEST.archive.jsonl file. * * @param cwd - Optional working directory for path resolution * @returns Absolute path to the MANIFEST.archive.jsonl file * * @remarks * Returns the archive manifest path in the agent outputs directory. * * @example * ```typescript * const archivePath = getManifestArchivePath('/project'); * ``` * * @task T4700 */ export declare function getManifestArchivePath(cwd?: string): string; /** * Check if a path is absolute (POSIX or Windows). * * @param path - Filesystem path to check * @returns True if the path is absolute on any supported OS * * @remarks * Recognizes POSIX absolute paths (`/...`), Windows drive letters (`C:\...`), * and UNC paths (`\\...`). * * @example * ```typescript * isAbsolutePath('/usr/bin'); // true * isAbsolutePath('C:\\Users'); // true * isAbsolutePath('./relative'); // false * ``` */ export declare function isAbsolutePath(path: string): boolean; /** * Get the OS log directory for CLEO global logs. * Linux: ~/.local/state/cleo | macOS: ~/Library/Logs/cleo | Windows: %LOCALAPPDATA%\cleo\Log * * @returns Absolute path to the OS-appropriate log directory * * @remarks * Delegates to `getPlatformPaths().log` for XDG-compliant resolution. * * @example * ```typescript * const logDir = getCleoLogDir(); * ``` */ export declare function getCleoLogDir(): string; /** * Get the OS cache directory for CLEO. * Linux: ~/.cache/cleo | macOS: ~/Library/Caches/cleo | Windows: %LOCALAPPDATA%\cleo\Cache * * @returns Absolute path to the OS-appropriate cache directory * * @remarks * Delegates to `getPlatformPaths().cache` for XDG-compliant resolution. * * @example * ```typescript * const cacheDir = getCleoCacheDir(); * ``` */ export declare function getCleoCacheDir(): string; /** * Get the OS temp directory for CLEO ephemeral files. * * @returns Absolute path to the OS-appropriate temp directory * * @remarks * Delegates to `getPlatformPaths().temp` for platform-specific resolution. * * @example * ```typescript * const tempDir = getCleoTempDir(); * ``` */ export declare function getCleoTempDir(): string; /** * Get the OS config directory for CLEO. * Linux: ~/.config/cleo | macOS: ~/Library/Preferences/cleo | Windows: %APPDATA%\cleo\Config * * @returns Absolute path to the OS-appropriate config directory * * @remarks * Delegates to `getPlatformPaths().config` for XDG-compliant resolution. * * @example * ```typescript * const configDir = getCleoConfigDir(); * ``` */ export declare function getCleoConfigDir(): string; /** * Get the CLEO templates directory as a tilde-prefixed path for use * in `@` references (AGENTS.md, CLAUDE.md, etc.). Cross-platform: * replaces the user's home directory with `~` so the reference works * when loaded by LLM providers that resolve `~` at runtime. * * Linux: ~/.local/share/cleo/templates * macOS: ~/Library/Application Support/cleo/templates * Windows: ~/AppData/Local/cleo/Data/templates (approximate) * * @returns Tilde-prefixed path like "~/.local/share/cleo/templates" * * @remarks * Returns the absolute path if the home directory is not a prefix * (unlikely but handled). Always uses forward slashes after the tilde * for cross-platform compatibility in `@`-reference resolution. * * @example * ```typescript * const tildePath = getCleoTemplatesTildePath(); * // "~/.local/share/cleo/templates" * ``` */ export declare function getCleoTemplatesTildePath(): string; /** * Get the CLEO templates directory as a stable tilde-prefixed path for use in * `@`-references written into shared files (e.g. `~/.agents/AGENTS.md`). * * Unlike {@link getCleoTemplatesTildePath}, this function is immune to * `CLEO_HOME` overrides. It always returns `"~/.cleo/templates"` — the stable * symlink path that resolves to the OS-appropriate canonical data directory at * runtime. Use this when writing template references into files that persist * across sessions to prevent test environments from polluting shared files * with stale temp-path blocks (T9020 / T1929). * * @returns `"~/.cleo/templates"` on all platforms * * @example * ```typescript * const ref = `@${getCanonicalTemplatesTildePath()}/CLEO-INJECTION.md`; * // "@~/.cleo/templates/CLEO-INJECTION.md" * ``` */ export declare function getCanonicalTemplatesTildePath(): string; /** * Get the global agents hub directory. * Respects AGENTS_HOME env var, defaults to ~/.agents. * * @returns Absolute path to the agents hub directory * * @remarks * Returns `AGENTS_HOME` env var if set, otherwise `~/.agents`. * * @example * ```typescript * const agentsHome = getAgentsHome(); // "/home/user/.agents" * ``` */ export declare function getAgentsHome(): string; /** * Get the Claude Code agents directory (~/.claude/agents by default). * * @returns Absolute path to the Claude agents directory * * @remarks * Respects `CLAUDE_HOME` env var for the parent directory. * * @example * ```typescript * const dir = getClaudeAgentsDir(); * ``` * * @deprecated Use AdapterPathProvider.getAgentInstallDir() from the active adapter instead. */ export declare function getClaudeAgentsDir(): string; /** * Get the claude-mem SQLite database path. * * @returns Absolute path to the claude-mem.db file * * @remarks * Respects `CLAUDE_MEM_DB` env var, defaults to `~/.claude-mem/claude-mem.db`. * This is a third-party tool path; homedir() is correct here (no env-paths standard). * * @example * ```typescript * const dbPath = getClaudeMemDbPath(); * ``` * * @deprecated Use AdapterPathProvider.getMemoryDbPath() from the active adapter instead. */ export declare function getClaudeMemDbPath(): string; /** * Result of {@link resolveWorktreeRouting}. * * Encodes the canonical project root + caller cwd + whether routing kicked in * so CLI verbs can emit a single, consistent log line and pre-resolve file * paths against the worktree's cwd before dispatch reaches the canonical-root- * anchored sanitizer. * * @public * @task T10389 */ export interface WorktreeRouting { /** Caller's `process.cwd()` (or the override passed to the helper). */ readonly cwd: string; /** Canonical project root as returned by {@link getProjectRoot}. */ readonly canonicalRoot: string; /** * True when `cwd` is inside a git worktree whose canonical root resolves to * a different directory (i.e. `.git` is a gitlink FILE pointing back to a * different repo). * * False when running directly from the canonical project root OR from a * subdirectory that walks up to the same project root. */ readonly isWorktree: boolean; /** * Absolute path to the worktree directory when `isWorktree` is true, * otherwise `undefined`. Equal to the closest ancestor of `cwd` that * carries `.git` as a FILE (the gitlink). */ readonly worktreePath?: string; } /** * Detect whether the caller is operating from inside a git worktree whose * canonical project root resolves to a DIFFERENT directory than `cwd`. * * The resolver walks ancestors from `cwd` looking for `.git`. When `.git` is * a FILE it is a worktree gitlink; the canonical root is the main repo (the * resolver in {@link getProjectRoot} already parses this case correctly). * * Used by CLI verbs that need to resolve user-supplied file paths against * the worktree's cwd (not the canonical root) BEFORE dispatching to the * sanitizer middleware, which enforces canonical-root anchoring. * * @param cwdOverride - Override for `process.cwd()`; used by tests. * * @returns A {@link WorktreeRouting} envelope describing the detected routing. * * @public * @task T10389 */ export declare function resolveWorktreeRouting(cwdOverride?: string): WorktreeRouting; /** * Resolve a user-supplied file path against the calling worktree's cwd when * applicable, returning an absolute path suitable for I/O. * * Behaviour: * - When the caller is NOT in a worktree (or `routing` is not provided), * returns `resolve(filePath)` (the historic behaviour). * - When the caller IS in a worktree, resolves relative paths against the * worktree's cwd (`routing.cwd`). Absolute paths pass through unchanged. * * The returned absolute path may be OUTSIDE the canonical project root — that * is the whole point of worktree routing. Callers that pass this path through * the dispatch sanitizer MUST exempt the operation (see * `packages/core/src/security/input-sanitization.ts` `allowExternalPath`). * * @param filePath - Raw file path as supplied by the user. * @param routing - Detected routing envelope (from {@link resolveWorktreeRouting}). * * @returns Absolute file path resolved against the worktree's cwd. * * @public * @task T10389 */ export declare function resolveWorktreeFilePath(filePath: string, routing: WorktreeRouting): string; /** * Detect a stray `.cleo/tasks.db` SQLite handle inside a worktree directory. * * Background: CLEO worktrees are advised to keep their `.cleo/` empty so * every DB open routes through `openCleoDb()` against the canonical project * root (T9806 / ADR-068). A leaked `.cleo/tasks.db` inside a worktree * indicates a pre-T9803 install OR a misbehaving agent that wrote to the * worktree instead of routing to the canonical root. * * When this helper detects a stray DB, CLI verbs should emit an actionable * error BEFORE invoking the dispatch chain so the user sees clear * remediation steps rather than the lower-level `E_WT_DB_ISOLATION_VIOLATION` * thrown from inside the DB chokepoint. * * @param routing - Detected routing envelope (from {@link resolveWorktreeRouting}). * * @returns The absolute path to the stray `tasks.db` when present, otherwise * `undefined`. The presence of `.cleo/` alone (without `tasks.db`) is NOT * a stray — intentional caches may legitimately live there. * * @public * @task T10389 */ export declare function detectStrayCleoDb(routing: WorktreeRouting): string | undefined; /** A lightweight snapshot of a project_registry row. */ export interface ProjectRegistryEntry { projectId: string; projectRoot: string; projectHash: string; name: string; registeredAt: string; lastSeen: string; healthStatus: string; permissions: string; taskCount: number; } /** Look up a project by canonical or legacy projectId (AC1, AC4). */ export declare function resolveProjectById(projectId: string): Promise; /** Auto-register a project on first encounter (AC2, AC3, AC5, AC6). */ export declare function registerProjectOnEncounter(projectRoot: string, infoProjectId: string): Promise; //# sourceMappingURL=paths.d.ts.map