"use strict";
/**
 * Directory Management
 *
 * Manages the .codegraph/ directory structure for CodeGraph data.
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.CODEGRAPH_DIR = void 0;
exports.codeGraphDirName = codeGraphDirName;
exports.isCodeGraphDataDir = isCodeGraphDataDir;
exports.getCodeGraphDir = getCodeGraphDir;
exports.isInitialized = isInitialized;
exports.unsafeIndexRootReason = unsafeIndexRootReason;
exports.findNearestCodeGraphRoot = findNearestCodeGraphRoot;
exports.createDirectory = createDirectory;
exports.removeDirectory = removeDirectory;
exports.listDirectoryContents = listDirectoryContents;
exports.getDirectorySize = getDirectorySize;
exports.ensureSubdirectory = ensureSubdirectory;
exports.validateDirectory = validateDirectory;
const fs = __importStar(require("fs"));
const os = __importStar(require("os"));
const path = __importStar(require("path"));
/** The default per-project data directory name. */
const DEFAULT_CODEGRAPH_DIR = '.codegraph';
let warnedBadDirName = false;
/**
 * Resolve the per-project data directory name, honoring the `CODEGRAPH_DIR`
 * environment override (default `.codegraph`). The override is a single path
 * segment that lives in the project root.
 *
 * Why this exists: two environments that share one working tree must NOT share
 * one `.codegraph/` — most concretely Windows-native and WSL (issue #636). The
 * daemon lockfile (`.codegraph/daemon.pid`) records a platform-specific pid and
 * socket path (a Windows named pipe vs a WSL Unix socket), and SQLite file
 * locking across the WSL2 ↔ Windows filesystem boundary is unreliable, so two
 * daemons sharing one index risks corruption. Setting `CODEGRAPH_DIR=.codegraph-win`
 * on one side gives each environment its own index in the same tree.
 *
 * Read live (not captured at load) so it is both process-accurate and testable.
 * An override that isn't a plain directory name — empty, containing a path
 * separator, `.`, `..`/traversal, or absolute — is ignored (we keep the
 * default) rather than risk writing the index outside the project or into the
 * project root itself; we warn once to stderr so the misconfiguration is seen.
 */
function codeGraphDirName() {
    const raw = process.env.CODEGRAPH_DIR?.trim();
    if (!raw)
        return DEFAULT_CODEGRAPH_DIR;
    const invalid = raw === '.' ||
        raw.includes('..') ||
        raw.includes('/') ||
        raw.includes('\\') ||
        path.isAbsolute(raw);
    if (invalid) {
        if (!warnedBadDirName) {
            warnedBadDirName = true;
            // stderr only — stdout is the MCP protocol channel.
            console.warn(`[codegraph] Ignoring invalid CODEGRAPH_DIR="${raw}" — it must be a plain ` +
                `directory name (no path separators, no "..", not absolute). Using "${DEFAULT_CODEGRAPH_DIR}".`);
        }
        return DEFAULT_CODEGRAPH_DIR;
    }
    return raw;
}
/**
 * CodeGraph directory name — a load-time snapshot of {@link codeGraphDirName}.
 * A running process's environment is fixed, so this equals the live value;
 * it's kept as a stable string export for backward compatibility. Internal code
 * resolves the name through {@link codeGraphDirName} / {@link getCodeGraphDir}
 * so the `CODEGRAPH_DIR` override always applies.
 */
exports.CODEGRAPH_DIR = codeGraphDirName();
/**
 * Is `name` (a single path segment) a CodeGraph data directory? Matches the
 * default `.codegraph`, the active `CODEGRAPH_DIR` override, and any
 * `.codegraph-*` sibling. File-watching and the indexer skip ALL of these, so
 * when two environments share one working tree (Windows + WSL, issue #636)
 * neither indexes or watches the other's index directory.
 */
function isCodeGraphDataDir(name) {
    return (name === DEFAULT_CODEGRAPH_DIR ||
        name === codeGraphDirName() ||
        name.startsWith(DEFAULT_CODEGRAPH_DIR + '-'));
}
/**
 * Get the .codegraph directory path for a project
 */
function getCodeGraphDir(projectRoot) {
    return path.join(projectRoot, codeGraphDirName());
}
/**
 * Check if a project has been initialized with CodeGraph
 * Requires both .codegraph/ directory AND codegraph.db to exist
 */
function isInitialized(projectRoot) {
    const codegraphDir = getCodeGraphDir(projectRoot);
    if (!fs.existsSync(codegraphDir) || !fs.statSync(codegraphDir).isDirectory()) {
        return false;
    }
    // Must have codegraph.db, not just .codegraph folder
    const dbPath = path.join(codegraphDir, 'codegraph.db');
    return fs.existsSync(dbPath);
}
/**
 * Find the nearest parent directory containing .codegraph/
 *
 * Walks up from the given path to find a CodeGraph-initialized project,
 * similar to how git finds .git/ directories.
 *
 * @param startPath - Directory to start searching from
 * @returns The project root containing .codegraph/, or null if not found
 */
/**
 * Reason a directory is unsafe to use as an index ROOT, or null when it's fine.
 *
 * Indexing your home directory or a filesystem root drags in caches, `Library`,
 * every other project, etc. — a multi-GB index, constant file-watcher churn, and
 * (pre-1.0 on macOS) a file-descriptor blowup that exhausted `kern.maxfiles` and
 * took unrelated apps / the whole machine down (#845). The classic trigger:
 * running the installer or `codegraph init` from `$HOME`, which auto-indexes the
 * current directory. These are never intended project roots, so the installer
 * and `init`/`index` refuse them (overridable with `--force`).
 *
 * Pure-ish (reads only `os.homedir()` + realpath) so it's easy to unit-test.
 * The returned string is a human phrase that slots into "… looks like {reason}".
 */
function unsafeIndexRootReason(projectRoot) {
    const resolve = (p) => {
        try {
            return fs.realpathSync(path.resolve(p));
        }
        catch {
            return path.resolve(p);
        }
    };
    const resolved = resolve(projectRoot);
    // Filesystem root: `/` on POSIX, a drive root like `C:\` on Windows.
    if (path.parse(resolved).root === resolved) {
        return 'the filesystem root';
    }
    const home = resolve(os.homedir());
    // Case-insensitive on macOS/Windows (case-preserving but case-insensitive FS).
    const norm = (p) => process.platform === 'darwin' || process.platform === 'win32' ? p.toLowerCase() : p;
    const r = norm(resolved);
    const h = norm(home);
    if (r === h) {
        return 'your home directory';
    }
    // An ancestor of home (e.g. `/Users`, `/home`) — even broader than home.
    if (h.startsWith(r + path.sep)) {
        return 'a parent of your home directory';
    }
    return null;
}
function findNearestCodeGraphRoot(startPath) {
    let current = path.resolve(startPath);
    const root = path.parse(current).root;
    while (current !== root) {
        if (isInitialized(current)) {
            return current;
        }
        const parent = path.dirname(current);
        if (parent === current)
            break; // Reached filesystem root
        current = parent;
    }
    // Check root as well
    if (isInitialized(current)) {
        return current;
    }
    return null;
}
/**
 * Contents of `.codegraph/.gitignore`. A single wildcard ignore keeps every
 * transient file in the index dir — the database, `daemon.pid`, the socket,
 * logs, cache, and anything future versions add — out of git, without having
 * to enumerate each name (issues #788, #492, #484). Older versions wrote an
 * explicit allowlist that never listed `daemon.pid` or the socket, so those
 * runtime files were silently committed.
 */
const GITIGNORE_CONTENT = `# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
`;
/** Header line that prefixes every .gitignore CodeGraph has auto-generated. */
const GITIGNORE_MARKER = '# CodeGraph data files';
/**
 * Is `content` a stale CodeGraph-generated `.gitignore` that should be
 * regenerated in place? True when it carries our header but predates the
 * wildcard ignore (it has no bare `*` line) — i.e. one of the old explicit
 * allowlists (`*.db`, `cache/`, `.dirty`, …) that never ignored `daemon.pid`
 * or the socket (issue #788). A file WITHOUT our header is user-authored and
 * is left untouched; one that already has the wildcard is current. Matching
 * on the header (not a byte-exact list of past defaults) heals every old
 * variant — v0.7.x through 0.9.9 — and is idempotent once upgraded.
 */
function isStaleDefaultGitignore(content) {
    if (!content.trimStart().startsWith(GITIGNORE_MARKER))
        return false;
    return !content.split('\n').some((line) => line.trim() === '*');
}
/**
 * Write `.codegraph/.gitignore` if it's absent, or upgrade a stale
 * CodeGraph-generated default in place; a user-customized file is left alone.
 * Best-effort — returns `false` only if a needed write failed.
 */
function ensureGitignore(gitignorePath) {
    let existing;
    try {
        existing = fs.readFileSync(gitignorePath, 'utf-8');
    }
    catch {
        existing = null; // absent (ENOENT) or unreadable — (re)create below
    }
    // Current default or a user-authored file: nothing to do.
    if (existing !== null && !isStaleDefaultGitignore(existing))
        return true;
    try {
        fs.writeFileSync(gitignorePath, GITIGNORE_CONTENT, 'utf-8');
        return true;
    }
    catch {
        return false;
    }
}
/**
 * Create the .codegraph directory structure
 * Note: Only throws if codegraph.db already exists, not just if .codegraph/ exists.
 */
function createDirectory(projectRoot) {
    const codegraphDir = getCodeGraphDir(projectRoot);
    const dbPath = path.join(codegraphDir, 'codegraph.db');
    // Only throw if CodeGraph is actually initialized (db exists)
    // .codegraph/ folder alone is fine
    if (fs.existsSync(dbPath)) {
        throw new Error(`CodeGraph already initialized in ${projectRoot}`);
    }
    // Create main directory (if it doesn't exist)
    fs.mkdirSync(codegraphDir, { recursive: true });
    // Write .gitignore inside .codegraph (create if absent, upgrade a stale
    // pre-wildcard default left by an older version — issue #788).
    ensureGitignore(path.join(codegraphDir, '.gitignore'));
}
/**
 * Remove the .codegraph directory
 */
function removeDirectory(projectRoot) {
    const codegraphDir = getCodeGraphDir(projectRoot);
    if (!fs.existsSync(codegraphDir)) {
        return;
    }
    // Verify .codegraph is a real directory, not a symlink pointing elsewhere
    const lstat = fs.lstatSync(codegraphDir);
    if (lstat.isSymbolicLink()) {
        // Only remove the symlink itself, never follow it for recursive delete
        fs.unlinkSync(codegraphDir);
        return;
    }
    if (!lstat.isDirectory()) {
        // Not a directory - remove the single file
        fs.unlinkSync(codegraphDir);
        return;
    }
    // Recursively remove directory
    fs.rmSync(codegraphDir, { recursive: true, force: true });
}
/**
 * Get all files in the .codegraph directory
 */
function listDirectoryContents(projectRoot) {
    const codegraphDir = getCodeGraphDir(projectRoot);
    if (!fs.existsSync(codegraphDir)) {
        return [];
    }
    const files = [];
    function walkDir(dir, prefix = '') {
        const entries = fs.readdirSync(dir, { withFileTypes: true });
        for (const entry of entries) {
            const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name;
            // Skip symlinks to prevent following links outside .codegraph
            if (entry.isSymbolicLink()) {
                continue;
            }
            if (entry.isDirectory()) {
                walkDir(path.join(dir, entry.name), relativePath);
            }
            else {
                files.push(relativePath);
            }
        }
    }
    walkDir(codegraphDir);
    return files;
}
/**
 * Get the total size of the .codegraph directory in bytes
 */
function getDirectorySize(projectRoot) {
    const codegraphDir = getCodeGraphDir(projectRoot);
    if (!fs.existsSync(codegraphDir)) {
        return 0;
    }
    let totalSize = 0;
    function walkDir(dir) {
        const entries = fs.readdirSync(dir, { withFileTypes: true });
        for (const entry of entries) {
            // Skip symlinks to prevent following links outside .codegraph
            if (entry.isSymbolicLink()) {
                continue;
            }
            const fullPath = path.join(dir, entry.name);
            if (entry.isDirectory()) {
                walkDir(fullPath);
            }
            else {
                const stats = fs.statSync(fullPath);
                totalSize += stats.size;
            }
        }
    }
    walkDir(codegraphDir);
    return totalSize;
}
/**
 * Ensure a subdirectory exists within .codegraph
 */
function ensureSubdirectory(projectRoot, subdirName) {
    if (subdirName.includes('..') || subdirName.includes(path.sep) || subdirName.includes('/')) {
        throw new Error(`Invalid subdirectory name: ${subdirName}`);
    }
    const subdirPath = path.join(getCodeGraphDir(projectRoot), subdirName);
    if (!fs.existsSync(subdirPath)) {
        fs.mkdirSync(subdirPath, { recursive: true });
    }
    return subdirPath;
}
/**
 * Check if the .codegraph directory has valid structure
 */
function validateDirectory(projectRoot) {
    const errors = [];
    const codegraphDir = getCodeGraphDir(projectRoot);
    if (!fs.existsSync(codegraphDir)) {
        errors.push('CodeGraph directory does not exist');
        return { valid: false, errors };
    }
    if (!fs.statSync(codegraphDir).isDirectory()) {
        errors.push('.codegraph exists but is not a directory');
        return { valid: false, errors };
    }
    // Auto-repair / upgrade .gitignore (non-critical file). A missing one is
    // recreated; a stale pre-wildcard default that never ignored daemon.pid is
    // regenerated in place (issue #788); a user-authored file is left alone.
    const gitignorePath = path.join(codegraphDir, '.gitignore');
    const existedBefore = fs.existsSync(gitignorePath);
    if (!ensureGitignore(gitignorePath) && !existedBefore) {
        // Only a missing-and-uncreatable file is surfaced; a failed in-place
        // upgrade of an existing file is non-fatal — the index still works.
        errors.push('.gitignore missing in .codegraph directory and could not be created');
    }
    return {
        valid: errors.length === 0,
        errors,
    };
}
//# sourceMappingURL=directory.js.map