char-slop/ai-dots

ai dotfiles

git clone https://git.t4t.associates/char-slop/ai-dots

Charlotte Sompi: spring cleaning766a1e1

main
2.0 KiB73 linesraw
1/**
2 * Shared settings.json loading utilities.
3 *
4 * Reads global (~/.pi/agent/settings.json) and project (.pi/settings.json)
5 * settings, merging them so the closest-to-cwd wins.
6 */
7
8import { getAgentDir } from "@earendil-works/pi-coding-agent";
9import * as fs from "node:fs";
10import * as path from "node:path";
11
12type SettingsObject = Record<string, unknown>;
13
14function isSettingsObject(value: unknown): value is SettingsObject {
15  return typeof value === "object" && value !== null && !Array.isArray(value);
16}
17
18function mergeInto(target: SettingsObject, source: SettingsObject): void {
19  for (const [key, value] of Object.entries(source)) {
20    const existing = target[key];
21    if (isSettingsObject(existing) && isSettingsObject(value)) {
22      mergeInto(existing, value);
23    } else {
24      target[key] = value;
25    }
26  }
27}
28
29/**
30 * Read and merge settings from global + all project-level settings.json files.
31 * Project settings are walked up from `cwd` and applied farthest-first,
32 * so the closest `.pi/settings.json` wins over more distant ones.
33 */
34export function loadSettings(cwd: string): Record<string, unknown> {
35  const merged: SettingsObject = {};
36
37  function mergeFrom(raw: string) {
38    try {
39      const settings = JSON.parse(raw);
40      if (isSettingsObject(settings)) {
41        mergeInto(merged, settings);
42      }
43    } catch {
44      /* ignore */
45    }
46  }
47
48  // Global settings
49  try {
50    mergeFrom(fs.readFileSync(path.join(getAgentDir(), "settings.json"), "utf-8"));
51  } catch {
52    /* ignore */
53  }
54
55  // Project settings (walk up from cwd, collect all, apply farthest-first so closest wins)
56  const projectPaths: string[] = [];
57  let dir = cwd;
58  while (true) {
59    projectPaths.push(path.join(dir, ".pi", "settings.json"));
60    const parent = path.dirname(dir);
61    if (parent === dir) break;
62    dir = parent;
63  }
64  for (let i = projectPaths.length - 1; i >= 0; i--) {
65    try {
66      mergeFrom(fs.readFileSync(projectPaths[i], "utf-8"));
67    } catch {
68      /* ignore */
69    }
70  }
71
72  return merged;
73}