refactor(coding-agent): improve settings storage semantics and error handling

This commit is contained in:
Mario Zechner 2026-02-17 00:08:32 +01:00
parent 5133697bc4
commit de2736bad0
7 changed files with 386 additions and 152 deletions

View file

@ -24,7 +24,7 @@ describe("SettingsManager", () => {
});
describe("preserves externally added settings", () => {
it("should preserve enabledModels when changing thinking level", () => {
it("should preserve enabledModels when changing thinking level", async () => {
// Create initial settings file
const settingsPath = join(agentDir, "settings.json");
writeFileSync(
@ -45,6 +45,7 @@ describe("SettingsManager", () => {
// User changes thinking level via Shift+Tab
manager.setDefaultThinkingLevel("high");
await manager.flush();
// Verify enabledModels is preserved
const savedSettings = JSON.parse(readFileSync(settingsPath, "utf-8"));
@ -54,7 +55,7 @@ describe("SettingsManager", () => {
expect(savedSettings.defaultModel).toBe("claude-sonnet");
});
it("should preserve custom settings when changing theme", () => {
it("should preserve custom settings when changing theme", async () => {
const settingsPath = join(agentDir, "settings.json");
writeFileSync(
settingsPath,
@ -73,6 +74,7 @@ describe("SettingsManager", () => {
// User changes theme
manager.setTheme("light");
await manager.flush();
// Verify all settings preserved
const savedSettings = JSON.parse(readFileSync(settingsPath, "utf-8"));
@ -81,7 +83,7 @@ describe("SettingsManager", () => {
expect(savedSettings.theme).toBe("light");
});
it("should let in-memory changes override file changes for same key", () => {
it("should let in-memory changes override file changes for same key", async () => {
const settingsPath = join(agentDir, "settings.json");
writeFileSync(
settingsPath,
@ -99,6 +101,7 @@ describe("SettingsManager", () => {
// But then changes it via UI to "high"
manager.setDefaultThinkingLevel("high");
await manager.flush();
// In-memory change should win
const savedSettings = JSON.parse(readFileSync(settingsPath, "utf-8"));
@ -193,6 +196,22 @@ describe("SettingsManager", () => {
});
});
describe("error tracking", () => {
it("should collect and clear load errors via drainErrors", () => {
const globalSettingsPath = join(agentDir, "settings.json");
const projectSettingsPath = join(projectDir, ".pi", "settings.json");
writeFileSync(globalSettingsPath, "{ invalid global json");
writeFileSync(projectSettingsPath, "{ invalid project json");
const manager = SettingsManager.create(projectDir, agentDir);
const errors = manager.drainErrors();
expect(errors).toHaveLength(2);
expect(errors.map((e) => e.scope).sort()).toEqual(["global", "project"]);
expect(manager.drainErrors()).toEqual([]);
});
});
describe("shellCommandPrefix", () => {
it("should load shellCommandPrefix from settings", () => {
const settingsPath = join(agentDir, "settings.json");
@ -212,12 +231,13 @@ describe("SettingsManager", () => {
expect(manager.getShellCommandPrefix()).toBeUndefined();
});
it("should preserve shellCommandPrefix when saving unrelated settings", () => {
it("should preserve shellCommandPrefix when saving unrelated settings", async () => {
const settingsPath = join(agentDir, "settings.json");
writeFileSync(settingsPath, JSON.stringify({ shellCommandPrefix: "shopt -s expand_aliases" }));
const manager = SettingsManager.create(projectDir, agentDir);
manager.setTheme("light");
await manager.flush();
const savedSettings = JSON.parse(readFileSync(settingsPath, "utf-8"));
expect(savedSettings.shellCommandPrefix).toBe("shopt -s expand_aliases");