export declare const SPEC_WORKFLOW_SHARED_ROOT_ENV = "SPEC_WORKFLOW_SHARED_ROOT"; export declare const SPEC_WORKFLOW_WORKSPACE_ENV = "SPEC_WORKFLOW_WORKSPACE"; /** * Environment variables that tell git where to look, removed from **every** git * invocation in this module (requirement 1.9). * * An inherited `GIT_DIR` makes `git rev-parse --git-common-dir` succeed from any * directory — including one that is not a repository at all — and return that * value, so two unrelated paths compare equal and workspace inference adopts an * unrelated directory. The variables are reachable from a git hook, from * `git rebase --exec`, and from any parent process that exported them. * * Scrubbing changes the `git --git-dir=$HOME/.dotfiles --work-tree=$HOME` * pattern, where the exported variables *are* the configuration: resolution * sees the directory's own repository instead. That is the intended answer here. */ export declare const SCRUBBED_GIT_ENV_VARS: readonly ["GIT_DIR", "GIT_COMMON_DIR", "GIT_WORK_TREE", "GIT_INDEX_FILE"]; /** * A copy of the environment with the four {@link SCRUBBED_GIT_ENV_VARS} * removed, for any process that runs git or that hands its environment to a * child which will (requirement 2.12). * * The copy is taken from the live `process.env` on every call rather than * snapshotted at module load, so a variable exported after this module was * imported is still scrubbed. Everything else — including * `SPEC_WORKFLOW_HOST_PATH_PREFIX` and `SPEC_WORKFLOW_CONTAINER_PATH_PREFIX`, * which Docker path translation needs (requirement 2.13) — is preserved. */ export declare function scrubbedGitEnv(): NodeJS.ProcessEnv; /** * Absolute, `realpath`-normalized form of a path, for use as an identity key. * * Behaviour: `realpath(resolve(p))`. Relative input is resolved against the * process working directory first, so the result is always absolute. * * Fallback: when `realpath` fails — the directory does not exist, as after * `git worktree remove`, or is unreadable — the un-normalized absolute path * `resolve(p)` is returned (requirement 1.12). * * What the fallback guarantees is determinism, not stability across a change to * the filesystem: two computations of the same input agree, but a value * computed while the path resolved need not equal one computed after it stopped * resolving. Concretely, if a symlinked component of `p` is removed between the * two calls, the earlier call returns the physical `/real/child` and the later * one the link spelling `/link/child`, and they do not match. An identifier * therefore must not be recomputed from a path that may have gone away since — * requirement 1.13 unregisters a project by the identifier cached at * registration for exactly this reason. * * `onFallback` is the logging seam for requirement 1.12. This function does not * log on its own: it runs on read paths the dashboard exercises per request, so * an unconditional `console.error` here would emit a line per request for every * registry entry whose directory has gone away. The caller that has an identity * at stake supplies the sink and decides how to rate-limit it — see * `logIdentityFallback` in `project-registry.ts`. Whether it is supplied or not * changes nothing about the returned value. */ export declare function normalizeIdentityPath(p: string, onFallback?: (error: unknown, absolutePath: string) => void): string; /** * Absolute, `realpath`-normalized git common directory for `cwd`, or null when * `cwd` is not inside a git repository or git is unavailable. * * This is exactly `realpath(resolve(cwd, raw))` — no component is stripped. * `resolve` handles every form git emits: bare `.git` at a repository root, * `../..`-repeated to depth from a subdirectory, an absolute `
/.git` from * a linked worktree, `/.git/modules/` from a submodule, `.` from a * bare repository or from inside a `.git` directory, and an arbitrary path * under `--separate-git-dir`. The `realpath` is what makes a repository reached * through a symlinked path compare equal to its own linked worktree * (requirement 1.6). * * The `.git`-stripping normalization inside {@link resolveGitRoot} is * deliberately **not** reused: it returns the repository *root*, which makes a * submodule compare equal to its superproject (requirement 1.5), and produces a * nonexistent path under `--separate-git-dir`, whose common directory contains * no `.git` segment at all. */ export declare function gitCommonDirAbsolute(cwd: string): string | null; /** * Absolute, `realpath`-normalized git top-level directory for `cwd`, or null * when the command fails. * * It fails — and this is the point (requirement 1.7) — in a bare repository and * from inside a `.git` directory, both of which still report a usable common * directory. Callers gating workspace inference must require a non-null result * on both sides; otherwise those failures fall back to their inputs, satisfy a * "differing toplevels" precondition, and inference adopts a directory that is * not a checkout. */ export declare function gitTopLevel(cwd: string): string | null; /** * True only when both paths are inside a git repository and it is the same one. * * Returns false when either side resolves to no common directory, so two * directories that are not in any repository never compare equal * (requirement 1.4). Common directories are compared **directly**, never a * parent derived from them, which is what keeps a submodule distinct from its * superproject (requirement 1.5). * * This predicate does not gate on `git rev-parse --show-toplevel`; that is a * separate, required gate the caller applies (requirement 1.7, see * {@link gitTopLevel}). */ export declare function sameRepository(a: string, b: string): boolean; /** * Resolves the git workspace root directory. * For repositories and worktrees, this returns the top-level checked-out directory. * * @param projectPath - Any path inside the workspace * @returns Workspace root path, or original path when git is unavailable */ export declare function resolveGitWorkspaceRoot(projectPath: string): string; /** * Resolves the git root directory for storing shared specs. * In worktrees, this returns the main repository path so all worktrees share specs. * * @param projectPath - The current project/worktree path * @returns The resolved path (main repo for worktrees, or original path) */ export declare function resolveGitRoot(projectPath: string): string; /** * Checks if the current directory is a git worktree (not the main repo). * * @param projectPath - The path to check * @returns true if in a worktree, false if main repo or not a git repo */ export declare function isGitWorktree(projectPath: string): boolean; /** How the workspace path was chosen, in precedence order (requirement 2.3). */ export type WorkspaceSource = 'env' | 'flag' | 'inference' | 'argument'; export interface ResolvedRoots { /** Where **code** lives: the diff, the typecheck, and spawned agents. */ workspacePath: string; /** Where `.spec-workflow` lives; shared across a repository's worktrees. */ workflowRootPath: string; source: WorkspaceSource; } export interface WorkspaceRootsOptions { /** The CLI path argument, tilde-expanded; defaults to the process cwd upstream. */ configuredPath: string; /** The launch directory, i.e. `process.cwd()`. */ cwd: string; /** Dashboard-only mode: no workspace of its own (requirements 1.14, 2.11). */ dashboardMode: boolean; /** `--no-workspace-inference` (requirement 1.15). */ noInference: boolean; /** `--no-shared-worktree-specs` (requirement 2.8). */ noSharedWorktreeSpecs: boolean; } /** * The one decision point for workspace identity and the shared workflow root. * * **Workspace precedence (requirement 2.3), exactly:** * `SPEC_WORKFLOW_WORKSPACE` → `--no-workspace-inference` → inference → * `resolveGitWorkspaceRoot(configuredPath)`. * * The environment override and inference are both skipped in dashboard-only * mode (requirements 1.14, 2.11), which has no workspace of its own. Dashboard * mode is an input distinct from `noInference` (requirement 2.10) precisely so * that the two can be reported differently in `source`. * * **The fallback is the git toplevel, not the raw configured path** * (requirement 1.2). Returning the configured path verbatim would move * `projectId`, narrow containment to a subdirectory, and make the two roots * unequal for users with no worktrees at all. * * **Validation of an *inferred* path is the caller's** (requirement 1.8). * `validateProjectPath` is async while this function and `parseArguments` are * synchronous, so the resolver reports `source` and `SpecWorkflowMCPServer.initialize` * — which already awaits that predicate — checks the workspace when * `source === 'inference'` and falls back with a log rather than throwing. * * **Workflow root precedence:** `SPEC_WORKFLOW_SHARED_ROOT`, resolved to * absolute (requirement 2.9) → the resolved workspace path when * `--no-shared-worktree-specs` is passed, including when that path came from * inference or the environment (requirement 2.8) → `resolveGitRoot` of the * **configured path argument** (requirement 2.7). * * Never throws: every git invocation below fails closed to null or to its * input, so `parseArguments` cannot die on a missing git binary. */ export declare function resolveWorkspaceRoots(options: WorkspaceRootsOptions): ResolvedRoots; //# sourceMappingURL=git-utils.d.ts.map