/** * Squad directory resolution — walk-up and global path algorithms. * * resolveSquad() — find .squad/ by walking up from startDir to .git boundary * resolveSquadPaths() — dual-root resolution (projectDir / teamDir) for remote squad mode * resolveGlobalSquadPath() — platform-specific global config directory * * Dual-root resolution and remote mode design ported from @spboyer (Shayne Boyer)'s * PR bradygaster/squad#131. Original concept: resolveSquadPaths() with config.json * pointer for team identity separation. * * @module resolution */ import { type StateBackend, type StateBackendType } from './state-backend.js'; import type { StorageProvider } from './storage/storage-provider.js'; /** * Clear all in-process caches used by `resolveSquad()` and `findSquadDir()`. * * Call this from any command or test that creates, moves, or deletes a * `.squad/` (or `.ai-team/`) directory so subsequent resolution calls in * the same process observe the fresh filesystem state immediately, * instead of waiting up to {@link RESOLVE_CACHE_TTL_MS} ms for the TTL * to expire. * * Examples of callers that should invoke this: * - `squad init` — creates `.squad/` in the project root * - `squad link` — points an existing checkout at a remote team root * - `squad upgrade` — may regenerate `.squad/` layout * - Test fixtures that scaffold or tear down temporary `.squad/` dirs * * Safe to call when the cache is disabled (no-op). */ export declare function clearResolveSquadCache(): void; /** * Schema for `.squad/config.json` — controls remote squad mode. * Named SquadDirConfig to avoid collision with the runtime SquadConfig. */ export interface SquadDirConfig { version: number; teamRoot: string; projectKey: string | null; /** True when in consult mode (personal squad consulting on external project) */ consult?: boolean; /** True when extraction is disabled for consult sessions (read-only consultation) */ extractionDisabled?: boolean; /** Where state is stored: 'external' when moved out of the working tree */ stateLocation?: string; /** State storage backend: local | external | git-notes | orphan */ stateBackend?: string; } /** * Resolved paths for dual-root squad mode. * * In **local** mode, projectDir and teamDir point to the same `.squad/` directory. * In **remote** mode, config.json specifies a `teamRoot` that resolves to a * separate directory for team identity (agents, casting, skills). */ export interface ResolvedSquadPaths { mode: 'local' | 'remote'; /** Project-local .squad/ (decisions, logs) */ projectDir: string; /** Team identity root (agents, casting, skills) */ teamDir: string; /** User's personal squad dir, null if not found or disabled */ personalDir: string | null; config: SquadDirConfig | null; name: '.squad' | '.ai-team'; isLegacy: boolean; } /** * Walk up the directory tree from `startDir` looking for a `.squad/` directory. * * Stops at the repository root (the directory containing `.git` as a directory). * When `.git` is a **file** (git worktree), falls back to the main checkout strategy: * reads the `gitdir:` pointer, resolves the main checkout path, and checks there. * Returns the **absolute path** to the `.squad/` directory, or `null` if none is found. * * Resolution order (worktree-local strategy first, main-checkout strategy second): * 1. Walk up from `startDir` checking for `.squad/` — stops at `.git` directory boundary * 2. If `.git` is a file (worktree), check the main checkout for `.squad/` * * @param startDir - Directory to start searching from. Defaults to `process.cwd()`. * @returns Absolute path to `.squad/` or `null`. */ export declare function resolveSquad(startDir?: string): string | null; /** * Try to read and parse `.squad/config.json` (or `.ai-team/config.json`). * Returns null for missing file, unreadable file, or malformed JSON. */ export declare function loadDirConfig(squadDir: string): SquadDirConfig | null; /** * Check if a config represents consult mode (personal squad consulting on external project). */ export declare function isConsultMode(config: SquadDirConfig | null): boolean; /** * Resolve dual-root squad paths (projectDir / teamDir). * * - Walks up from `startDir` looking for `.squad/` (or `.ai-team/` for legacy repos). * - If `.squad/config.json` exists with a valid `teamRoot` → **remote** mode: * teamDir is resolved relative to the **project root** (parent of .squad/). * - Otherwise → **local** mode: projectDir === teamDir. * * @param startDir - Directory to start searching from. Defaults to `process.cwd()`. * @returns Resolved paths, or `null` if no squad directory is found. */ export declare function resolveSquadPaths(startDir?: string): ResolvedSquadPaths | null; /** * Return the platform-specific global Squad configuration directory. * * | Platform | Path | * |----------|--------------------------------------------| * | Windows | `%APPDATA%/squad/` | * | macOS | `~/Library/Application Support/squad/` | * | Linux | `$XDG_CONFIG_HOME/squad/` (default `~/.config/squad/`) | * * The directory is created (recursively) if it does not already exist. * * @returns Absolute path to the global squad config directory. */ export declare function resolveGlobalSquadPath(): string; /** * Resolves the user's personal squad directory. * Returns null if SQUAD_NO_PERSONAL is set or directory doesn't exist. * * Platform paths: * - Windows: %APPDATA%/squad/personal-squad * - macOS: ~/Library/Application Support/squad/personal-squad * - Linux: $XDG_CONFIG_HOME/squad/personal-squad or ~/.config/squad/personal-squad */ export declare function resolvePersonalSquadDir(): string | null; /** * Ensure the user's personal squad directory exists with the expected structure. * Creates `personal-squad/agents/` and `personal-squad/config.json` if missing. * * Idempotent — safe to call multiple times. * * @returns Absolute path to the personal squad directory. */ export declare function ensurePersonalSquadDir(): string; /** * Validate that a file path is within `.squad/` or the system temp directory. * * Use this guard before writing any scratch/temp/state files to ensure Squad * never clutters the repo root or arbitrary filesystem locations. * * @param filePath - Absolute path to validate. * @param squadRoot - Absolute path to the `.squad/` directory (e.g. from `resolveSquad()`). * @returns The resolved absolute `filePath` if it is safe. * @throws If `filePath` is outside `.squad/` and not in the system temp directory. */ export declare function ensureSquadPath(filePath: string, squadRoot: string): string; /** * Validate that a file path is within either the projectDir or teamDir * (or the system temp directory). For use in dual-root / remote mode. * * @param filePath - Absolute path to validate. * @param projectDir - Absolute path to the project-local .squad/ directory. * @param teamDir - Absolute path to the team identity directory. * @returns The resolved absolute filePath if it is safe. * @throws If filePath is outside both roots and not in the system temp directory. */ export declare function ensureSquadPathDual(filePath: string, projectDir: string, teamDir: string): string; /** * Validates a file path is inside one of three allowed directories: * projectDir, teamDir, personalDir, or system temp. * Extends ensureSquadPathDual() for triple-root (project + team + personal). */ export declare function ensureSquadPathTriple(filePath: string, projectDir: string, teamDir: string, personalDir: string | null): string; /** * ensureSquadPath that works with resolved dual-root paths. * Convenience wrapper around ensureSquadPathDual. */ export declare function ensureSquadPathResolved(filePath: string, paths: ResolvedSquadPaths): string; /** * Resolve the scratch directory for temporary files. * * Returns `{squadRoot}/.scratch/` — the canonical location for ephemeral files * that Squad and its agents create during operations (prompt files, intermediate * processing artifacts, commit message drafts, etc.). * * If `create` is true (default), the directory is created if it does not exist. * * @param squadRoot - Absolute path to the `.squad/` directory. * @param create - Whether to create the directory if missing (default: true). * @returns Absolute path to the scratch directory. */ export declare function scratchDir(squadRoot: string, create?: boolean): string; /** * Return a unique file path inside the scratch directory. * * Writes content to the file if `content` is provided; otherwise returns * the path only and the caller is responsible for writing to it. * The caller is also responsible for deleting the file when done * (or relying on the cleanup capability). * * @param squadRoot - Absolute path to the `.squad/` directory. * @param prefix - Filename prefix (e.g. `"fleet-prompt"`). * @param ext - File extension including dot (e.g. `".txt"`). Defaults to `".tmp"`. * @param content - Optional content to write immediately. * @returns Absolute path to the temp file. */ export declare function scratchFile(squadRoot: string, prefix: string, ext?: string, content?: string): string; /** * Derive a stable project key from a project directory path. * * Takes the basename of the path, lowercases it, and replaces unsafe characters * with dashes. Returns `'unknown-project'` if the basename is empty (e.g., * filesystem root). * * @param projectDir - Absolute path to the project root. * @returns A sanitized, lowercase project key suitable for use as a directory name. */ export declare function deriveProjectKey(projectDir: string): string; /** * Resolve the external state directory for a project. * * Returns `{globalDir}/projects/{sanitizedKey}/` where `globalDir` is the * platform-specific global config directory (e.g., `%APPDATA%/squad` on Windows, * `~/Library/Application Support/squad` on macOS, `$XDG_CONFIG_HOME/squad` or * `~/.config/squad` on Linux). * * Validates the project key to prevent path traversal. Throws if the key * is empty or contains `..` sequences. * * @param projectKey - The project key (from deriveProjectKey or user-supplied). * @param create - Whether to create the directory if it doesn't exist (default: true). * @returns Absolute path to the project's external state directory. * @throws If projectKey is empty or contains path traversal sequences. */ export declare function resolveExternalStateDir(projectKey: string, create?: boolean): string; /** * Resolve the squad home directory — a roaming squad root for personal agents * and presets that follows the user across machines. * * Resolution order: * 1. `SQUAD_HOME` env var (explicit override, e.g. a synced folder) * 2. `~/.squad/` (conventional default — user's home dir) * * Unlike `resolveGlobalSquadPath()` (which returns platform-specific app config), * squad home is a **squad root** — it can contain `agents/`, `presets/`, etc. * * @param create - Whether to create the directory if missing (default: false). * @returns Absolute path to the squad home directory, or null if it doesn't * exist and `create` is false. */ export declare function resolveSquadHome(create?: boolean): string | null; /** * Ensure the squad home directory exists with standard structure. * Creates `agents/` and `presets/` subdirectories. * * Idempotent — safe to call multiple times. * * @returns Absolute path to the squad home directory. */ export declare function ensureSquadHome(): string; /** * Resolve the presets directory within squad home. * * @returns Absolute path to `/presets/`, or null if squad home * doesn't exist. */ export declare function resolvePresetsDir(): string | null; /** * Resolved state context for a squad session. * * Combines the resolved paths with the active state backend. Commands * and SDK functions that need state I/O use this context instead of * directly instantiating an FSStorageProvider. * * **Boundary:** Only mutable squad state flows through the backend. * Bootstrap artifacts (config.json, team.md structure checks) stay on * the local filesystem because they are needed before a backend can be * resolved. */ export interface SquadStateContext { /** Dual-root resolved paths (projectDir, teamDir, etc.) */ paths: ResolvedSquadPaths; /** The active state backend (local, git-notes, or orphan) */ backend: StateBackend; /** The repo root directory (for git-native backends) */ repoRoot: string; /** StorageProvider backed by the active state backend — pass to SDK modules */ storage: StorageProvider; } /** * Resolve the full squad state context: paths + state backend. * * Call once at command entry and thread the context through to SDK functions. * This ensures the configured state backend (local, git-notes, orphan) * applies to all squad operations — not just the watch command. * * @param startDir - Directory to start searching from. Defaults to cwd. * @param cliOverride - CLI flag override for state backend type. * @returns Resolved context, or null if no squad directory is found. */ export declare function resolveSquadState(startDir?: string, cliOverride?: StateBackendType): SquadStateContext | null; //# sourceMappingURL=resolution.d.ts.map