import { PaneGridPlanner } from "./grid-planner.js"; import { SessionManager } from "./session-manager.js"; import { TmuxMultiplexer } from "./tmux-multiplexer.js"; import type { Logger } from "./tmux-multiplexer.js"; import { type SessionManagerAdapter } from "./types.js"; /** * Resolve the full path to a binary using `which` (POSIX) or `where` (win32). * Returns null if not found. */ export declare function resolveBinary(name: string): Promise; /** * Get the tmux version string by running `{tmuxPath} -V`. * * Note: `tmux --version` is parsed by the C entrypoint as a CLI shorthand * and the `--` token is interpreted as "end of options" by some tmux * builds (e.g. 3.6b on macOS Homebrew returns * "tmux: unknown option -- -" * when called via `execFile`). The portable shorthand is `tmux -V` * which is accepted by tmux 2.x and later. */ export declare function getTmuxVersion(tmuxPath: string): Promise; /** * Resolve the tmux port for `projectDir`, returning the persisted port if * the state file exists, else computing a deterministic fallback from a * SHA-256 hash of `projectDir` mapped into the 10000..65535 range. * * The previous name `readPersistedPort` was misleading — the function does * NOT return `null` on a cold start; it always returns a usable port via * the deterministic hash fallback. The new name `readOrMigratePort` * documents the "persist or derive" behavior at the call site. * * Birthday-collision invariant: the deterministic fallback has 55,535 * distinct ports (10000..65535 inclusive). Per the birthday paradox, the * first collision among N projects on a shared host is expected at * ~√55535 ≈ 236 projects. Two projects on the same host whose SHA-256 * hashes happen to map to the same port will both attempt to bind, and * the second will silently fall back to a different port via the * `EADDRINUSE` retry path in the tmux server bootstrap. This is * acceptable for the single-developer, single-host target use case * documented in P42/SPEC.md. * * @param projectDir - absolute path used as the hash key AND as the * parent of the persisted port file (`.hivemind/state/tmux-port.json`). * @returns a port number in 10000..65535, or `null` only if the persisted * file exists but is malformed (parse error or non-numeric `port` * field). A `null` return is a signal to the caller to treat the * project as having no valid port and to surface the error to the user * rather than silently using a fresh hash (which would be a different * port than the one the user may have been told to use). */ export declare function readOrMigratePort(projectDir: string): number | null; /** * Persist the tmux port to `.hivemind/state/tmux-port.json` for cross-session * stability. Creates the state directory if it does not exist. */ export declare function persistPort(projectDir: string, port: number): void; /** * Detect the server URL for a given project directory. * First tries the persisted port; if none found, returns null * (caller may detect URL from PluginInput later). */ export declare function detectServerUrl(projectDir: string): Promise; /** * Try to read `opencode.json` in the project root and extract an explicit * `server.port` configuration. This is the user-friendly path: when a user * adds `"server": { "port": 4096 }` to their `opencode.json`, we use that * port verbatim for `opencode attach ` calls. * * Why this matters: `opencode attach ` needs the EXACT port the * running opencode server is listening on. The in-tree integration * previously relied on a hash-derived port (from `readOrMigratePort`), * which only matches the running server if the user happened to start * opencode with that port. Reading `opencode.json` makes the wiring * deterministic — the user configures the port in one place. * * Returns `null` if: * - `opencode.json` does not exist * - JSON parse fails * - `server.port` is not a positive integer * - `server.port` is 0 (means "OS-assigned" in opencode — we can't predict it) * * D-04 mirror: any file-read error is swallowed and returns `null`. * * @param projectDir - absolute path to the project root. * @returns the configured port number, or `null` if not set / unparseable. */ export declare function loadOpencodeServerPort(projectDir: string): number | null; /** * Resolve the opencode server URL using discover-then-persist pattern. * * Priority order (designed for the real-world tmux workflow): * 1. **Persisted port** — previously discovered and saved to * `.hivemind/state/tmux-port.json`. Fast path (no process scan). * Validates by probing; if stale, falls through to re-discovery. * 2. **Live discovery** — `lsof` + `ps` to find the actual opencode * serve process. Persists the result for subsequent calls. * 3. **Config fallback** — `opencode.json` `server.port` (may be stale * if user started with a different port). * 4. **Hash fallback** — deterministic port from project dir hash. */ export declare function resolveOpencodeServerUrl(projectDir: string): Promise; /** * Try to find a running opencode server on localhost by probing a * small set of common ports. Returns the first port that accepts a * connection within 250ms, or `null` if no port responds. * * Total worst-case latency: 5 ports × 250ms = 1.25 seconds. In * practice this returns in <50ms when opencode is on its default * port (4096). The probe is intentionally sequential — we stop at * the first hit and don't burn the full budget when found early. * * Used by `resolveOpencodeServerUrl` as the live-detection fallback * when `opencode.json` doesn't declare an explicit `server.port`. */ export declare function probeLocalhostForOpencodeServer(): Promise; /** * The wired tmux subsystem. The `tmux-copilot` tool consumes `adapter` * (the `SessionManagerAdapter` shape). `multiplexer` and * `sessionManager_` are exposed for testing + future plugin-time wiring * (e.g. observer hookup in `plugin.ts`). */ export interface TmuxIntegration { readonly isAvailable: () => boolean; readonly version: string | null; readonly binaryPath: string | null; readonly opencodeBinaryPath: string | null; readonly serverUrl: string | null; readonly projectDirectory: string; readonly adapter: SessionManagerAdapter; readonly multiplexer: TmuxMultiplexer; readonly sessionManager_: SessionManager; /** Factory for the in-tree `PaneGridPlanner`. */ readonly createPaneGridPlanner: (debounceMs?: number) => PaneGridPlanner; } /** * Create a `TmuxIntegration` instance if tmux and opencode are available. * Returns `null` (silent no-op) when: * - tmux binary not found on PATH * - Not running inside a tmux session (process.env.TMUX not set) * - opencode binary not found on PATH * - Any error occurs during detection * * Phase 51 (REQ-51-05): the factory owns the in-tree `TmuxMultiplexer` * and `SessionManager` lifecycle. The `adapter` it returns is the * `SessionManagerAdapter` shape that the `tmux-copilot` tool consumes * (replaces the fork-bridge that Phase 43-46 used). * * D-04 contract: runtime existsSync-based detection. The factory * returns `null` (not throws) when tmux is unavailable. The D-04 * graceful-fallback guarantee is preserved by the silent-null return * pattern — callers (e.g. `plugin.ts`) treat a `null` return as * "tmux integration not available, skip the tool registration." */ export declare function createTmuxIntegrationIfSupported(projectDirectory: string, options?: { log?: Logger; }): Promise; //# sourceMappingURL=integration.d.ts.map