mirror of
https://github.com/harivansh-afk/clanker-agent.git
synced 2026-04-16 18:03:53 +00:00
move pi-mono into companion-cloud as apps/companion-os
- Copy all pi-mono source into apps/companion-os/ - Update Dockerfile to COPY pre-built binary instead of downloading from GitHub Releases - Update deploy-staging.yml to build pi from source (bun compile) before Docker build - Add apps/companion-os/** to path triggers - No more cross-repo dispatch needed Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
0250f72976
579 changed files with 206942 additions and 0 deletions
165
packages/coding-agent/test/settings-manager-bug.test.ts
Normal file
165
packages/coding-agent/test/settings-manager-bug.test.ts
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
||||
import { SettingsManager } from "../src/core/settings-manager.js";
|
||||
|
||||
/**
|
||||
* Tests for the fix to a bug where external file changes to arrays were overwritten.
|
||||
*
|
||||
* The bug scenario was:
|
||||
* 1. Pi starts with settings.json containing packages: ["npm:some-pkg"]
|
||||
* 2. User externally edits file to packages: []
|
||||
* 3. User changes an unrelated setting (e.g., theme) via UI
|
||||
* 4. save() would overwrite packages back to ["npm:some-pkg"] from stale in-memory state
|
||||
*
|
||||
* The fix tracks which fields were explicitly modified during the session, and only
|
||||
* those fields override file values during save().
|
||||
*/
|
||||
describe("SettingsManager - External Edit Preservation", () => {
|
||||
const testDir = join(process.cwd(), "test-settings-bug-tmp");
|
||||
const agentDir = join(testDir, "agent");
|
||||
const projectDir = join(testDir, "project");
|
||||
|
||||
beforeEach(() => {
|
||||
if (existsSync(testDir)) {
|
||||
rmSync(testDir, { recursive: true });
|
||||
}
|
||||
mkdirSync(agentDir, { recursive: true });
|
||||
mkdirSync(join(projectDir, ".pi"), { recursive: true });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (existsSync(testDir)) {
|
||||
rmSync(testDir, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("should preserve file changes to packages array when changing unrelated setting", async () => {
|
||||
const settingsPath = join(agentDir, "settings.json");
|
||||
|
||||
// Initial state: packages has one item
|
||||
writeFileSync(
|
||||
settingsPath,
|
||||
JSON.stringify({
|
||||
theme: "dark",
|
||||
packages: ["npm:pi-mcp-adapter"],
|
||||
}),
|
||||
);
|
||||
|
||||
// Pi starts up, loads settings into memory
|
||||
const manager = SettingsManager.create(projectDir, agentDir);
|
||||
|
||||
// At this point, globalSettings.packages = ["npm:pi-mcp-adapter"]
|
||||
expect(manager.getPackages()).toEqual(["npm:pi-mcp-adapter"]);
|
||||
|
||||
// User externally edits settings.json to remove the package
|
||||
const currentSettings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
||||
currentSettings.packages = []; // User wants to remove this!
|
||||
writeFileSync(settingsPath, JSON.stringify(currentSettings, null, 2));
|
||||
|
||||
// Verify file was changed
|
||||
expect(JSON.parse(readFileSync(settingsPath, "utf-8")).packages).toEqual(
|
||||
[],
|
||||
);
|
||||
|
||||
// User changes an UNRELATED setting via UI (this triggers save)
|
||||
manager.setTheme("light");
|
||||
await manager.flush();
|
||||
|
||||
// With the fix, packages should be preserved as [] (not reverted to startup value)
|
||||
const savedSettings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
||||
|
||||
expect(savedSettings.packages).toEqual([]);
|
||||
expect(savedSettings.theme).toBe("light");
|
||||
});
|
||||
|
||||
it("should preserve file changes to extensions array when changing unrelated setting", async () => {
|
||||
const settingsPath = join(agentDir, "settings.json");
|
||||
|
||||
writeFileSync(
|
||||
settingsPath,
|
||||
JSON.stringify({
|
||||
theme: "dark",
|
||||
extensions: ["/old/extension.ts"],
|
||||
}),
|
||||
);
|
||||
|
||||
const manager = SettingsManager.create(projectDir, agentDir);
|
||||
|
||||
// User externally updates extensions
|
||||
const currentSettings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
||||
currentSettings.extensions = ["/new/extension.ts"];
|
||||
writeFileSync(settingsPath, JSON.stringify(currentSettings, null, 2));
|
||||
|
||||
// Change unrelated setting
|
||||
manager.setDefaultThinkingLevel("high");
|
||||
await manager.flush();
|
||||
|
||||
const savedSettings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
||||
|
||||
// With the fix, extensions should be preserved (not reverted to startup value)
|
||||
expect(savedSettings.extensions).toEqual(["/new/extension.ts"]);
|
||||
});
|
||||
|
||||
it("should preserve external project settings changes when updating unrelated project field", async () => {
|
||||
const projectSettingsPath = join(projectDir, ".pi", "settings.json");
|
||||
writeFileSync(
|
||||
projectSettingsPath,
|
||||
JSON.stringify({
|
||||
extensions: ["./old-extension.ts"],
|
||||
prompts: ["./old-prompt.md"],
|
||||
}),
|
||||
);
|
||||
|
||||
const manager = SettingsManager.create(projectDir, agentDir);
|
||||
|
||||
const currentProjectSettings = JSON.parse(
|
||||
readFileSync(projectSettingsPath, "utf-8"),
|
||||
);
|
||||
currentProjectSettings.prompts = ["./new-prompt.md"];
|
||||
writeFileSync(
|
||||
projectSettingsPath,
|
||||
JSON.stringify(currentProjectSettings, null, 2),
|
||||
);
|
||||
|
||||
manager.setProjectExtensionPaths(["./updated-extension.ts"]);
|
||||
await manager.flush();
|
||||
|
||||
const savedProjectSettings = JSON.parse(
|
||||
readFileSync(projectSettingsPath, "utf-8"),
|
||||
);
|
||||
expect(savedProjectSettings.prompts).toEqual(["./new-prompt.md"]);
|
||||
expect(savedProjectSettings.extensions).toEqual(["./updated-extension.ts"]);
|
||||
});
|
||||
|
||||
it("should let in-memory project changes override external changes for the same project field", async () => {
|
||||
const projectSettingsPath = join(projectDir, ".pi", "settings.json");
|
||||
writeFileSync(
|
||||
projectSettingsPath,
|
||||
JSON.stringify({
|
||||
extensions: ["./initial-extension.ts"],
|
||||
}),
|
||||
);
|
||||
|
||||
const manager = SettingsManager.create(projectDir, agentDir);
|
||||
|
||||
const currentProjectSettings = JSON.parse(
|
||||
readFileSync(projectSettingsPath, "utf-8"),
|
||||
);
|
||||
currentProjectSettings.extensions = ["./external-extension.ts"];
|
||||
writeFileSync(
|
||||
projectSettingsPath,
|
||||
JSON.stringify(currentProjectSettings, null, 2),
|
||||
);
|
||||
|
||||
manager.setProjectExtensionPaths(["./in-memory-extension.ts"]);
|
||||
await manager.flush();
|
||||
|
||||
const savedProjectSettings = JSON.parse(
|
||||
readFileSync(projectSettingsPath, "utf-8"),
|
||||
);
|
||||
expect(savedProjectSettings.extensions).toEqual([
|
||||
"./in-memory-extension.ts",
|
||||
]);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue