diff --git a/.gitignore b/.gitignore index ba2a97b..8d72f3a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules coverage +.pi/readcache/ diff --git a/README.md b/README.md index cdce8bf..5c4f248 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,20 @@ pi install git:https://github.com/Gurpartap/pi-readcache After installation, you can use pi normally. If pi is already running when you install or update, run `/reload` in that session. +### Custom Cache Storage + +By default, `pi-readcache` stores its cache in `.pi/readcache` within your project root. + +To override this, add a `readcacheDir` key to your `~/.pi/agent/settings.json` file: + +```json +{ + "readcacheDir": "/path/to/your/custom/cache" +} +``` + +The extension will respect this path and create the necessary subdirectories there. + ## Day-to-day usage | Action | Command | Expected result | diff --git a/src/constants.ts b/src/constants.ts index 5b2969e..afe82d1 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -24,7 +24,10 @@ export const DEFAULT_EXCLUDED_PATH_PATTERNS = [ ".netrc", ] as const; -export const READCACHE_ROOT_DIR = ".pi/readcache"; +import { getReadcacheCacheDir } from "./pi-config.js"; + +// ... +export const READCACHE_ROOT_DIR = await getReadcacheCacheDir(); export const READCACHE_OBJECTS_DIR = `${READCACHE_ROOT_DIR}/objects`; export const READCACHE_TMP_DIR = `${READCACHE_ROOT_DIR}/tmp`; export const READCACHE_OBJECT_MAX_AGE_MS = 24 * 60 * 60 * 1000; diff --git a/src/pi-config.ts b/src/pi-config.ts new file mode 100644 index 0000000..e3c343e --- /dev/null +++ b/src/pi-config.ts @@ -0,0 +1,42 @@ +import { homedir } from "node:os"; +import { join } from "node:path"; +import { readFile } from "node:fs/promises"; + +// Configuration constants following Pi standards +const CONFIG_DIR_NAME = ".pi"; +const ENV_AGENT_DIR = "PI_CODING_AGENT_DIR"; + +/** + * Returns the standard agent config directory (~/.pi/agent/). + */ +export function getAgentDir(): string { + const envDir = process.env[ENV_AGENT_DIR]; + if (envDir) { + if (envDir === "~") return homedir(); + if (envDir.startsWith("~/")) return homedir() + envDir.slice(1); + return envDir; + } + return join(homedir(), CONFIG_DIR_NAME, "agent"); +} + +/** + * Returns the standard path to settings.json (~/.pi/agent/settings.json). + */ +export function getSettingsPath(): string { + return join(getAgentDir(), "settings.json"); +} + +/** + * Reads the settings.json file and returns the 'readcacheDir' key, + * defaulting to the standard local cache path if not set. + */ +export async function getReadcacheCacheDir(): Promise { + const defaultDir = ".pi/readcache"; + try { + const content = await readFile(getSettingsPath(), "utf-8"); + const settings = JSON.parse(content); + return settings.readcacheDir || defaultDir; + } catch { + return defaultDir; + } +} diff --git a/test/unit/pi-config.test.ts b/test/unit/pi-config.test.ts new file mode 100644 index 0000000..1e61435 --- /dev/null +++ b/test/unit/pi-config.test.ts @@ -0,0 +1,51 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { getReadcacheCacheDir } from "../../src/pi-config.js"; +import { readFile } from "node:fs/promises"; +import { homedir } from "node:os"; +import { join } from "node:path"; + +vi.mock("node:fs/promises"); + +describe("getReadcacheCacheDir", () => { + const originalEnv = process.env; + + beforeEach(() => { + process.env = { ...originalEnv }; + vi.resetAllMocks(); + }); + + afterEach(() => { + process.env = originalEnv; + }); + + it("returns default path when settings.json does not exist", async () => { + vi.mocked(readFile).mockRejectedValue(new Error("File not found")); + const dir = await getReadcacheCacheDir(); + expect(dir).toBe(".pi/readcache"); + }); + + it("returns default path when readcacheDir is missing in settings", async () => { + vi.mocked(readFile).mockResolvedValue("{}"); + const dir = await getReadcacheCacheDir(); + expect(dir).toBe(".pi/readcache"); + }); + + it("returns custom path from settings.json", async () => { + vi.mocked(readFile).mockResolvedValue(JSON.stringify({ readcacheDir: "/tmp/custom-cache" })); + const dir = await getReadcacheCacheDir(); + expect(dir).toBe("/tmp/custom-cache"); + }); + + it("respects PI_CODING_AGENT_DIR override for settings location", async () => { + process.env.PI_CODING_AGENT_DIR = "/custom/agent"; + vi.mocked(readFile).mockImplementation(async (path: string) => { + if (path === join("/custom/agent", "settings.json")) { + return JSON.stringify({ readcacheDir: "/env/cache" }); + } + throw new Error("File not found"); + }); + + const dir = await getReadcacheCacheDir(); + expect(dir).toBe("/env/cache"); + }); +});