pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
104 lines (103 loc) • 4.51 kB
JavaScript
import { Type } from "../clients/deps/typebox.js";
import { compactRenderResult } from "./render-compact.js";
import { LANGUAGES } from "./shared.js";
function createAstDumpToolWithName(astGrepClient, name) {
return {
name,
label: "AST-Grep Dump",
description: "Dump the tree-sitter AST for a source snippet using ast-grep CLI. Use when ast_grep_search returns zero matches and you need exact node kinds, field names, or nesting. Named nodes only by default; includeAnonymous=true shows punctuation/CST nodes too.",
promptSnippet: "Inspect AST node kinds before writing ast-grep patterns",
renderResult: compactRenderResult(({ details, args, isError, lineCount, text }) => {
const lang = details?.lang ?? (typeof args.lang === "string" ? args.lang : "");
if (isError) {
return `${name} ${lang} — ${text.split("\n")[0] ?? "error"}`.trim();
}
return `${name} ${lang} — ${lineCount} AST nodes`.replace(/\s+/g, " ");
}),
parameters: Type.Object({
source: Type.String({
description: "Source code snippet to parse and dump",
}),
lang: Type.String({
enum: [...LANGUAGES],
description: "Target language",
}),
includeAnonymous: Type.Optional(Type.Boolean({
description: "Show anonymous punctuation/CST nodes too (default false)",
})),
}),
async execute(_toolCallId, params, _signal, _onUpdate) {
const source = typeof params.source === "string" ? params.source : "";
const lang = typeof params.lang === "string"
? params.lang.replace(/^"|"$/g, "")
: "";
const includeAnonymous = params.includeAnonymous === true;
try {
if (!source.trim()) {
return {
content: [{ type: "text", text: "source is required" }],
isError: true,
details: { lang, includeAnonymous },
};
}
if (_signal.aborted) {
return {
content: [
{ type: "text", text: "Error: operation aborted" },
],
isError: true,
details: { lang, includeAnonymous },
};
}
if (!(await astGrepClient.ensureAvailable())) {
return {
content: [
{
type: "text",
text: "ast-grep CLI not found. Install: npm i -D @ast-grep/cli",
},
],
isError: true,
details: { lang, includeAnonymous },
};
}
if (_signal.aborted) {
return {
content: [
{ type: "text", text: "Error: operation aborted" },
],
isError: true,
details: { lang, includeAnonymous },
};
}
const result = await astGrepClient.dumpAst(source, lang, {
includeAnonymous,
});
if (result.error) {
return {
content: [
{ type: "text", text: `Error: ${result.error}` },
],
isError: true,
details: { lang, includeAnonymous },
};
}
return {
content: [{ type: "text", text: result.output ?? "" }],
details: { lang, includeAnonymous },
};
}
catch (err) {
const message = err instanceof Error ? err.message : String(err);
return {
content: [{ type: "text", text: `Error: ${message}` }],
isError: true,
details: { lang, includeAnonymous },
};
}
},
};
}
export function createAstGrepDumpTool(astGrepClient) {
return createAstDumpToolWithName(astGrepClient, "ast_grep_dump");
}