@compilr-dev/agents-coding-python 0.1.0
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/LICENSE +21 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/parser/index.d.ts +6 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +5 -0
- package/dist/parser/node-types.d.ts +119 -0
- package/dist/parser/node-types.d.ts.map +1 -0
- package/dist/parser/node-types.js +4 -0
- package/dist/parser/python-parser.d.ts +85 -0
- package/dist/parser/python-parser.d.ts.map +1 -0
- package/dist/parser/python-parser.js +477 -0
- package/dist/skills/index.d.ts +26 -0
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +36 -0
- package/dist/skills/python-best-practices.d.ts +7 -0
- package/dist/skills/python-best-practices.d.ts.map +1 -0
- package/dist/skills/python-best-practices.js +78 -0
- package/dist/skills/python-code-health.d.ts +7 -0
- package/dist/skills/python-code-health.d.ts.map +1 -0
- package/dist/skills/python-code-health.js +209 -0
- package/dist/skills/python-code-structure.d.ts +7 -0
- package/dist/skills/python-code-structure.d.ts.map +1 -0
- package/dist/skills/python-code-structure.js +155 -0
- package/dist/skills/python-dependency-audit.d.ts +7 -0
- package/dist/skills/python-dependency-audit.d.ts.map +1 -0
- package/dist/skills/python-dependency-audit.js +246 -0
- package/dist/skills/python-refactor-impact.d.ts +7 -0
- package/dist/skills/python-refactor-impact.d.ts.map +1 -0
- package/dist/skills/python-refactor-impact.js +232 -0
- package/dist/tools/extract-docstrings.d.ts +70 -0
- package/dist/tools/extract-docstrings.d.ts.map +1 -0
- package/dist/tools/extract-docstrings.js +575 -0
- package/dist/tools/find-dead-code.d.ts +62 -0
- package/dist/tools/find-dead-code.d.ts.map +1 -0
- package/dist/tools/find-dead-code.js +422 -0
- package/dist/tools/find-duplicates.d.ts +65 -0
- package/dist/tools/find-duplicates.d.ts.map +1 -0
- package/dist/tools/find-duplicates.js +289 -0
- package/dist/tools/find-implementations.d.ts +71 -0
- package/dist/tools/find-implementations.d.ts.map +1 -0
- package/dist/tools/find-implementations.js +342 -0
- package/dist/tools/find-patterns.d.ts +71 -0
- package/dist/tools/find-patterns.d.ts.map +1 -0
- package/dist/tools/find-patterns.js +477 -0
- package/dist/tools/find-references.d.ts +66 -0
- package/dist/tools/find-references.d.ts.map +1 -0
- package/dist/tools/find-references.js +306 -0
- package/dist/tools/find-symbol.d.ts +86 -0
- package/dist/tools/find-symbol.d.ts.map +1 -0
- package/dist/tools/find-symbol.js +414 -0
- package/dist/tools/get-call-graph.d.ts +89 -0
- package/dist/tools/get-call-graph.d.ts.map +1 -0
- package/dist/tools/get-call-graph.js +431 -0
- package/dist/tools/get-class-hierarchy.d.ts +38 -0
- package/dist/tools/get-class-hierarchy.d.ts.map +1 -0
- package/dist/tools/get-class-hierarchy.js +289 -0
- package/dist/tools/get-complexity.d.ts +61 -0
- package/dist/tools/get-complexity.d.ts.map +1 -0
- package/dist/tools/get-complexity.js +384 -0
- package/dist/tools/get-dependency-graph.d.ts +85 -0
- package/dist/tools/get-dependency-graph.d.ts.map +1 -0
- package/dist/tools/get-dependency-graph.js +387 -0
- package/dist/tools/get-exports.d.ts +78 -0
- package/dist/tools/get-exports.d.ts.map +1 -0
- package/dist/tools/get-exports.js +437 -0
- package/dist/tools/get-file-structure.d.ts +28 -0
- package/dist/tools/get-file-structure.d.ts.map +1 -0
- package/dist/tools/get-file-structure.js +186 -0
- package/dist/tools/get-imports.d.ts +34 -0
- package/dist/tools/get-imports.d.ts.map +1 -0
- package/dist/tools/get-imports.js +455 -0
- package/dist/tools/get-signature.d.ts +100 -0
- package/dist/tools/get-signature.d.ts.map +1 -0
- package/dist/tools/get-signature.js +800 -0
- package/dist/tools/index.d.ts +55 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +75 -0
- package/dist/tools/types.d.ts +378 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +4 -0
- package/package.json +85 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* getExports Tool
|
|
3
|
+
*
|
|
4
|
+
* Get all exports from a Python module.
|
|
5
|
+
* Analyzes __all__ declarations and identifies public API.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from "node:fs/promises";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import { defineTool, createSuccessResult, createErrorResult, } from "@compilr-dev/agents";
|
|
10
|
+
import { parseFile, parseFunction, parseClass, } from "../parser/python-parser.js";
|
|
11
|
+
// Tool description
|
|
12
|
+
const TOOL_DESCRIPTION = `Get all exports from a Python module.
|
|
13
|
+
Analyzes __all__ declarations and identifies public API surface.
|
|
14
|
+
Use this to understand what a module exposes to other modules.`;
|
|
15
|
+
// Tool input schema
|
|
16
|
+
const TOOL_INPUT_SCHEMA = {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: {
|
|
19
|
+
path: {
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "File to analyze",
|
|
22
|
+
},
|
|
23
|
+
includeReExports: {
|
|
24
|
+
type: "boolean",
|
|
25
|
+
description: "Include re-exports from imports (default: true)",
|
|
26
|
+
default: true,
|
|
27
|
+
},
|
|
28
|
+
includePrivate: {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
description: "Include private symbols with _ prefix (default: false)",
|
|
31
|
+
default: false,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
required: ["path"],
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* getExports tool
|
|
38
|
+
*/
|
|
39
|
+
export const getExportsTool = defineTool({
|
|
40
|
+
name: "get_exports_python",
|
|
41
|
+
description: TOOL_DESCRIPTION,
|
|
42
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
43
|
+
execute: executeGetExports,
|
|
44
|
+
});
|
|
45
|
+
/**
|
|
46
|
+
* Execute the getExports tool
|
|
47
|
+
*/
|
|
48
|
+
async function executeGetExports(input) {
|
|
49
|
+
const { path: inputPath, includeReExports = true, includePrivate = false, } = input;
|
|
50
|
+
// Validate input
|
|
51
|
+
if (!inputPath || inputPath.trim().length === 0) {
|
|
52
|
+
return createErrorResult("Path is required");
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const resolvedPath = path.resolve(inputPath);
|
|
56
|
+
// Check if file exists
|
|
57
|
+
try {
|
|
58
|
+
await fs.access(resolvedPath);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return createErrorResult(`File not found: ${resolvedPath}`);
|
|
62
|
+
}
|
|
63
|
+
const stats = await fs.stat(resolvedPath);
|
|
64
|
+
if (!stats.isFile()) {
|
|
65
|
+
return createErrorResult(`Path must be a file: ${resolvedPath}`);
|
|
66
|
+
}
|
|
67
|
+
if (!resolvedPath.endsWith(".py")) {
|
|
68
|
+
return createErrorResult("File must be a Python file (.py)");
|
|
69
|
+
}
|
|
70
|
+
// Analyze the file
|
|
71
|
+
const result = await analyzeExports(resolvedPath, includeReExports, includePrivate);
|
|
72
|
+
return createSuccessResult({
|
|
73
|
+
...result,
|
|
74
|
+
path: resolvedPath,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
79
|
+
return createErrorResult(`Failed to analyze exports: ${message}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Analyze exports in a Python file
|
|
84
|
+
*/
|
|
85
|
+
async function analyzeExports(filePath, includeReExports, includePrivate) {
|
|
86
|
+
const parseResult = await parseFile(filePath);
|
|
87
|
+
const { tree, source } = parseResult;
|
|
88
|
+
const allDeclaration = [];
|
|
89
|
+
const publicExports = [];
|
|
90
|
+
const privateSymbols = [];
|
|
91
|
+
const reExports = [];
|
|
92
|
+
// Track names in __all__
|
|
93
|
+
const allNames = new Set();
|
|
94
|
+
// First pass: find __all__ declaration
|
|
95
|
+
for (const child of tree.rootNode.children) {
|
|
96
|
+
if (child.type === "expression_statement") {
|
|
97
|
+
const expr = child.children[0];
|
|
98
|
+
if (expr?.type === "assignment") {
|
|
99
|
+
const left = expr.childForFieldName("left");
|
|
100
|
+
if (left?.text === "__all__") {
|
|
101
|
+
const right = expr.childForFieldName("right");
|
|
102
|
+
if (right) {
|
|
103
|
+
const names = extractAllNames(right);
|
|
104
|
+
names.forEach((n) => {
|
|
105
|
+
allDeclaration.push(n);
|
|
106
|
+
allNames.add(n);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Second pass: extract all module-level definitions
|
|
114
|
+
for (const child of tree.rootNode.children) {
|
|
115
|
+
// Function definitions
|
|
116
|
+
if (child.type === "function_definition") {
|
|
117
|
+
const funcInfo = parseFunction(child, source);
|
|
118
|
+
const isPrivate = funcInfo.name.startsWith("_");
|
|
119
|
+
const inAll = allNames.has(funcInfo.name);
|
|
120
|
+
const symbol = {
|
|
121
|
+
name: funcInfo.name,
|
|
122
|
+
kind: "function",
|
|
123
|
+
line: funcInfo.line,
|
|
124
|
+
signature: buildFunctionSignature(child, source),
|
|
125
|
+
docstring: extractDocstring(child),
|
|
126
|
+
isPrivate,
|
|
127
|
+
inAll,
|
|
128
|
+
};
|
|
129
|
+
if (isPrivate && !inAll) {
|
|
130
|
+
if (includePrivate)
|
|
131
|
+
privateSymbols.push(symbol);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
publicExports.push(symbol);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
// Async function definitions
|
|
138
|
+
else if (child.type === "async_function_definition") {
|
|
139
|
+
const funcDef = child.children.find((c) => c.type === "function_definition");
|
|
140
|
+
if (funcDef) {
|
|
141
|
+
const funcInfo = parseFunction(funcDef, source);
|
|
142
|
+
const isPrivate = funcInfo.name.startsWith("_");
|
|
143
|
+
const inAll = allNames.has(funcInfo.name);
|
|
144
|
+
const symbol = {
|
|
145
|
+
name: funcInfo.name,
|
|
146
|
+
kind: "async_function",
|
|
147
|
+
line: funcInfo.line,
|
|
148
|
+
signature: `async ${buildFunctionSignature(funcDef, source)}`,
|
|
149
|
+
docstring: extractDocstring(funcDef),
|
|
150
|
+
isPrivate,
|
|
151
|
+
inAll,
|
|
152
|
+
};
|
|
153
|
+
if (isPrivate && !inAll) {
|
|
154
|
+
if (includePrivate)
|
|
155
|
+
privateSymbols.push(symbol);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
publicExports.push(symbol);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Class definitions
|
|
163
|
+
else if (child.type === "class_definition") {
|
|
164
|
+
const classInfo = parseClass(child, source);
|
|
165
|
+
const isPrivate = classInfo.name.startsWith("_");
|
|
166
|
+
const inAll = allNames.has(classInfo.name);
|
|
167
|
+
const symbol = {
|
|
168
|
+
name: classInfo.name,
|
|
169
|
+
kind: "class",
|
|
170
|
+
line: classInfo.line,
|
|
171
|
+
signature: buildClassSignature(child, source),
|
|
172
|
+
docstring: extractDocstring(child),
|
|
173
|
+
isPrivate,
|
|
174
|
+
inAll,
|
|
175
|
+
};
|
|
176
|
+
if (isPrivate && !inAll) {
|
|
177
|
+
if (includePrivate)
|
|
178
|
+
privateSymbols.push(symbol);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
publicExports.push(symbol);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Decorated definitions
|
|
185
|
+
else if (child.type === "decorated_definition") {
|
|
186
|
+
const definition = child.children.find((c) => c.type === "function_definition" ||
|
|
187
|
+
c.type === "async_function_definition" ||
|
|
188
|
+
c.type === "class_definition");
|
|
189
|
+
if (definition) {
|
|
190
|
+
if (definition.type === "class_definition") {
|
|
191
|
+
const classInfo = parseClass(definition, source);
|
|
192
|
+
const isPrivate = classInfo.name.startsWith("_");
|
|
193
|
+
const inAll = allNames.has(classInfo.name);
|
|
194
|
+
const symbol = {
|
|
195
|
+
name: classInfo.name,
|
|
196
|
+
kind: "class",
|
|
197
|
+
line: classInfo.line,
|
|
198
|
+
signature: buildClassSignature(definition, source),
|
|
199
|
+
docstring: extractDocstring(definition),
|
|
200
|
+
isPrivate,
|
|
201
|
+
inAll,
|
|
202
|
+
};
|
|
203
|
+
if (isPrivate && !inAll) {
|
|
204
|
+
if (includePrivate)
|
|
205
|
+
privateSymbols.push(symbol);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
publicExports.push(symbol);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
const funcDef = definition.type === "async_function_definition"
|
|
213
|
+
? definition.children.find((c) => c.type === "function_definition")
|
|
214
|
+
: definition;
|
|
215
|
+
if (funcDef) {
|
|
216
|
+
const funcInfo = parseFunction(funcDef, source);
|
|
217
|
+
const isPrivate = funcInfo.name.startsWith("_");
|
|
218
|
+
const inAll = allNames.has(funcInfo.name);
|
|
219
|
+
const isAsync = definition.type === "async_function_definition";
|
|
220
|
+
const symbol = {
|
|
221
|
+
name: funcInfo.name,
|
|
222
|
+
kind: isAsync ? "async_function" : "function",
|
|
223
|
+
line: funcInfo.line,
|
|
224
|
+
signature: isAsync
|
|
225
|
+
? `async ${buildFunctionSignature(funcDef, source)}`
|
|
226
|
+
: buildFunctionSignature(funcDef, source),
|
|
227
|
+
docstring: extractDocstring(funcDef),
|
|
228
|
+
isPrivate,
|
|
229
|
+
inAll,
|
|
230
|
+
};
|
|
231
|
+
if (isPrivate && !inAll) {
|
|
232
|
+
if (includePrivate)
|
|
233
|
+
privateSymbols.push(symbol);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
publicExports.push(symbol);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Variable assignments (module-level constants/variables)
|
|
243
|
+
else if (child.type === "expression_statement") {
|
|
244
|
+
const expr = child.children[0];
|
|
245
|
+
if (expr?.type === "assignment") {
|
|
246
|
+
const left = expr.childForFieldName("left");
|
|
247
|
+
if (left?.type === "identifier" && left.text !== "__all__") {
|
|
248
|
+
const name = left.text;
|
|
249
|
+
const isPrivate = name.startsWith("_");
|
|
250
|
+
const inAll = allNames.has(name);
|
|
251
|
+
const isConstant = name === name.toUpperCase();
|
|
252
|
+
const symbol = {
|
|
253
|
+
name,
|
|
254
|
+
kind: isConstant ? "constant" : "variable",
|
|
255
|
+
line: child.startPosition.row + 1,
|
|
256
|
+
isPrivate,
|
|
257
|
+
inAll,
|
|
258
|
+
};
|
|
259
|
+
if (isPrivate && !inAll) {
|
|
260
|
+
if (includePrivate)
|
|
261
|
+
privateSymbols.push(symbol);
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
publicExports.push(symbol);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Import statements (for re-exports)
|
|
270
|
+
else if (includeReExports && child.type === "import_from_statement") {
|
|
271
|
+
const reExport = extractReExport(child, source);
|
|
272
|
+
if (reExport) {
|
|
273
|
+
reExports.push(reExport);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// Calculate statistics
|
|
278
|
+
const exportStats = {
|
|
279
|
+
totalExports: publicExports.length,
|
|
280
|
+
functions: publicExports.filter((e) => e.kind === "function" || e.kind === "async_function").length,
|
|
281
|
+
classes: publicExports.filter((e) => e.kind === "class").length,
|
|
282
|
+
variables: publicExports.filter((e) => e.kind === "variable" || e.kind === "constant").length,
|
|
283
|
+
reExports: reExports.length,
|
|
284
|
+
hasAll: allDeclaration.length > 0,
|
|
285
|
+
allCount: allDeclaration.length > 0 ? allDeclaration.length : undefined,
|
|
286
|
+
};
|
|
287
|
+
return {
|
|
288
|
+
allDeclaration: allDeclaration.length > 0 ? allDeclaration : undefined,
|
|
289
|
+
publicExports,
|
|
290
|
+
privateSymbols: includePrivate && privateSymbols.length > 0 ? privateSymbols : undefined,
|
|
291
|
+
reExports: includeReExports && reExports.length > 0 ? reExports : undefined,
|
|
292
|
+
stats: exportStats,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Extract names from __all__ list
|
|
297
|
+
*/
|
|
298
|
+
function extractAllNames(node) {
|
|
299
|
+
const names = [];
|
|
300
|
+
if (node.type === "list") {
|
|
301
|
+
for (const child of node.children) {
|
|
302
|
+
if (child.type === "string") {
|
|
303
|
+
// Remove quotes
|
|
304
|
+
const text = child.text;
|
|
305
|
+
const name = text.slice(1, -1);
|
|
306
|
+
if (name)
|
|
307
|
+
names.push(name);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
else if (node.type === "tuple") {
|
|
312
|
+
for (const child of node.children) {
|
|
313
|
+
if (child.type === "string") {
|
|
314
|
+
const text = child.text;
|
|
315
|
+
const name = text.slice(1, -1);
|
|
316
|
+
if (name)
|
|
317
|
+
names.push(name);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return names;
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Build function signature string
|
|
325
|
+
*/
|
|
326
|
+
function buildFunctionSignature(funcNode, _source) {
|
|
327
|
+
const nameNode = funcNode.childForFieldName("name");
|
|
328
|
+
const paramsNode = funcNode.childForFieldName("parameters");
|
|
329
|
+
const returnNode = funcNode.childForFieldName("return_type");
|
|
330
|
+
const name = nameNode?.text ?? "unknown";
|
|
331
|
+
const params = paramsNode?.text ?? "()";
|
|
332
|
+
const returnType = returnNode ? ` -> ${returnNode.text}` : "";
|
|
333
|
+
return `def ${name}${params}${returnType}`;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Build class signature string
|
|
337
|
+
*/
|
|
338
|
+
function buildClassSignature(classNode, _source) {
|
|
339
|
+
const nameNode = classNode.childForFieldName("name");
|
|
340
|
+
const superclassNode = classNode.childForFieldName("superclasses");
|
|
341
|
+
const name = nameNode?.text ?? "unknown";
|
|
342
|
+
const bases = superclassNode?.text ?? "";
|
|
343
|
+
return bases ? `class ${name}${bases}` : `class ${name}`;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Extract docstring from a definition
|
|
347
|
+
*/
|
|
348
|
+
function extractDocstring(node) {
|
|
349
|
+
const body = node.childForFieldName("body");
|
|
350
|
+
if (!body)
|
|
351
|
+
return undefined;
|
|
352
|
+
for (const child of body.children) {
|
|
353
|
+
if (child.type === "expression_statement") {
|
|
354
|
+
const string = child.children.find((c) => c.type === "string" || c.type === "concatenated_string");
|
|
355
|
+
if (string) {
|
|
356
|
+
// Extract first line of docstring
|
|
357
|
+
let content = string.text;
|
|
358
|
+
if (content.startsWith('"""') || content.startsWith("'''")) {
|
|
359
|
+
content = content.slice(3, -3);
|
|
360
|
+
}
|
|
361
|
+
else if (content.startsWith('"') || content.startsWith("'")) {
|
|
362
|
+
content = content.slice(1, -1);
|
|
363
|
+
}
|
|
364
|
+
const firstLine = content.trim().split("\n")[0];
|
|
365
|
+
return firstLine.length > 100
|
|
366
|
+
? firstLine.slice(0, 100) + "..."
|
|
367
|
+
: firstLine;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Stop after first non-docstring statement
|
|
371
|
+
if (child.type !== "expression_statement" && child.type !== "comment") {
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return undefined;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Extract re-export info from import statement
|
|
379
|
+
*/
|
|
380
|
+
function extractReExport(node, _source) {
|
|
381
|
+
// from module import name, name2
|
|
382
|
+
// from module import *
|
|
383
|
+
const moduleNode = node.childForFieldName("module_name");
|
|
384
|
+
if (!moduleNode)
|
|
385
|
+
return null;
|
|
386
|
+
const moduleName = moduleNode.text;
|
|
387
|
+
const isRelative = moduleName.startsWith(".");
|
|
388
|
+
const line = node.startPosition.row + 1;
|
|
389
|
+
// Check for star import
|
|
390
|
+
const hasWildcard = node.children.some((c) => c.type === "wildcard_import" || c.text === "*");
|
|
391
|
+
if (hasWildcard) {
|
|
392
|
+
return {
|
|
393
|
+
symbols: "*",
|
|
394
|
+
from: moduleName,
|
|
395
|
+
line,
|
|
396
|
+
isRelative,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
// Named imports
|
|
400
|
+
const symbols = [];
|
|
401
|
+
for (const child of node.children) {
|
|
402
|
+
if (child.type === "dotted_name" && child !== moduleNode) {
|
|
403
|
+
symbols.push(child.text);
|
|
404
|
+
}
|
|
405
|
+
else if (child.type === "aliased_import") {
|
|
406
|
+
const nameNode = child.childForFieldName("name");
|
|
407
|
+
if (nameNode) {
|
|
408
|
+
symbols.push(nameNode.text);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (symbols.length === 0)
|
|
413
|
+
return null;
|
|
414
|
+
return {
|
|
415
|
+
symbols,
|
|
416
|
+
from: moduleName,
|
|
417
|
+
line,
|
|
418
|
+
isRelative,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Factory function to create a customized getExports tool
|
|
423
|
+
*/
|
|
424
|
+
export function createGetExportsTool(options) {
|
|
425
|
+
return defineTool({
|
|
426
|
+
name: "get_exports_python",
|
|
427
|
+
description: TOOL_DESCRIPTION,
|
|
428
|
+
inputSchema: TOOL_INPUT_SCHEMA,
|
|
429
|
+
execute: async (input) => {
|
|
430
|
+
return executeGetExports({
|
|
431
|
+
...input,
|
|
432
|
+
includeReExports: input.includeReExports ?? options?.defaultIncludeReExports ?? true,
|
|
433
|
+
includePrivate: input.includePrivate ?? options?.defaultIncludePrivate ?? false,
|
|
434
|
+
});
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get File Structure Tool
|
|
3
|
+
*
|
|
4
|
+
* Analyzes a Python file and returns its structural overview including
|
|
5
|
+
* classes, functions, variables, and imports.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Input for getFileStructure tool
|
|
9
|
+
*/
|
|
10
|
+
export interface GetFileStructureInput {
|
|
11
|
+
/** Path to Python file */
|
|
12
|
+
path: string;
|
|
13
|
+
/** Include private members (starting with _) */
|
|
14
|
+
includePrivate?: boolean;
|
|
15
|
+
/** Maximum nesting depth */
|
|
16
|
+
maxDepth?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get file structure tool
|
|
20
|
+
*/
|
|
21
|
+
export declare const getFileStructureTool: import("@compilr-dev/agents").Tool<GetFileStructureInput>;
|
|
22
|
+
/**
|
|
23
|
+
* Factory function to create getFileStructure tool with custom options
|
|
24
|
+
*/
|
|
25
|
+
export declare function createGetFileStructureTool(options?: {
|
|
26
|
+
baseDir?: string;
|
|
27
|
+
}): import("@compilr-dev/agents").Tool<GetFileStructureInput>;
|
|
28
|
+
//# sourceMappingURL=get-file-structure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-file-structure.d.ts","sourceRoot":"","sources":["../../src/tools/get-file-structure.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAqBH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,gDAAgD;IAChD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,4BAA4B;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB,2DAwB/B,CAAC;AAyJH;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,6DAmCxE"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get File Structure Tool
|
|
3
|
+
*
|
|
4
|
+
* Analyzes a Python file and returns its structural overview including
|
|
5
|
+
* classes, functions, variables, and imports.
|
|
6
|
+
*/
|
|
7
|
+
import { defineTool, createSuccessResult, createErrorResult, } from "@compilr-dev/agents";
|
|
8
|
+
import { stat } from "node:fs/promises";
|
|
9
|
+
import { resolve } from "node:path";
|
|
10
|
+
import { parseFile, parseClass, parseFunction, parseImport, parseVariable, parseDecorators, extractDocstring, } from "../parser/python-parser.js";
|
|
11
|
+
/**
|
|
12
|
+
* Get file structure tool
|
|
13
|
+
*/
|
|
14
|
+
export const getFileStructureTool = defineTool({
|
|
15
|
+
name: "get_file_structure_python",
|
|
16
|
+
description: "Get structural overview of a Python file including classes, functions, variables, and imports. " +
|
|
17
|
+
"Useful for understanding file organization and API surface.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
path: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "Path to Python file (.py)",
|
|
24
|
+
},
|
|
25
|
+
includePrivate: {
|
|
26
|
+
type: "boolean",
|
|
27
|
+
description: "Include private members (starting with _)",
|
|
28
|
+
},
|
|
29
|
+
maxDepth: {
|
|
30
|
+
type: "number",
|
|
31
|
+
description: "Maximum nesting depth to analyze",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
required: ["path"],
|
|
35
|
+
},
|
|
36
|
+
execute: executeGetFileStructure,
|
|
37
|
+
});
|
|
38
|
+
async function executeGetFileStructure(input) {
|
|
39
|
+
const filePath = resolve(input.path);
|
|
40
|
+
const includePrivate = input.includePrivate ?? false;
|
|
41
|
+
// Validate file exists and is a Python file
|
|
42
|
+
try {
|
|
43
|
+
const stats = await stat(filePath);
|
|
44
|
+
if (!stats.isFile()) {
|
|
45
|
+
return createErrorResult(`Not a file: ${filePath}`);
|
|
46
|
+
}
|
|
47
|
+
if (!filePath.endsWith(".py") && !filePath.endsWith(".pyi")) {
|
|
48
|
+
return createErrorResult(`Not a Python file: ${filePath}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return createErrorResult(`File not found: ${filePath}`);
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const parseResult = await parseFile(filePath);
|
|
56
|
+
const { tree, source } = parseResult;
|
|
57
|
+
const rootNode = tree.rootNode;
|
|
58
|
+
const result = {
|
|
59
|
+
path: filePath,
|
|
60
|
+
classes: [],
|
|
61
|
+
functions: [],
|
|
62
|
+
variables: [],
|
|
63
|
+
imports: [],
|
|
64
|
+
stats: {
|
|
65
|
+
totalClasses: 0,
|
|
66
|
+
totalFunctions: 0,
|
|
67
|
+
totalVariables: 0,
|
|
68
|
+
totalImports: 0,
|
|
69
|
+
totalLines: source.split("\n").length,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
// Extract module docstring
|
|
73
|
+
result.docstring = extractDocstring({ childForFieldName: () => rootNode }, source);
|
|
74
|
+
// Process top-level nodes
|
|
75
|
+
for (const child of rootNode.children) {
|
|
76
|
+
// Imports
|
|
77
|
+
if (child.type === "import_statement" ||
|
|
78
|
+
child.type === "import_from_statement") {
|
|
79
|
+
const importInfo = parseImport(child, source);
|
|
80
|
+
result.imports.push(importInfo);
|
|
81
|
+
result.stats.totalImports++;
|
|
82
|
+
}
|
|
83
|
+
// Class definitions
|
|
84
|
+
if (child.type === "class_definition") {
|
|
85
|
+
const classInfo = parseClass(child, source);
|
|
86
|
+
if (includePrivate || !classInfo.name.startsWith("_")) {
|
|
87
|
+
// Filter private methods if needed
|
|
88
|
+
if (!includePrivate) {
|
|
89
|
+
classInfo.methods = classInfo.methods.filter((m) => !m.name.startsWith("_") || m.name.startsWith("__"));
|
|
90
|
+
classInfo.attributes = classInfo.attributes.filter((a) => !a.name.startsWith("_"));
|
|
91
|
+
}
|
|
92
|
+
result.classes.push(classInfo);
|
|
93
|
+
result.stats.totalClasses++;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Decorated class definitions
|
|
97
|
+
if (child.type === "decorated_definition") {
|
|
98
|
+
const decorators = parseDecorators(child, source);
|
|
99
|
+
const classNode = child.children.find((c) => c.type === "class_definition");
|
|
100
|
+
if (classNode) {
|
|
101
|
+
const classInfo = parseClass(classNode, source, decorators);
|
|
102
|
+
if (includePrivate || !classInfo.name.startsWith("_")) {
|
|
103
|
+
if (!includePrivate) {
|
|
104
|
+
classInfo.methods = classInfo.methods.filter((m) => !m.name.startsWith("_") || m.name.startsWith("__"));
|
|
105
|
+
classInfo.attributes = classInfo.attributes.filter((a) => !a.name.startsWith("_"));
|
|
106
|
+
}
|
|
107
|
+
result.classes.push(classInfo);
|
|
108
|
+
result.stats.totalClasses++;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Function definitions
|
|
113
|
+
if (child.type === "function_definition" ||
|
|
114
|
+
child.type === "async_function_definition") {
|
|
115
|
+
const funcInfo = parseFunction(child, source);
|
|
116
|
+
if (includePrivate || !funcInfo.name.startsWith("_")) {
|
|
117
|
+
result.functions.push(funcInfo);
|
|
118
|
+
result.stats.totalFunctions++;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Decorated function definitions
|
|
122
|
+
if (child.type === "decorated_definition") {
|
|
123
|
+
const decorators = parseDecorators(child, source);
|
|
124
|
+
const funcNode = child.children.find((c) => c.type === "function_definition" ||
|
|
125
|
+
c.type === "async_function_definition");
|
|
126
|
+
if (funcNode) {
|
|
127
|
+
const funcInfo = parseFunction(funcNode, source, decorators);
|
|
128
|
+
if (includePrivate || !funcInfo.name.startsWith("_")) {
|
|
129
|
+
result.functions.push(funcInfo);
|
|
130
|
+
result.stats.totalFunctions++;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Variable assignments at module level
|
|
135
|
+
if (child.type === "expression_statement") {
|
|
136
|
+
const expr = child.firstChild;
|
|
137
|
+
if (expr &&
|
|
138
|
+
(expr.type === "assignment" || expr.type === "annotated_assignment")) {
|
|
139
|
+
const varInfo = parseVariable(expr, source);
|
|
140
|
+
if (varInfo && (includePrivate || !varInfo.name.startsWith("_"))) {
|
|
141
|
+
result.variables.push(varInfo);
|
|
142
|
+
result.stats.totalVariables++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return createSuccessResult(result);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
return createErrorResult(error instanceof Error ? error.message : String(error));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Factory function to create getFileStructure tool with custom options
|
|
155
|
+
*/
|
|
156
|
+
export function createGetFileStructureTool(options) {
|
|
157
|
+
return defineTool({
|
|
158
|
+
name: "get_file_structure_python",
|
|
159
|
+
description: "Get structural overview of a Python file including classes, functions, variables, and imports.",
|
|
160
|
+
inputSchema: {
|
|
161
|
+
type: "object",
|
|
162
|
+
properties: {
|
|
163
|
+
path: {
|
|
164
|
+
type: "string",
|
|
165
|
+
description: "Path to Python file (.py)",
|
|
166
|
+
},
|
|
167
|
+
includePrivate: {
|
|
168
|
+
type: "boolean",
|
|
169
|
+
description: "Include private members (starting with _)",
|
|
170
|
+
},
|
|
171
|
+
maxDepth: {
|
|
172
|
+
type: "number",
|
|
173
|
+
description: "Maximum nesting depth to analyze",
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
required: ["path"],
|
|
177
|
+
},
|
|
178
|
+
execute: async (input) => {
|
|
179
|
+
let targetPath = input.path;
|
|
180
|
+
if (options?.baseDir && !targetPath.startsWith("/")) {
|
|
181
|
+
targetPath = resolve(options.baseDir, targetPath);
|
|
182
|
+
}
|
|
183
|
+
return executeGetFileStructure({ ...input, path: targetPath });
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* getImports Tool
|
|
3
|
+
*
|
|
4
|
+
* Analyzes imports in Python files.
|
|
5
|
+
* Categorizes imports into stdlib, third-party, and local.
|
|
6
|
+
*/
|
|
7
|
+
import type { Tool } from "@compilr-dev/agents";
|
|
8
|
+
/**
|
|
9
|
+
* Input for getImports tool
|
|
10
|
+
*/
|
|
11
|
+
export interface GetImportsInput {
|
|
12
|
+
/** File or directory to analyze */
|
|
13
|
+
path: string;
|
|
14
|
+
/** Recursively analyze directory (default: false) */
|
|
15
|
+
recursive?: boolean;
|
|
16
|
+
/** Filter to specific import sources */
|
|
17
|
+
filterModule?: string;
|
|
18
|
+
/** Maximum files to analyze (default: 50) */
|
|
19
|
+
maxFiles?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* getImports tool - Analyze Python imports
|
|
23
|
+
*/
|
|
24
|
+
export declare const getImportsTool: Tool<GetImportsInput>;
|
|
25
|
+
/**
|
|
26
|
+
* Factory function to create a customized getImports tool
|
|
27
|
+
*/
|
|
28
|
+
export declare function createGetImportsTool(options?: {
|
|
29
|
+
/** Default recursive */
|
|
30
|
+
defaultRecursive?: boolean;
|
|
31
|
+
/** Default maxFiles */
|
|
32
|
+
defaultMaxFiles?: number;
|
|
33
|
+
}): Tool<GetImportsInput>;
|
|
34
|
+
//# sourceMappingURL=get-imports.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-imports.d.ts","sourceRoot":"","sources":["../../src/tools/get-imports.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EAAE,IAAI,EAAuB,MAAM,qBAAqB,CAAC;AAwNrE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAiCD;;GAEG;AACH,eAAO,MAAM,cAAc,uBAKzB,CAAC;AAyOH;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE;IAC7C,wBAAwB;IACxB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,uBAAuB;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,IAAI,CAAC,eAAe,CAAC,CAexB"}
|