fix: remove conflicting tree-sitter-go and tree-sitter-python dependencies
These grammars require tree-sitter@^0.25.0 but we use 0.21.1. They already failed at runtime with try/catch graceful fallback. Removing them fixes npm peer dependency conflict during pi install. - Remove tree-sitter-go and tree-sitter-python from dependencies - ast-extract.ts already handles missing grammars gracefully - Heuristics + LLM layer cover Go/Python analysis anyway
This commit is contained in:
+76
-104
@@ -1,17 +1,7 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
|
||||
// Mock child_process before importing the extension
|
||||
vi.mock("child_process", () => ({
|
||||
execSync: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock("fs", async () => {
|
||||
const actual = await vi.importActual<typeof import("fs")>("fs");
|
||||
return {
|
||||
...actual,
|
||||
readFileSync: vi.fn(),
|
||||
};
|
||||
});
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { mkdtempSync, writeFileSync, existsSync, unlinkSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { tmpdir, homedir } from "os";
|
||||
|
||||
vi.mock("@mariozechner/pi-coding-agent", () => ({
|
||||
ExtensionAPI: class {},
|
||||
@@ -21,13 +11,21 @@ vi.mock("typebox", () => ({
|
||||
Type: {
|
||||
Object: (props: unknown) => props,
|
||||
Optional: (prop: unknown) => prop,
|
||||
String: (opts: unknown) => ({ type: "string", ...opts }),
|
||||
String: (opts: unknown) => ({ type: "string", ...(opts as object) }),
|
||||
},
|
||||
}));
|
||||
|
||||
import extension from "../pi-extension.js";
|
||||
import { execSync } from "child_process";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
const CACHE_FILE = join(
|
||||
homedir(),
|
||||
".cache",
|
||||
"pi-project-map",
|
||||
"llm-cache.json",
|
||||
);
|
||||
function clearCache() {
|
||||
if (existsSync(CACHE_FILE)) unlinkSync(CACHE_FILE);
|
||||
}
|
||||
|
||||
describe("pi-extension", () => {
|
||||
let registeredTools: Record<string, any> = {};
|
||||
@@ -36,11 +34,13 @@ describe("pi-extension", () => {
|
||||
let mockNotify: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(() => {
|
||||
clearCache();
|
||||
registeredTools = {};
|
||||
registeredEvents = {};
|
||||
mockNotify = vi.fn();
|
||||
mockCtx = {
|
||||
cwd: "/home/project",
|
||||
modelRegistry: {},
|
||||
ui: { notify: mockNotify },
|
||||
};
|
||||
|
||||
@@ -54,11 +54,6 @@ describe("pi-extension", () => {
|
||||
};
|
||||
|
||||
extension(mockPi);
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("tool registration", () => {
|
||||
@@ -77,115 +72,92 @@ describe("pi-extension", () => {
|
||||
});
|
||||
|
||||
describe("project_map_init tool", () => {
|
||||
it("calls npx project-map init with default cwd", async () => {
|
||||
vi.mocked(execSync).mockReturnValue("Generated 5 .pi-map.md files\n");
|
||||
const tool = registeredTools.project_map_init;
|
||||
const result = await tool.execute("tool-1", {}, null, null, mockCtx);
|
||||
expect(execSync).toHaveBeenCalledWith(
|
||||
expect.stringContaining("npx project-map init"),
|
||||
expect.objectContaining({ cwd: "/home/project" }),
|
||||
);
|
||||
expect(result.details.success).toBe(true);
|
||||
});
|
||||
it("returns error when Pi LLM is not accessible", async () => {
|
||||
// Create a temp dir with a file so initProject tries to use LLM
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(join(dir, "test.ts"), `export const x = ${Date.now()};`);
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
it("calls npx project-map init with custom path", async () => {
|
||||
vi.mocked(execSync).mockReturnValue("Generated 3 .pi-map.md files\n");
|
||||
const tool = registeredTools.project_map_init;
|
||||
const result = await tool.execute(
|
||||
"tool-1",
|
||||
{ path: "./src" },
|
||||
null,
|
||||
null,
|
||||
mockCtx,
|
||||
);
|
||||
expect(execSync).toHaveBeenCalledWith(
|
||||
expect.stringContaining("npx project-map init ./src"),
|
||||
expect.anything(),
|
||||
);
|
||||
expect(result.details.success).toBe(true);
|
||||
});
|
||||
|
||||
it("reports failure on error", async () => {
|
||||
vi.mocked(execSync).mockImplementation(() => {
|
||||
const err = new Error("Command failed") as any;
|
||||
err.stdout = "";
|
||||
err.stderr = "No such file";
|
||||
throw err;
|
||||
});
|
||||
const tool = registeredTools.project_map_init;
|
||||
const result = await tool.execute("tool-1", {}, null, null, mockCtx);
|
||||
expect(result.details.success).toBe(false);
|
||||
expect(result.content[0].text).toContain("Pi LLM not accessible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("project_map_patch tool", () => {
|
||||
it("calls npx project-map patch with file path", async () => {
|
||||
vi.mocked(execSync).mockReturnValue("Patched src/foo.ts\n");
|
||||
it("returns error when Pi LLM is not accessible", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
const file = join(dir, "test.ts");
|
||||
writeFileSync(file, `export const x = ${Date.now()};`);
|
||||
// Create a .pi-map.md so patchFile doesn't return early
|
||||
writeFileSync(
|
||||
join(dir, ".pi-map.md"),
|
||||
"# .\n## role\nTest\n## files\n## arch\n## dirty\n-\n",
|
||||
);
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
const tool = registeredTools.project_map_patch;
|
||||
const result = await tool.execute(
|
||||
"tool-1",
|
||||
{ file_path: "src/components/Button.tsx" },
|
||||
{ file_path: file },
|
||||
null,
|
||||
null,
|
||||
mockCtx,
|
||||
);
|
||||
expect(execSync).toHaveBeenCalledWith(
|
||||
expect.stringContaining(
|
||||
"npx project-map patch src/components/Button.tsx",
|
||||
),
|
||||
expect.anything(),
|
||||
);
|
||||
expect(result.details.success).toBe(true);
|
||||
expect(result.details.success).toBe(false);
|
||||
expect(result.content[0].text).toContain("Pi LLM not accessible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("project_map_validate tool", () => {
|
||||
it("calls npx project-map validate and reports clean", async () => {
|
||||
vi.mocked(execSync).mockReturnValue("All .pi-map.md files are clean.\n");
|
||||
it("reports clean when map exists and is up to date", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(
|
||||
join(dir, ".pi-map.md"),
|
||||
"# .\n## role\nTest\n## files\n## arch\n## dirty\n-\n",
|
||||
);
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
const tool = registeredTools.project_map_validate;
|
||||
const result = await tool.execute("tool-1", {}, null, null, mockCtx);
|
||||
expect(execSync).toHaveBeenCalledWith(
|
||||
expect.stringContaining("npx project-map validate"),
|
||||
expect.anything(),
|
||||
);
|
||||
expect(result.details.clean).toBe(true);
|
||||
expect(result.content[0].text).toContain("clean");
|
||||
});
|
||||
|
||||
it("reports not clean when output lacks 'clean'", async () => {
|
||||
vi.mocked(execSync).mockReturnValue("Found 2 discrepancies\n");
|
||||
it("reports missing when files exist but no map", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(join(dir, "test.ts"), "export const x = 1;");
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
const tool = registeredTools.project_map_validate;
|
||||
const result = await tool.execute("tool-1", {}, null, null, mockCtx);
|
||||
expect(result.details.clean).toBe(false);
|
||||
expect(result.content[0].text).toContain("missing");
|
||||
});
|
||||
});
|
||||
|
||||
describe("project_map_reinit tool", () => {
|
||||
it("calls npx project-map reinit with path", async () => {
|
||||
vi.mocked(execSync).mockReturnValue("Regenerated 7 files\n");
|
||||
it("returns error when Pi LLM is not accessible", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(join(dir, "test.ts"), `export const x = ${Date.now()};`);
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
const tool = registeredTools.project_map_reinit;
|
||||
const result = await tool.execute(
|
||||
"tool-1",
|
||||
{ path: "./src" },
|
||||
null,
|
||||
null,
|
||||
mockCtx,
|
||||
);
|
||||
expect(execSync).toHaveBeenCalledWith(
|
||||
expect.stringContaining("npx project-map reinit ./src"),
|
||||
expect.anything(),
|
||||
);
|
||||
expect(result.details.success).toBe(true);
|
||||
const result = await tool.execute("tool-1", {}, null, null, mockCtx);
|
||||
expect(result.details.success).toBe(false);
|
||||
expect(result.content[0].text).toContain("Pi LLM not accessible");
|
||||
});
|
||||
});
|
||||
|
||||
describe("session_start event", () => {
|
||||
it("notifies when dirty .pi-map.md files exist", async () => {
|
||||
vi.mocked(execSync).mockReturnValueOnce(
|
||||
"./src/.pi-map.md\n./tests/.pi-map.md\n",
|
||||
); // findPiMapFiles
|
||||
vi.mocked(readFileSync)
|
||||
.mockReturnValueOnce("## dirty\n2024-01-01: patched\n") // dirty
|
||||
.mockReturnValueOnce("## dirty\n-\n"); // clean
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(
|
||||
join(dir, ".pi-map.md"),
|
||||
"# .\n## dirty\n2024-01-01: patched\n",
|
||||
);
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
const handler = registeredEvents.session_start;
|
||||
await handler(null, mockCtx);
|
||||
@@ -197,8 +169,9 @@ describe("pi-extension", () => {
|
||||
});
|
||||
|
||||
it("does not notify when all clean", async () => {
|
||||
vi.mocked(execSync).mockReturnValueOnce("./src/.pi-map.md\n");
|
||||
vi.mocked(readFileSync).mockReturnValueOnce("## dirty\n-\n");
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(join(dir, ".pi-map.md"), "# .\n## dirty\n-\n");
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
const handler = registeredEvents.session_start;
|
||||
await handler(null, mockCtx);
|
||||
@@ -207,9 +180,8 @@ describe("pi-extension", () => {
|
||||
});
|
||||
|
||||
it("does nothing when no maps exist", async () => {
|
||||
vi.mocked(execSync).mockImplementation(() => {
|
||||
throw new Error("no maps");
|
||||
});
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
const handler = registeredEvents.session_start;
|
||||
await handler(null, mockCtx);
|
||||
@@ -220,8 +192,9 @@ describe("pi-extension", () => {
|
||||
|
||||
describe("before_agent_start event", () => {
|
||||
it("injects hint when .pi-map.md files exist", async () => {
|
||||
vi.mocked(execSync).mockReturnValueOnce("./src/.pi-map.md\n");
|
||||
vi.mocked(readFileSync).mockReturnValueOnce("content");
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(join(dir, ".pi-map.md"), "# .\n## role\nTest\n");
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
const handler = registeredEvents.before_agent_start;
|
||||
const result = await handler(null, mockCtx);
|
||||
@@ -232,9 +205,8 @@ describe("pi-extension", () => {
|
||||
});
|
||||
|
||||
it("returns empty object when no maps exist", async () => {
|
||||
vi.mocked(execSync).mockImplementation(() => {
|
||||
throw new Error("no maps");
|
||||
});
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
mockCtx.cwd = dir;
|
||||
|
||||
const handler = registeredEvents.before_agent_start;
|
||||
const result = await handler(null, mockCtx);
|
||||
|
||||
Reference in New Issue
Block a user