export declare function getUserConfigPath(): string; export declare function isInsidePath(childPath: string, parentPath: string): boolean; export declare function findProjectRoot(startPath: string): string | null; export declare function resolveProjectRootForConfig(startPath: string): string; /** * Canonicalise a user-supplied project root path against git's view of the * repository root. This is the fix for the nested-directory regression * where peaks-loop would write `.peaks/` under a nested sub-folder * (e.g. `prompt-project/prompt-project/.peaks/`) because the LLM passed * `$(pwd)` from inside a sub-directory of a real git repo. Without * canonicalisation, peaks accepted the cwd as-is, built the .peaks/ * tree there, and left the team with two parallel state stores. * * Strategy: * 1. If `startPath` (or any ancestor) is inside a git repo, return * `git rev-parse --show-toplevel` from `startPath`. The git root * is the *only* correct answer for "where does the .peaks/ tree * belong?" — sub-folders of a git repo are not their own projects. * 2. If `startPath` is not inside a git repo, fall back to * `findProjectRoot` (the existing heuristic) so the CLI still * works for non-git projects. * 3. If both fail, return `startPath` unchanged — better to write * to the cwd than to refuse the command. * * This is intentionally fail-open: it only *promotes* a path towards * the git root, it never demotes one. A non-git user is unaffected. * The function does NOT throw on a missing git binary or a non-zero * `git rev-parse` exit; both fall through to the heuristic. */ export declare function resolveCanonicalProjectRoot(startPath: string): string; /** * canonicalization for use in trust-boundary paths (e.g. * `peaks session primer`). * * Unlike `resolveCanonicalProjectRoot` (fail-open, returns `start` * on any resolution failure), this helper THROWS on: * - empty / whitespace-only input * - NUL byte in input * - non-existent path (realpathSync ENOENT) * - non-canonical (symlink) input (when the input path and its * realpath differ in a way that suggests the user passed a * symlinked path through a security-sensitive surface) * * Existing fail-open `resolveCanonicalProjectRoot` is retained for * back-compat — many callers depend on the fail-open behavior * (e.g. `init-command.ts:32`). The strict variant is opt-in. */ export declare class InvalidProjectRootError extends Error { readonly reason: 'empty' | 'nul-byte' | 'non-existent' | 'non-canonical'; constructor(reason: 'empty' | 'nul-byte' | 'non-existent' | 'non-canonical', input: string); } export declare function resolveCanonicalProjectRootStrict(startPath: string): string; /** * True when `projectRoot` IS the user's home directory itself. * * Exact match, NOT `isInsidePath`: `~/my-project` is an ordinary project and * must keep working — the guard exists for the one directory that is never a * project. * * `projectRootsMatch` rather than `===` because the two strings reach this * function from different sources: a `--project .` argument arrives in the * caller's spelling while `homedir()` yields the OS form (`C:\Users\x` vs * `C:/Users/x`, and differing case on win32/darwin). * * This is the ONE definition of "the user's home" in the codebase; the * harness-window writer's own guard delegates here so the two cannot drift. */ export declare function isUserHomeProjectRoot(projectRoot: string): boolean; /** * Thrown by `assertWritableProjectRoot` / `resolveWritableProjectRoot` when a * write would land in the user's home directory. Distinct from * `InvalidProjectRootError` (which is about a malformed/absent path): this path * is perfectly valid — it is just not a project. */ export declare class UnsafeProjectRootError extends Error { readonly projectRoot: string; constructor(projectRoot: string); } /** * Refuse a project root that IS the user's home directory. * * The hazard this closes: a fresh terminal starts in `$HOME`, and * `peaks workspace init --project .` there resolved to `$HOME` (via * `resolveCanonicalProjectRoot`'s fail-open fallback, which returns the start * path when it can find no project marker) and then materialized a whole * project tree INTO the user's home — `.claude/settings.local.json` above all, * which is a live config file shared with every other project on the machine. * * Deliberately NOT enforced inside `resolveCanonicalProjectRoot`. That helper * is called from 85 sites across 32 files, most of them READS (`peaks status`, * `peaks dashboard`, `peaks memory`, the observability commands). Refusing * there would break every one of them the moment a user runs a read command * from their home directory, and its fail-open contract is documented and * depended on. The hazard is a WRITE to `$HOME/.claude/`, so the guard belongs * on write paths — this function, plus `resolveWritableProjectRoot` so a * writer only has to make one call. */ export declare function assertWritableProjectRoot(projectRoot: string): void; /** * `resolveCanonicalProjectRoot` for a path that is about to be WRITTEN to: * same git-root-then-heuristic promotion, plus the home-directory refusal. * Read paths keep using the fail-open helper. */ export declare function resolveWritableProjectRoot(startPath: string): string; export declare function getProjectConfigPath(projectRoot: string | null): string | null; export declare function getProjectBootstrapConfigPath(projectRoot: string): string; export declare function validateProjectBootstrapConfigPathForWrite(projectRoot: string, configPath: string): void; export declare function validateUserConfigPathForWrite(configPath: string): void; export declare function validateArtifactWorkspaceRoot(artifactRoot: string, _workspaceRoot: string): void; export declare function validateArtifactWorkspaceMarkerPath(artifactRoot: string, peaksPath: string, markerPath: string): void; export declare function readConfigFileSafely(configPath: string, errorMessage: string): string; export declare function writeConfigFileSafely(configPath: string, content: string, validateBeforeWrite: () => void, errorMessage: string): void; export declare function writeProjectConfigFile(projectRoot: string, configPath: string, content: string): void; export declare function writeUserConfigFile(configPath: string, content: string): void;