/* global process */ import { join } from 'node:path'; import fs from 'node:fs'; import { getAgentDir } from '@earendil-works/pi-coding-agent'; /** * Absolute path to settings.json in the pi agent config dir (honors PI_CODING_AGENT_DIR). * Module-level so tests can stub HOME before a dynamic import. */ export const SETTINGS_PATH = join(getAgentDir(), 'settings.json'); /** * Read the raw settings.json as a plain object. * Tolerates a missing file, invalid JSON, or non-object content → {}. * (Same policy as loadHenyoSettings' original inline read.) */ export function readSettingsFile(): Record { if (!fs.existsSync(SETTINGS_PATH)) { return {}; } try { const parsed: unknown = JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf-8')); if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) { return parsed as Record; } return {}; } catch { return {}; } } /** * Writes the whole settings.json atomically: write to a tmp file, then * rename — a crash mid-write can't leave a truncated/corrupt settings.json * (rename is atomic on POSIX). Silent-fail — a settings write must never * break a session. Callers are responsible for preserving keys this write * doesn't know about. */ export function writeSettingsFile(data: Record): void { const tmpPath = `${SETTINGS_PATH}.tmp-${process.pid}`; try { fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2), 'utf-8'); fs.renameSync(tmpPath, SETTINGS_PATH); } catch { try { fs.unlinkSync(tmpPath); // best-effort tmp cleanup } catch { // ignore — tmp may not exist if the write itself failed } // Silently fail — don't break sessions if write fails } }