/**
 * --claude-home — per-world Claude Code instance isolation.
 *
 * Today, every world inherits the operator's single `~/.claude/` (host login,
 * settings, hooks, MCP topology, 429 bucket). When the operator wants to run
 * one world against their personal Anthropic account and another against
 * their work account on the same host, they have no clean seam — both worlds
 * share `~/.claude/.credentials.json` and the same 429 cooldown.
 *
 * Inspired by t3code (pingdotgg/t3code) — one server process hosts N Claude
 * Code instances, each with its own `HOME` env var pointing at a separate
 * `~/.claude.instance-N` directory. Zero containers, zero processes-per-
 * account, just `env: { HOME: '...' }` on the spawn.
 *
 * This module exports two pure helpers used by `olam create --claude-home`:
 *
 *   - {@link resolveClaudeHome} — pick the directory to use as HOME for the
 *     world's Claude Code config snapshot. Precedence:
 *       1. Explicit `--claude-home <id-or-path>` flag.
 *       2. Stored claudeHome from a prior `olam create` (re-runs of
 *          `olam enter <worldId>` honour the original choice).
 *       3. Operator's default `$HOME` (legacy behaviour — no behavioural
 *          change for worlds created without the flag).
 *
 *   - {@link ensureClaudeHomeDir} — `mkdir -p` the target dir and write a
 *     `.olam-claude-home` sentinel so first-use detection works.
 *
 * Composes with @olam/auth-client's vault: the vault still rotates
 * credentials WITHIN a HOME; `--claude-home` isolates DIFFERENT HOMEs from
 * each other.
 *
 * See `docs/decisions/045-claude-home-override.md` for the full rationale
 * + operator workflow.
 */
import { mkdir, writeFile, access } from 'node:fs/promises';
import { homedir } from 'node:os';
import path from 'node:path';
/** Relative path under `$HOME` where bare-id homes are materialised. */
export const CLAUDE_HOMES_BASE = path.join('.olam', 'claude-homes');
/** First-use sentinel filename — written into a freshly-created home. */
export const SENTINEL_FILENAME = '.olam-claude-home';
/**
 * Allowed shape of a bare `--claude-home <id>` argument.
 *
 * Same shape rule as world ids and skill prefixes: a leading lowercase-or-
 * digit char, then 0–63 more of `[a-z0-9_-]`. Rejects `..`, spaces,
 * absolute-path injections, uppercase. Absolute paths are handled
 * separately (see {@link resolveClaudeHome}) — they don't go through this
 * regex.
 */
export const HOME_ID_RE = /^[a-z0-9][a-z0-9_-]{0,63}$/;
/**
 * Resolve the absolute filesystem path to use as the world's Claude Code
 * HOME. Pure function — no IO, no env reads beyond the injected hooks.
 *
 * @throws Error if `flag` (or `existingWorldConfig.claudeHome`) is a
 *   relative path that doesn't match {@link HOME_ID_RE}.
 */
export function resolveClaudeHome(args) {
    const home = args.homeDir ?? homedir();
    if (args.flag !== undefined && args.flag.length > 0) {
        return resolveSpec(args.flag, home);
    }
    const stored = args.existingWorldConfig?.claudeHome;
    if (stored !== undefined && stored.length > 0) {
        return resolveSpec(stored, home);
    }
    // Default — legacy behaviour. Operator's $HOME, with same fallback as
    // node's stdlib (HOME env → os.homedir()).
    return args.envHome ?? home;
}
function resolveSpec(spec, home) {
    if (path.isAbsolute(spec)) {
        return spec;
    }
    if (!HOME_ID_RE.test(spec)) {
        throw new Error(`--claude-home value "${spec}" must be either an absolute path ` +
            `or a bare id matching ${HOME_ID_RE} (alphanumeric, underscore, dash; ` +
            `starts with alphanumeric; max 64 chars). Rejected: relative paths, ` +
            `parent refs, uppercase, whitespace.`);
    }
    return path.join(home, CLAUDE_HOMES_BASE, spec);
}
/**
 * Ensure the resolved Claude home directory exists and carries the
 * `.olam-claude-home` sentinel. Idempotent — re-running on an existing
 * home leaves the sentinel byte-identical (so `olam enter` re-runs don't
 * mutate the home's mtime gratuitously).
 *
 * Does NOT seed credentials, settings, or MCP config — that's the
 * operator's job (one-time `HOME=<resolved-path> claude login`).
 */
export async function ensureClaudeHomeDir(targetPath) {
    await mkdir(targetPath, { recursive: true });
    const sentinelPath = path.join(targetPath, SENTINEL_FILENAME);
    try {
        await access(sentinelPath);
        // Sentinel exists — leave it alone (idempotency).
        return;
    }
    catch {
        // Sentinel absent → write it.
    }
    await writeFile(sentinelPath, '# olam claude-home — managed by `olam create --claude-home`.\n' +
        '# Do not delete. Re-create by running `olam create --claude-home <id>` again.\n', 'utf-8');
}
//# sourceMappingURL=home-override.js.map