@pi-archimedes/subagent 2.10.1 → 2.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/local-config.test.ts +55 -0
- package/src/local-config.ts +7 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-archimedes/subagent",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/danielcherubini/pi-archimedes.git"
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
],
|
|
16
16
|
"main": "./src/index.ts",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@pi-archimedes/core": "2.10.
|
|
19
|
-
"@pi-archimedes/todo": "2.10.
|
|
18
|
+
"@pi-archimedes/core": "2.10.2",
|
|
19
|
+
"@pi-archimedes/todo": "2.10.2"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"@earendil-works/pi-ai": ">=0.1.0",
|
package/src/local-config.test.ts
CHANGED
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
existsSync,
|
|
6
6
|
readdirSync,
|
|
7
7
|
writeFileSync,
|
|
8
|
+
readFileSync,
|
|
9
|
+
lstatSync,
|
|
10
|
+
symlinkSync,
|
|
8
11
|
} from "node:fs";
|
|
9
12
|
import { join } from "node:path";
|
|
10
13
|
import { tmpdir } from "node:os";
|
|
@@ -15,6 +18,9 @@ import {
|
|
|
15
18
|
writeLocalThinking,
|
|
16
19
|
deleteLocalThinking,
|
|
17
20
|
deleteLocalAgent,
|
|
21
|
+
writeLocalField,
|
|
22
|
+
deleteLocalField,
|
|
23
|
+
setLocalConfig,
|
|
18
24
|
} from "./local-config.js";
|
|
19
25
|
|
|
20
26
|
// Redirect getAgentDir() to a temp directory via PI_CODING_AGENT_DIR.
|
|
@@ -204,4 +210,53 @@ describe("local-config", () => {
|
|
|
204
210
|
expect(files).not.toContain("agents.local.json.tmp");
|
|
205
211
|
expect(existsSync(join(testDir, "agents.local.json"))).toBe(true);
|
|
206
212
|
});
|
|
213
|
+
|
|
214
|
+
describe("symlink preservation", () => {
|
|
215
|
+
const linkPath = join(testDir, "agents.local.json");
|
|
216
|
+
const targetPath = join(testDir, "agents.local.json.target");
|
|
217
|
+
|
|
218
|
+
// Fresh per-test setup: a target file pre-seeded with { "preexisting":
|
|
219
|
+
// "keep-me" } and a symlink agents.local.json → target.
|
|
220
|
+
beforeEach(() => {
|
|
221
|
+
rmSync(linkPath, { force: true });
|
|
222
|
+
rmSync(join(testDir, "agents.local.json.tmp"), { force: true });
|
|
223
|
+
rmSync(targetPath, { force: true });
|
|
224
|
+
writeFileSync(targetPath, JSON.stringify({ preexisting: "keep-me" }), "utf-8");
|
|
225
|
+
symlinkSync(targetPath, linkPath);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("writeLocalField preserves the symlink and updates the target", () => {
|
|
229
|
+
writeLocalField("general", "model", "test-model");
|
|
230
|
+
expect(lstatSync(linkPath).isSymbolicLink()).toBe(true);
|
|
231
|
+
const data = JSON.parse(readFileSync(targetPath, "utf-8"));
|
|
232
|
+
expect(data.general.model).toBe("test-model");
|
|
233
|
+
expect(data.preexisting).toBe("keep-me");
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("deleteLocalField preserves the symlink and removes the entry", () => {
|
|
237
|
+
writeLocalField("general", "model", "test-model");
|
|
238
|
+
deleteLocalField("general", "model");
|
|
239
|
+
expect(lstatSync(linkPath).isSymbolicLink()).toBe(true);
|
|
240
|
+
const data = JSON.parse(readFileSync(targetPath, "utf-8"));
|
|
241
|
+
expect(data.general).toBeUndefined();
|
|
242
|
+
expect(data.preexisting).toBe("keep-me");
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it("regular file still works (no symlink regression) and leaves no .tmp", () => {
|
|
246
|
+
rmSync(linkPath);
|
|
247
|
+
writeFileSync(linkPath, JSON.stringify({ preexisting: "keep-me" }), "utf-8");
|
|
248
|
+
writeLocalField("general", "thinking", "high");
|
|
249
|
+
expect(lstatSync(linkPath).isSymbolicLink()).toBe(false);
|
|
250
|
+
const data = JSON.parse(readFileSync(linkPath, "utf-8"));
|
|
251
|
+
expect(data.general.thinking).toBe("high");
|
|
252
|
+
expect(readdirSync(testDir)).not.toContain("agents.local.json.tmp");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("setLocalConfig is a full replace through the symlink", () => {
|
|
256
|
+
setLocalConfig({ alpha: { model: "m" } });
|
|
257
|
+
expect(lstatSync(linkPath).isSymbolicLink()).toBe(true);
|
|
258
|
+
const data = JSON.parse(readFileSync(targetPath, "utf-8"));
|
|
259
|
+
expect(data).toEqual({ alpha: { model: "m" } });
|
|
260
|
+
});
|
|
261
|
+
});
|
|
207
262
|
});
|
package/src/local-config.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
readFileSync,
|
|
3
|
-
writeFileSync,
|
|
4
|
-
existsSync,
|
|
5
|
-
renameSync,
|
|
6
|
-
unlinkSync,
|
|
7
|
-
} from "node:fs";
|
|
1
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
8
2
|
import { join } from "node:path";
|
|
9
3
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { writeAtomicPreservingSymlinks } from "@pi-archimedes/core/settings-io";
|
|
10
5
|
|
|
11
6
|
/** Fields that can be overridden per agent in agents.local.json. */
|
|
12
7
|
export type LocalField = "model" | "thinking";
|
|
@@ -35,25 +30,12 @@ export function readLocalConfig(): LocalConfig {
|
|
|
35
30
|
return readLocalConfigRaw();
|
|
36
31
|
}
|
|
37
32
|
|
|
38
|
-
/**
|
|
39
|
-
* Write the full config atomically: write to .tmp then rename.
|
|
40
|
-
* Falls back to a direct write if rename fails; cleans up .tmp on failure.
|
|
41
|
-
* Follows the pattern in packages/core/src/settings-io.ts.
|
|
42
|
-
*/
|
|
33
|
+
/** Write the full config atomically (backup+restore safe). Delegates to the shared core helper, which preserves symlinked config files. */
|
|
43
34
|
function writeConfigAtomic(config: LocalConfig): void {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
renameSync(tmpPath, path);
|
|
49
|
-
} catch {
|
|
50
|
-
try {
|
|
51
|
-
unlinkSync(tmpPath);
|
|
52
|
-
} catch {
|
|
53
|
-
// ignore — tmp file may not exist
|
|
54
|
-
}
|
|
55
|
-
writeFileSync(path, JSON.stringify(config, null, 2), "utf-8");
|
|
56
|
-
}
|
|
35
|
+
writeAtomicPreservingSymlinks(
|
|
36
|
+
getLocalConfigPath(),
|
|
37
|
+
JSON.stringify(config, null, 2),
|
|
38
|
+
);
|
|
57
39
|
}
|
|
58
40
|
|
|
59
41
|
/** Set the local override for a field on a given agent, preserving existing entries. */
|