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:
2026-06-09 23:33:57 +02:00
parent 69d3acda5d
commit e66ed25cee
8 changed files with 273 additions and 225 deletions
+133 -69
View File
@@ -1,44 +1,62 @@
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Type } from "typebox";
import { execSync } from "child_process";
import { readFileSync } from "fs";
import { join } from "path";
import { readFileSync, readdirSync, statSync } from "fs";
import { join, relative } from "path";
import {
initProject,
patchFile,
validateMaps,
reinitPath,
} from "./src/index.js";
import { createLLMClient } from "./src/llm-client.js";
import { LLMError } from "./src/llm-error.js";
function runCommand(
command: string,
args: string[],
cwd: string,
): { stdout: string; stderr: string; success: boolean } {
try {
const result = execSync(`npx project-map ${command} ${args.join(" ")}`, {
cwd,
encoding: "utf8",
stdio: ["pipe", "pipe", "pipe"],
});
return { stdout: result, stderr: "", success: true };
} catch (error: any) {
return {
stdout: error.stdout || "",
stderr: error.stderr || error.message,
success: false,
};
}
/**
* Get the LLM client for Pi runtime.
*
* When running inside Pi, we ALWAYS use Pi's native LLM.
* If Pi's LLM is not accessible, this throws a hard error.
* We ignore all external configuration (env vars, config files, etc.)
* because inside Pi we must use Pi's model exclusively.
*/
function getPiLLMClient(ctx: any) {
return createLLMClient("pi", { extensionContext: ctx });
}
function findPiMapFiles(cwd: string): string[] {
try {
const result = execSync('find . -name ".pi-map.md" -type f', {
cwd,
encoding: "utf8",
});
return result.trim().split("\n").filter(Boolean);
} catch {
return [];
const results: string[] = [];
function walk(dir: string) {
let entries: import("fs").Dirent[];
try {
entries = readdirSync(dir, { withFileTypes: true });
} catch {
return;
}
for (const entry of entries) {
if (
entry.isDirectory() &&
!entry.name.startsWith(".") &&
entry.name !== "node_modules"
) {
walk(join(dir, entry.name));
}
}
try {
statSync(join(dir, ".pi-map.md"));
results.push(relative(cwd, join(dir, ".pi-map.md")));
} catch {
// no map in this dir
}
}
walk(cwd);
return results;
}
function isDirty(content: string): boolean {
return content.includes("## dirty") && !content.includes("## dirty\n-");
}
export default function (pi: ExtensionAPI) {
// Register custom tools
pi.registerTool({
name: "project_map_init",
label: "Project Map Init",
@@ -58,16 +76,26 @@ export default function (pi: ExtensionAPI) {
),
}),
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
const targetPath = params.path || ctx.cwd;
const result = runCommand(
"init",
targetPath === ctx.cwd ? [] : [targetPath],
ctx.cwd,
);
return {
content: [{ type: "text", text: result.stdout || result.stderr }],
details: { success: result.success, cwd: ctx.cwd },
};
try {
const targetPath = params.path || ctx.cwd;
const client = getPiLLMClient(ctx);
await initProject(targetPath, { verbose: false, llmClient: client });
return {
content: [
{
type: "text",
text: `Generated .pi-map.md files for ${targetPath}`,
},
],
details: { success: true, cwd: ctx.cwd },
};
} catch (err: any) {
const msg = err instanceof LLMError ? err.message : String(err);
return {
content: [{ type: "text", text: `Error: ${msg}` }],
details: { success: false, error: msg },
};
}
},
});
@@ -87,11 +115,25 @@ export default function (pi: ExtensionAPI) {
}),
}),
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
const result = runCommand("patch", [params.file_path], ctx.cwd);
return {
content: [{ type: "text", text: result.stdout || result.stderr }],
details: { success: result.success },
};
try {
const client = getPiLLMClient(ctx);
await patchFile(params.file_path, client);
return {
content: [
{
type: "text",
text: `Patched map for ${params.file_path}`,
},
],
details: { success: true },
};
} catch (err: any) {
const msg = err instanceof LLMError ? err.message : String(err);
return {
content: [{ type: "text", text: `Error: ${msg}` }],
details: { success: false, error: msg },
};
}
},
});
@@ -112,19 +154,29 @@ export default function (pi: ExtensionAPI) {
),
}),
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
const targetPath = params.path || ctx.cwd;
const result = runCommand(
"validate",
targetPath === ctx.cwd ? [] : [targetPath],
ctx.cwd,
);
return {
content: [{ type: "text", text: result.stdout || result.stderr }],
details: {
success: result.success,
clean: result.stdout.includes("clean"),
},
};
try {
const targetPath = params.path || ctx.cwd;
const result = await validateMaps(targetPath, {
fix: false,
verbose: false,
});
const text = result.clean
? "All .pi-map.md files are clean."
: `Found ${result.discrepancies.length} discrepancies:\n` +
result.discrepancies
.map((d) => ` [${d.type}] ${d.path}: ${d.message}`)
.join("\n");
return {
content: [{ type: "text", text }],
details: { success: true, clean: result.clean },
};
} catch (err: any) {
const msg = err instanceof LLMError ? err.message : String(err);
return {
content: [{ type: "text", text: `Error: ${msg}` }],
details: { success: false, error: msg },
};
}
},
});
@@ -145,12 +197,26 @@ export default function (pi: ExtensionAPI) {
),
}),
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
const args = params.path ? [params.path] : [];
const result = runCommand("reinit", args, ctx.cwd);
return {
content: [{ type: "text", text: result.stdout || result.stderr }],
details: { success: result.success },
};
try {
const targetPath = params.path || ctx.cwd;
const client = getPiLLMClient(ctx);
await reinitPath(targetPath, { verbose: false, llmClient: client });
return {
content: [
{
type: "text",
text: `Regenerated maps for ${targetPath}`,
},
],
details: { success: true },
};
} catch (err: any) {
const msg = err instanceof LLMError ? err.message : String(err);
return {
content: [{ type: "text", text: `Error: ${msg}` }],
details: { success: false, error: msg },
};
}
},
});
@@ -159,11 +225,10 @@ export default function (pi: ExtensionAPI) {
const mapFiles = findPiMapFiles(ctx.cwd);
if (mapFiles.length === 0) return;
// Check for dirty markers
const dirtyFiles = mapFiles.filter((f) => {
try {
const content = readFileSync(join(ctx.cwd, f), "utf8");
return content.includes("## dirty") && !content.includes("## dirty\n-");
return isDirty(content);
} catch {
return false;
}
@@ -179,7 +244,6 @@ export default function (pi: ExtensionAPI) {
// Inject maintenance instructions before agent starts
pi.on("before_agent_start", async (_event, _ctx) => {
// Only inject if .pi-map.md files exist
const mapFiles = findPiMapFiles(_ctx.cwd);
if (mapFiles.length === 0) return {};
@@ -188,7 +252,7 @@ export default function (pi: ExtensionAPI) {
customType: "pi-project-map-hint",
content:
"📋 Project map active: If you modify any source file, run `project_map_patch` with the file path. If you suspect staleness, run `project_map_validate`.",
display: false, // Don't show in UI, only in LLM context
display: false,
},
};
});