Do not create empty .pi folder unconditionally (#1588)

This commit is contained in:
Duncan Ogilvie 2026-02-23 00:59:10 +01:00 committed by GitHub
parent 6137de9ce7
commit f1a2092bcf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 5 deletions

View file

@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Fixed `.pi` folder being created unnecessarily when only reading settings. The folder is now only created when writing project-specific settings.
## [0.54.1] - 2026-02-22
### Fixed

View file

@ -147,16 +147,24 @@ export class FileSettingsStorage implements SettingsStorage {
withLock(scope: SettingsScope, fn: (current: string | undefined) => string | undefined): void {
const path = scope === "global" ? this.globalSettingsPath : this.projectSettingsPath;
const dir = dirname(path);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
let release: (() => void) | undefined;
try {
release = lockfile.lockSync(path, { realpath: false });
const current = existsSync(path) ? readFileSync(path, "utf-8") : undefined;
// Only create directory and lock if file exists or we need to write
const fileExists = existsSync(path);
if (fileExists) {
release = lockfile.lockSync(path, { realpath: false });
}
const current = fileExists ? readFileSync(path, "utf-8") : undefined;
const next = fn(current);
if (next !== undefined) {
// Only create directory when we actually need to write
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
if (!release) {
release = lockfile.lockSync(path, { realpath: false });
}
writeFileSync(path, next, "utf-8");
}
} finally {

View file

@ -212,6 +212,50 @@ describe("SettingsManager", () => {
});
});
describe("project settings directory creation", () => {
it("should not create .pi folder when only reading project settings", () => {
// Create agent dir with global settings, but NO .pi folder in project
const settingsPath = join(agentDir, "settings.json");
writeFileSync(settingsPath, JSON.stringify({ theme: "dark" }));
// Delete the .pi folder that beforeEach created
rmSync(join(projectDir, ".pi"), { recursive: true });
// Create SettingsManager (reads both global and project settings)
const manager = SettingsManager.create(projectDir, agentDir);
// .pi folder should NOT have been created just from reading
expect(existsSync(join(projectDir, ".pi"))).toBe(false);
// Settings should still be loaded from global
expect(manager.getTheme()).toBe("dark");
});
it("should create .pi folder when writing project settings", async () => {
// Create agent dir with global settings, but NO .pi folder in project
const settingsPath = join(agentDir, "settings.json");
writeFileSync(settingsPath, JSON.stringify({ theme: "dark" }));
// Delete the .pi folder that beforeEach created
rmSync(join(projectDir, ".pi"), { recursive: true });
const manager = SettingsManager.create(projectDir, agentDir);
// .pi folder should NOT exist yet
expect(existsSync(join(projectDir, ".pi"))).toBe(false);
// Write a project-specific setting
manager.setProjectPackages([{ source: "npm:test-pkg" }]);
await manager.flush();
// Now .pi folder should exist
expect(existsSync(join(projectDir, ".pi"))).toBe(true);
// And settings file should be created
expect(existsSync(join(projectDir, ".pi", "settings.json"))).toBe(true);
});
});
describe("shellCommandPrefix", () => {
it("should load shellCommandPrefix from settings", () => {
const settingsPath = join(agentDir, "settings.json");