import * as fs from "node:fs"; import * as path from "node:path"; import { CONFIG_DIR, CONFIG_FILE, GITIGNORE_ENTRY } from "./constants.js"; import { readFileSafe } from "./fs-utils.js"; import type { ProjectConfig } from "./types.js"; /** * Read the project-local config file. Returns null if the file is missing * or unparseable -- callers should treat that as "no override". */ export function readProjectConfig(cwd: string): ProjectConfig | null { const content = readFileSafe(path.join(cwd, CONFIG_DIR, CONFIG_FILE)); if (content === null) return null; try { return JSON.parse(content) as ProjectConfig; } catch { return null; } } /** * Write the project-local config file, creating its parent directory and * adding it to .gitignore on first write. Best-effort: errors are swallowed * since the in-memory state remains authoritative for the running session. */ export function writeProjectConfig(cwd: string, config: ProjectConfig): void { const dir = path.join(cwd, CONFIG_DIR); const file = path.join(dir, CONFIG_FILE); try { fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(file, JSON.stringify(config, null, 2) + "\n", "utf-8"); } catch { return; } ensureGitignored(cwd); } /** * Ensure the project's root .gitignore excludes the config directory. * Recognises a few equivalent existing patterns (e.g. ".pi/") so we don't * append a duplicate entry. */ function ensureGitignored(cwd: string): void { const gitignorePath = path.join(cwd, ".gitignore"); let existing = ""; try { existing = fs.readFileSync(gitignorePath, "utf-8"); } catch { // Missing -- we'll create it below. } const equivalents = new Set([ GITIGNORE_ENTRY, GITIGNORE_ENTRY.replace(/\/$/, ""), "/" + GITIGNORE_ENTRY, "/" + GITIGNORE_ENTRY.replace(/\/$/, ""), ".pi/", ".pi", "/.pi/", "/.pi", ]); for (const line of existing.split(/\r?\n/)) { if (equivalents.has(line.trim())) return; } const sep = existing.length === 0 || existing.endsWith("\n") ? "" : "\n"; try { fs.writeFileSync(gitignorePath, existing + sep + GITIGNORE_ENTRY + "\n", "utf-8"); } catch { // Best-effort. } }