refactor(coding-agent): simplify project trust changes

This commit is contained in:
Armin Ronacher
2026-06-03 14:25:07 +02:00
Unverified
parent faa794a8c9
commit 37f160cab6
7 changed files with 52 additions and 177 deletions
-12
View File
@@ -300,18 +300,6 @@ describe("parseArgs", () => {
});
});
describe("--force flag", () => {
test("parses --force flag", () => {
const result = parseArgs(["--force"]);
expect(result.force).toBe(true);
});
test("parses -f shorthand", () => {
const result = parseArgs(["-f"]);
expect(result.force).toBe(true);
});
});
describe("--offline flag", () => {
test("parses --offline flag", () => {
const result = parseArgs(["--offline"]);
@@ -1,4 +1,4 @@
import { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
import { mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
@@ -214,60 +214,6 @@ describe("package commands", () => {
}
});
it("does not treat update --force as a project trust override", async () => {
const globalPrefix = join(tempDir, "global-prefix");
const selfPackageDir = join(globalPrefix, "lib", "node_modules", "@earendil-works", "pi-coding-agent");
const fakeNpmPath = join(tempDir, "fake-npm-record.cjs");
const recordPath = join(tempDir, "update-force-records.json");
mkdirSync(selfPackageDir, { recursive: true });
mkdirSync(join(projectDir, ".pi"), { recursive: true });
writeFileSync(
fakeNpmPath,
`const fs=require("node:fs"),path=require("node:path"),args=process.argv.slice(2),prefix=args[args.indexOf("--prefix")+1];
if(args.includes("root")) {
console.log(path.join(prefix,"lib","node_modules"));
process.exit(0);
}
const records=fs.existsSync(${JSON.stringify(recordPath)})?JSON.parse(fs.readFileSync(${JSON.stringify(recordPath)},"utf-8")):[];
records.push(args);
fs.writeFileSync(${JSON.stringify(recordPath)},JSON.stringify(records));
`,
);
writeFileSync(
join(agentDir, "settings.json"),
JSON.stringify({ npmCommand: [originalExecPath, fakeNpmPath, "--prefix", globalPrefix] }, null, 2),
);
writeFileSync(
join(projectDir, ".pi", "settings.json"),
JSON.stringify({ packages: ["npm:@project/pkg"] }, null, 2),
);
process.env.PI_PACKAGE_DIR = selfPackageDir;
Object.defineProperty(process, "execPath", {
value: join(selfPackageDir, "dist", "cli.js"),
configurable: true,
});
const fetchMock = vi.fn();
vi.stubGlobal("fetch", fetchMock);
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
try {
await expect(main(["update", "--force"])).resolves.toBeUndefined();
expect(process.exitCode).toBeUndefined();
expect(errorSpy).not.toHaveBeenCalled();
expect(fetchMock).not.toHaveBeenCalled();
expect(existsSync(recordPath)).toBe(true);
const recordedCalls = JSON.parse(readFileSync(recordPath, "utf-8")) as string[][];
expect(recordedCalls.some((args) => args.some((arg) => arg.includes("@project/pkg")))).toBe(false);
expect(recordedCalls).toEqual([expect.arrayContaining(["install", "-g", PACKAGE_NAME])]);
} finally {
logSpy.mockRestore();
errorSpy.mockRestore();
}
});
it("uses global npmCommand and current package name for forced self updates without checking the api", async () => {
const globalPrefix = join(tempDir, "global-prefix");
const projectPrefix = join(tempDir, "project-prefix");
@@ -329,19 +329,6 @@ Content`,
expect(loader.getSystemPrompt()).toBe("You are a helpful assistant.");
});
it("should skip .pi SYSTEM.md when project .pi is not trusted", async () => {
const piDir = join(cwd, ".pi");
mkdirSync(piDir, { recursive: true });
writeFileSync(join(piDir, "SYSTEM.md"), "Project system prompt.");
writeFileSync(join(agentDir, "SYSTEM.md"), "Global system prompt.");
const settingsManager = SettingsManager.create(cwd, agentDir, { projectConfigTrusted: false });
const loader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });
await loader.reload();
expect(loader.getSystemPrompt()).toBe("Global system prompt.");
});
it("should skip .pi resources when project .pi is not trusted", async () => {
const piDir = join(cwd, ".pi");
const extensionsDir = join(piDir, "extensions");
@@ -352,6 +339,8 @@ Content`,
mkdirSync(skillDir, { recursive: true });
mkdirSync(promptsDir, { recursive: true });
mkdirSync(themesDir, { recursive: true });
writeFileSync(join(piDir, "SYSTEM.md"), "Project system prompt.");
writeFileSync(join(agentDir, "SYSTEM.md"), "Global system prompt.");
writeFileSync(join(extensionsDir, "project.ts"), `throw new Error("should not load");`);
writeFileSync(
join(skillDir, "SKILL.md"),
@@ -372,6 +361,7 @@ Project skill content`,
const loader = new DefaultResourceLoader({ cwd, agentDir, settingsManager });
await loader.reload();
expect(loader.getSystemPrompt()).toBe("Global system prompt.");
expect(loader.getExtensions().extensions).toHaveLength(0);
expect(loader.getExtensions().errors).toEqual([]);
expect(loader.getSkills().skills.some((skill) => skill.name === "project-skill")).toBe(false);
@@ -1,7 +1,6 @@
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import lockfile from "proper-lockfile";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { hasProjectPiDirectory, ProjectTrustStore } from "../src/core/trust-manager.ts";
@@ -44,29 +43,6 @@ describe("ProjectTrustStore", () => {
expect(readFileSync(trustPath, "utf-8")).toBe("{not json");
});
it("does not read trust.json while another process holds the trust lock", () => {
const trustPath = join(agentDir, "trust.json");
writeFileSync(trustPath, "{partial", "utf-8");
const release = lockfile.lockSync(agentDir, { realpath: false, lockfilePath: `${trustPath}.lock` });
const store = new ProjectTrustStore(agentDir);
try {
let error: unknown;
try {
store.get(cwd);
} catch (caught) {
error = caught;
}
expect(error).toBeInstanceOf(Error);
expect((error as { code?: unknown }).code).toBe("ELOCKED");
} finally {
release();
}
expect(() => store.get(cwd)).toThrow(/Failed to read trust store/);
});
it("detects project .pi directories", () => {
expect(hasProjectPiDirectory(cwd)).toBe(false);