declare const CODEGRAPH_PACKAGE_NAME = "@colbymchenry/codegraph"; declare const CODEGRAPH_PACKAGE_VERSION = "1.6.2"; declare const CODEGRAPH_EXECUTABLE: string; declare const ALLOWED_SUBCOMMANDS: readonly ["status", "init", "index", "query", "files", "context", "affected"]; type CodegraphSubcommand = (typeof ALLOWED_SUBCOMMANDS)[number]; type BaseCodegraphInvocationOptions = { subcommand: CodegraphSubcommand; project: string; search?: string; files?: string[]; json?: boolean; quiet?: boolean; force?: boolean; limit?: number; maxDepth?: number; }; type ContextCodegraphInvocationOptions = Omit & { subcommand: 'context'; task: string; }; type NonContextCodegraphInvocationOptions = BaseCodegraphInvocationOptions & { subcommand: Exclude; task?: never; }; export type CodegraphInvocationOptions = ContextCodegraphInvocationOptions | NonContextCodegraphInvocationOptions; export type CodegraphInvocation = { executable: typeof CODEGRAPH_EXECUTABLE; args: string[]; cwd: string; packageName: typeof CODEGRAPH_PACKAGE_NAME; packageVersion: typeof CODEGRAPH_PACKAGE_VERSION; subcommand: CodegraphSubcommand; }; export type CodegraphExecutionResult = { exitCode: number | null; stdout: string; stderr: string; }; export type CodegraphProcessRunner = (invocation: CodegraphInvocation) => Promise; /** * Resolve `--project` to an existing directory, CANONICALIZED through * `realpathSync.native`. * * Exported because the CLI's repair verbs used to hand-roll `resolve()` + * `statSync().isDirectory()` and skip the canonicalization, so the config / * backup paths they reported and wrote were alias-dependent: a symlinked or * short-named project root produced paths naming a different directory from * the one every other codegraph command (all of which spawn through * `createCodegraphInvocation`, i.e. through this function) reports on. * Callers that need the same root upstream will be spawned with must call * this rather than re-implement it. */ export declare function resolveProjectRoot(project: string): string; export declare function createCodegraphInvocation(options: CodegraphInvocationOptions): CodegraphInvocation; export declare function executeCodegraphInvocation(invocation: CodegraphInvocation, runner?: CodegraphProcessRunner): Promise; /** * Single managed codegraph data directory name, relative to the * project root. peaks-loop always reads/writes * `/.codegraph/` and spawns the upstream binary with * `cwd = ` so its default discovery lands on the same * directory. */ export declare const CODEGRAPH_DIR_NAME = ".codegraph"; /** * Marker file peaks-loop writes inside the resolved codegraph dir * after a successful upstream init. Its presence distinguishes * peaks-loop-managed schemas from foreign ones (aider / cody / * etc. all happily use the same directory name). */ export declare const CODEGRAPH_MARKER_NAME = ".peaks-loop-marker"; /** * Upstream SQLite schema filename. `isCodegraphInitialized` mirrors * `@colbymchenry/codegraph`'s `isInitialized`, which requires this file * (not just the `.codegraph/` directory) to exist. */ export declare const CODEGRAPH_DB_NAME = "codegraph.db"; export declare const CODEGRAPH_INIT_CONFLICT_EXIT_CODE = 73; export type ResolvedCodegraphLocation = { readonly source: 'root'; /** Absolute path the upstream binary should be spawned with. */ readonly cwd: string; /** Absolute path to the data directory codegraph reads/writes. */ readonly codegraphDir: string; }; /** * Root-only resolver: peaks-loop always manages * `/.codegraph/`. Returns the cwd the upstream codegraph * binary should be spawned with (= project root, so the binary's * default discovery reads `/.codegraph/`) and the * absolute data-dir path. Pure path computation; no fs IO. */ export declare function resolveCodegraphProjectRoot(projectRoot: string): ResolvedCodegraphLocation; /** * Resolve `/.codegraph/` and REFUSE it when the directory it * actually names is not contained by the canonical project root. Returns the * canonicalized directory on success. * * Slice-002 S12 (security R1). The H1 fix refuses a link planted at the * backup FILE path, but the DIRECTORY that path lives in was never checked: * with `/.codegraph` as a junction (or a symlink) to another directory, * every existing probe passed — `defaultCodegraphInitGuard` probes with * `statSync`, which follows — and `applyCodegraphConfigRepair` rewrote * `/config.json` and created `/config.json.bak`. That is a * write outside the project, into a directory chosen by whoever committed * the link, and `.codegraph/` has no gitignore coverage in a consumer * project (the same argument that made H1 reachable). Its bound is real but * not a containment argument: `applyCodegraphConfigRepair` parses and * validates the target as a codegraph config before it writes anything, so * the clean chain is cross-project config tampering rather than an arbitrary * overwrite. Slice-001's audit recorded "all write targets are constants * under `projectRoot`" as CLEAN; this check is what makes that true. * * The predicate is CONTAINMENT, deliberately not link-ness. A link that * resolves to a directory still inside the project root is allowed, because * refusing every link would fail closed on a state a user may legitimately * have (a derived `.codegraph/` kept inside the repo). What is refused is a * `.codegraph` that ESCAPES the root, which no legitimate layout needs. * * Both sides go through the repo's own canonicalization rather than a second * hand-rolled comparison — `resolveProjectRoot`'s `realpathSync.native` (the * M1 fix) and `isInsidePath` from `shared/path-utils.ts`. `realpath` is what * makes a junction resolve at all (verified on Windows: `lstat` reports a * junction as a symlink and `realpathSync.native` returns its target), and * it is the normalization every codegraph invocation already spawns under. * * An ABSENT directory is returned as-is, not refused: nothing exists to * contain yet, and the write this guard precedes cannot create it — a * `/.codegraph/config.json` whose directory does not resolve fails * with ENOENT before anything is written. The read the writer does first * would throw ENOENT anyway. */ export declare function assertCodegraphDirContained(projectRoot: string): string; export type CodegraphInitGuardResult = { status: 'fresh'; codegraphDir: string; } | { status: 'noop-already-peaks-loop'; codegraphDir: string; } | { status: 'conflict-foreign-schema'; codegraphDir: string; }; export declare class CodegraphInitConflictError extends Error { readonly codegraphDir: string; readonly code = "CODEGRAPH_INIT_CONFLICT"; readonly exitCode = 73; constructor(message: string, codegraphDir: string); } export type CodegraphInitGuard = (projectRoot: string) => CodegraphInitGuardResult; /** * True when `/.codegraph/codegraph.db` exists. Mirrors * upstream `@colbymchenry/codegraph` `isInitialized`: the `.codegraph/` * directory alone (or with only the peaks-loop marker) is NOT initialized. * Pure fs probe; never throws. */ export declare function isCodegraphInitialized(projectRoot: string): boolean; /** * Root-only init guard: probes `/.codegraph/`. 'fresh' * is returned when the directory does not exist yet OR when it exists * with the peaks-loop marker but no `codegraph.db` (dangling), so the * next `peaks codegraph init` creates the root `.codegraph/` directory * and its SQLite schema. */ export declare function defaultCodegraphInitGuard(projectRoot: string): CodegraphInitGuardResult; /** * Pure-fs helper that stamps the peaks-loop marker AFTER a * successful upstream init. The CLI action must call this after * `executeCodegraphInvocation` returns exit 0. */ export declare function writeCodegraphMarker(codegraphDir: string): void; /** * Test seam — pure stub of the default guard. Lets callers inject a * pre-computed outcome without touching the real filesystem. */ export declare function constantCodegraphInitGuard(outcome: CodegraphInitGuardResult): CodegraphInitGuard; /** * Per-file row rendered into the codegraph-context envelope's * Markdown table. Symbol-count + cross-ref edges are intentionally * optional: when the upstream `affected` JSON does not carry them * (older codegraph versions, or a non-JSON stdout) we leave the * cells blank instead of inventing numbers. */ export type CodegraphAffectedRow = { readonly file: string; readonly symbolCount?: number; readonly crossRefEdges?: number; }; export type WriteCodegraphAffectedContextInput = { /** The peaks-loop project root (where `.peaks/_runtime/` lives). */ readonly projectRoot: string; readonly rid: string; /** Project-relative file paths passed to `peaks codegraph affected`. */ readonly files: readonly string[]; /** Raw upstream payload (string or already-parsed JSON). */ readonly affectedPayload: unknown; /** * Optional override for the active session id. When omitted, * the writer calls `getSessionId(projectRoot)` and falls * back to a graceful skip-with-warning when no binding is * present (e.g. the consumer ran `peaks codegraph affected` * outside of a peaks session). */ readonly sessionId?: string | null; /** * Optional clock seam for tests; defaults to `new Date()`. */ readonly now?: () => Date; }; export type WriteCodegraphAffectedContextResult = { readonly written: true; readonly path: string; readonly sessionId: string; } | { readonly written: false; readonly path: ''; readonly warning: string; }; /** * Pure renderer — turns the affected payload + rid + file list * into a Markdown body with a leading human-readable table and * a trailing JSON fence for machine consumers. */ export declare function renderCodegraphAffectedContext(rid: string, files: readonly string[], affectedPayload: unknown, sessionId: string, now?: () => Date): string; /** * Write the `codegraph-context.md` envelope into the canonical * session directory (`.peaks/_runtime//rd/`). The * envelope is what peaks-code's RD dispatch hook reads back to * seed the QA / TXT handoff (see `peaks-code/SKILL.md` * §"Codegraph orchestration context"). * * The function is intentionally side-effect-safe: * - No session binding → returns `{ written: false, warning }`, * does NOT throw. Callers can either surface the warning or * treat it as a no-op. * - Directory creation is `mkdirSync({ recursive: true })`, so * the first run in a fresh session creates the `rd/` subdir * on demand. * - The path is canonicalized through `getSessionDir` so * downstream readers (QA / TXT) get a stable path even when * the caller passed a relative `projectRoot`. */ export declare function writeCodegraphAffectedContext(input: WriteCodegraphAffectedContextInput): WriteCodegraphAffectedContextResult; export {};