@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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/dist/index.d.ts +40 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +27 -0
  5. package/dist/parser/index.d.ts +6 -0
  6. package/dist/parser/index.d.ts.map +1 -0
  7. package/dist/parser/index.js +5 -0
  8. package/dist/parser/node-types.d.ts +119 -0
  9. package/dist/parser/node-types.d.ts.map +1 -0
  10. package/dist/parser/node-types.js +4 -0
  11. package/dist/parser/python-parser.d.ts +85 -0
  12. package/dist/parser/python-parser.d.ts.map +1 -0
  13. package/dist/parser/python-parser.js +477 -0
  14. package/dist/skills/index.d.ts +26 -0
  15. package/dist/skills/index.d.ts.map +1 -0
  16. package/dist/skills/index.js +36 -0
  17. package/dist/skills/python-best-practices.d.ts +7 -0
  18. package/dist/skills/python-best-practices.d.ts.map +1 -0
  19. package/dist/skills/python-best-practices.js +78 -0
  20. package/dist/skills/python-code-health.d.ts +7 -0
  21. package/dist/skills/python-code-health.d.ts.map +1 -0
  22. package/dist/skills/python-code-health.js +209 -0
  23. package/dist/skills/python-code-structure.d.ts +7 -0
  24. package/dist/skills/python-code-structure.d.ts.map +1 -0
  25. package/dist/skills/python-code-structure.js +155 -0
  26. package/dist/skills/python-dependency-audit.d.ts +7 -0
  27. package/dist/skills/python-dependency-audit.d.ts.map +1 -0
  28. package/dist/skills/python-dependency-audit.js +246 -0
  29. package/dist/skills/python-refactor-impact.d.ts +7 -0
  30. package/dist/skills/python-refactor-impact.d.ts.map +1 -0
  31. package/dist/skills/python-refactor-impact.js +232 -0
  32. package/dist/tools/extract-docstrings.d.ts +70 -0
  33. package/dist/tools/extract-docstrings.d.ts.map +1 -0
  34. package/dist/tools/extract-docstrings.js +575 -0
  35. package/dist/tools/find-dead-code.d.ts +62 -0
  36. package/dist/tools/find-dead-code.d.ts.map +1 -0
  37. package/dist/tools/find-dead-code.js +422 -0
  38. package/dist/tools/find-duplicates.d.ts +65 -0
  39. package/dist/tools/find-duplicates.d.ts.map +1 -0
  40. package/dist/tools/find-duplicates.js +289 -0
  41. package/dist/tools/find-implementations.d.ts +71 -0
  42. package/dist/tools/find-implementations.d.ts.map +1 -0
  43. package/dist/tools/find-implementations.js +342 -0
  44. package/dist/tools/find-patterns.d.ts +71 -0
  45. package/dist/tools/find-patterns.d.ts.map +1 -0
  46. package/dist/tools/find-patterns.js +477 -0
  47. package/dist/tools/find-references.d.ts +66 -0
  48. package/dist/tools/find-references.d.ts.map +1 -0
  49. package/dist/tools/find-references.js +306 -0
  50. package/dist/tools/find-symbol.d.ts +86 -0
  51. package/dist/tools/find-symbol.d.ts.map +1 -0
  52. package/dist/tools/find-symbol.js +414 -0
  53. package/dist/tools/get-call-graph.d.ts +89 -0
  54. package/dist/tools/get-call-graph.d.ts.map +1 -0
  55. package/dist/tools/get-call-graph.js +431 -0
  56. package/dist/tools/get-class-hierarchy.d.ts +38 -0
  57. package/dist/tools/get-class-hierarchy.d.ts.map +1 -0
  58. package/dist/tools/get-class-hierarchy.js +289 -0
  59. package/dist/tools/get-complexity.d.ts +61 -0
  60. package/dist/tools/get-complexity.d.ts.map +1 -0
  61. package/dist/tools/get-complexity.js +384 -0
  62. package/dist/tools/get-dependency-graph.d.ts +85 -0
  63. package/dist/tools/get-dependency-graph.d.ts.map +1 -0
  64. package/dist/tools/get-dependency-graph.js +387 -0
  65. package/dist/tools/get-exports.d.ts +78 -0
  66. package/dist/tools/get-exports.d.ts.map +1 -0
  67. package/dist/tools/get-exports.js +437 -0
  68. package/dist/tools/get-file-structure.d.ts +28 -0
  69. package/dist/tools/get-file-structure.d.ts.map +1 -0
  70. package/dist/tools/get-file-structure.js +186 -0
  71. package/dist/tools/get-imports.d.ts +34 -0
  72. package/dist/tools/get-imports.d.ts.map +1 -0
  73. package/dist/tools/get-imports.js +455 -0
  74. package/dist/tools/get-signature.d.ts +100 -0
  75. package/dist/tools/get-signature.d.ts.map +1 -0
  76. package/dist/tools/get-signature.js +800 -0
  77. package/dist/tools/index.d.ts +55 -0
  78. package/dist/tools/index.d.ts.map +1 -0
  79. package/dist/tools/index.js +75 -0
  80. package/dist/tools/types.d.ts +378 -0
  81. package/dist/tools/types.d.ts.map +1 -0
  82. package/dist/tools/types.js +4 -0
  83. package/package.json +85 -0
@@ -0,0 +1,431 @@
1
+ /**
2
+ * getCallGraph Tool
3
+ *
4
+ * Analyze function call relationships in Python code.
5
+ * Returns a call graph showing what functions call what.
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 = `Analyze function call relationships in Python code.
13
+ Returns a call graph showing which functions call which other functions.
14
+ Use this to understand code flow, dependencies, and refactoring impact.`;
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
+ functionName: {
24
+ type: "string",
25
+ description: "Specific function to analyze (optional)",
26
+ },
27
+ direction: {
28
+ type: "string",
29
+ enum: ["callers", "callees", "both"],
30
+ description: "Direction: 'callers' | 'callees' | 'both' (default: 'both')",
31
+ },
32
+ maxDepth: {
33
+ type: "number",
34
+ description: "Maximum depth to traverse (default: 2)",
35
+ default: 2,
36
+ },
37
+ includeExternal: {
38
+ type: "boolean",
39
+ description: "Include external package calls (default: false)",
40
+ default: false,
41
+ },
42
+ },
43
+ required: ["path"],
44
+ };
45
+ /**
46
+ * getCallGraph tool
47
+ */
48
+ export const getCallGraphTool = defineTool({
49
+ name: "get_call_graph_python",
50
+ description: TOOL_DESCRIPTION,
51
+ inputSchema: TOOL_INPUT_SCHEMA,
52
+ execute: executeGetCallGraph,
53
+ });
54
+ /**
55
+ * Execute the getCallGraph tool
56
+ */
57
+ async function executeGetCallGraph(input) {
58
+ const { path: inputPath, functionName, direction = "both", maxDepth = 2, includeExternal = false, } = input;
59
+ // Validate input
60
+ if (!inputPath || inputPath.trim().length === 0) {
61
+ return createErrorResult("Path is required");
62
+ }
63
+ try {
64
+ const resolvedPath = path.resolve(inputPath);
65
+ // Check if path exists
66
+ try {
67
+ await fs.access(resolvedPath);
68
+ }
69
+ catch {
70
+ return createErrorResult(`Path not found: ${resolvedPath}`);
71
+ }
72
+ const stats = await fs.stat(resolvedPath);
73
+ if (!stats.isFile()) {
74
+ return createErrorResult(`Path must be a file: ${resolvedPath}`);
75
+ }
76
+ if (!resolvedPath.endsWith(".py")) {
77
+ return createErrorResult("File must be a Python file (.py)");
78
+ }
79
+ // Analyze the file
80
+ const result = await analyzeCallGraph(resolvedPath, functionName, direction, maxDepth, includeExternal);
81
+ return createSuccessResult(result);
82
+ }
83
+ catch (error) {
84
+ const message = error instanceof Error ? error.message : String(error);
85
+ return createErrorResult(`Failed to analyze call graph: ${message}`);
86
+ }
87
+ }
88
+ /**
89
+ * Analyze call graph for a file
90
+ */
91
+ async function analyzeCallGraph(filePath, functionName, direction, maxDepth, includeExternal) {
92
+ const parseResult = await parseFile(filePath);
93
+ const { tree, source } = parseResult;
94
+ // Find all functions in the file
95
+ const functions = extractFunctions(tree.rootNode, source, filePath);
96
+ // Build the call graph
97
+ const graphNodes = new Map();
98
+ let rootNode;
99
+ let externalCallCount = 0;
100
+ // If functionName is specified, find it
101
+ if (functionName) {
102
+ const targetFunc = functions.find((f) => f.node.name === functionName);
103
+ if (!targetFunc) {
104
+ return {
105
+ graph: [],
106
+ stats: {
107
+ totalFunctions: 0,
108
+ totalCalls: 0,
109
+ maxDepthReached: 0,
110
+ },
111
+ };
112
+ }
113
+ rootNode = targetFunc.node;
114
+ }
115
+ // Analyze callees (what each function calls)
116
+ if (direction === "callees" || direction === "both") {
117
+ const functionsToAnalyze = functionName
118
+ ? functions.filter((f) => f.node.name === functionName)
119
+ : functions;
120
+ for (const func of functionsToAnalyze) {
121
+ const callees = extractCallees(func, functions, source, includeExternal);
122
+ let graphNode = graphNodes.get(func.node.name);
123
+ if (!graphNode) {
124
+ graphNode = {
125
+ function: func.node,
126
+ calls: [],
127
+ calledBy: [],
128
+ };
129
+ graphNodes.set(func.node.name, graphNode);
130
+ }
131
+ graphNode.calls = callees;
132
+ if (includeExternal) {
133
+ externalCallCount += callees.filter((c) => c.function.path === "external").length;
134
+ }
135
+ }
136
+ }
137
+ // Analyze callers (what calls each function)
138
+ if (direction === "callers" || direction === "both") {
139
+ const functionsToAnalyze = functionName
140
+ ? functions.filter((f) => f.node.name === functionName)
141
+ : functions;
142
+ for (const func of functionsToAnalyze) {
143
+ const callers = findCallersInFile(func.node.name, functions, source, filePath);
144
+ let graphNode = graphNodes.get(func.node.name);
145
+ if (!graphNode) {
146
+ graphNode = {
147
+ function: func.node,
148
+ calls: [],
149
+ calledBy: [],
150
+ };
151
+ graphNodes.set(func.node.name, graphNode);
152
+ }
153
+ graphNode.calledBy = callers;
154
+ }
155
+ }
156
+ // Calculate statistics
157
+ const graph = Array.from(graphNodes.values());
158
+ const totalCalls = graph.reduce((sum, node) => sum + node.calls.length, 0);
159
+ const callGraphStats = {
160
+ totalFunctions: graph.length,
161
+ totalCalls,
162
+ maxDepthReached: Math.min(maxDepth, 1),
163
+ externalCalls: includeExternal ? externalCallCount : undefined,
164
+ };
165
+ return {
166
+ root: rootNode,
167
+ graph,
168
+ stats: callGraphStats,
169
+ };
170
+ }
171
+ /**
172
+ * Extract all functions from an AST
173
+ */
174
+ function extractFunctions(rootNode, source, filePath) {
175
+ const functions = [];
176
+ function visit(node, className) {
177
+ // Function definitions
178
+ if (node.type === "function_definition" ||
179
+ node.type === "async_function_definition") {
180
+ const funcInfo = parseFunction(node, source);
181
+ const isAsync = node.type === "async_function_definition";
182
+ functions.push({
183
+ node: {
184
+ name: funcInfo.name,
185
+ path: filePath,
186
+ line: funcInfo.line,
187
+ column: node.startPosition.column + 1,
188
+ className,
189
+ async: isAsync,
190
+ },
191
+ astNode: node,
192
+ });
193
+ }
194
+ // Class definitions - extract methods
195
+ if (node.type === "class_definition") {
196
+ const classInfo = parseClass(node, source);
197
+ const body = node.childForFieldName("body");
198
+ if (body) {
199
+ for (const member of body.children) {
200
+ if (member.type === "function_definition" ||
201
+ member.type === "async_function_definition") {
202
+ const methodInfo = parseFunction(member, source);
203
+ const isAsync = member.type === "async_function_definition";
204
+ functions.push({
205
+ node: {
206
+ name: methodInfo.name,
207
+ path: filePath,
208
+ line: methodInfo.line,
209
+ column: member.startPosition.column + 1,
210
+ className: classInfo.name,
211
+ async: isAsync,
212
+ },
213
+ astNode: member,
214
+ });
215
+ }
216
+ // Decorated methods
217
+ if (member.type === "decorated_definition") {
218
+ const funcNode = member.children.find((c) => c.type === "function_definition" ||
219
+ c.type === "async_function_definition");
220
+ if (funcNode) {
221
+ const methodInfo = parseFunction(funcNode, source);
222
+ const isAsync = funcNode.type === "async_function_definition";
223
+ functions.push({
224
+ node: {
225
+ name: methodInfo.name,
226
+ path: filePath,
227
+ line: methodInfo.line,
228
+ column: funcNode.startPosition.column + 1,
229
+ className: classInfo.name,
230
+ async: isAsync,
231
+ },
232
+ astNode: funcNode,
233
+ });
234
+ }
235
+ }
236
+ }
237
+ }
238
+ }
239
+ // Decorated function definitions
240
+ if (node.type === "decorated_definition") {
241
+ const funcNode = node.children.find((c) => c.type === "function_definition" ||
242
+ c.type === "async_function_definition");
243
+ if (funcNode) {
244
+ const funcInfo = parseFunction(funcNode, source);
245
+ const isAsync = funcNode.type === "async_function_definition";
246
+ functions.push({
247
+ node: {
248
+ name: funcInfo.name,
249
+ path: filePath,
250
+ line: funcInfo.line,
251
+ column: funcNode.startPosition.column + 1,
252
+ className,
253
+ async: isAsync,
254
+ },
255
+ astNode: funcNode,
256
+ });
257
+ }
258
+ }
259
+ // Recurse for nested functions (but not into class methods which are handled separately)
260
+ if (node.type !== "class_definition") {
261
+ for (const child of node.children) {
262
+ visit(child, className);
263
+ }
264
+ }
265
+ }
266
+ visit(rootNode);
267
+ return functions;
268
+ }
269
+ /**
270
+ * Extract callees (functions called by a function)
271
+ */
272
+ function extractCallees(func, allFunctions, source, includeExternal) {
273
+ const callCounts = new Map();
274
+ const bodyNode = func.astNode.childForFieldName("body");
275
+ if (!bodyNode)
276
+ return [];
277
+ function visit(node) {
278
+ if (node.type === "call") {
279
+ const callInfo = extractCallInfo(node, allFunctions, source, includeExternal);
280
+ if (callInfo) {
281
+ const key = `${callInfo.function.name}:${callInfo.function.path}`;
282
+ const existing = callCounts.get(key);
283
+ if (existing) {
284
+ existing.count++;
285
+ }
286
+ else {
287
+ callCounts.set(key, callInfo);
288
+ }
289
+ }
290
+ }
291
+ for (const child of node.children) {
292
+ visit(child);
293
+ }
294
+ }
295
+ visit(bodyNode);
296
+ return Array.from(callCounts.values());
297
+ }
298
+ /**
299
+ * Extract call info from a call expression
300
+ */
301
+ function extractCallInfo(node, allFunctions, _source, includeExternal) {
302
+ const functionNode = node.childForFieldName("function");
303
+ if (!functionNode)
304
+ return null;
305
+ let funcName;
306
+ let callType = "direct";
307
+ let className;
308
+ // Simple function call: func_name()
309
+ if (functionNode.type === "identifier") {
310
+ funcName = functionNode.text;
311
+ }
312
+ // Method call: obj.method() or self.method()
313
+ else if (functionNode.type === "attribute") {
314
+ const attr = functionNode.childForFieldName("attribute");
315
+ const obj = functionNode.childForFieldName("object");
316
+ if (attr) {
317
+ funcName = attr.text;
318
+ callType = "method";
319
+ if (obj?.type === "identifier") {
320
+ const objName = obj.text;
321
+ if (objName !== "self" && objName !== "cls") {
322
+ className = objName;
323
+ }
324
+ }
325
+ }
326
+ }
327
+ if (!funcName)
328
+ return null;
329
+ // Check if it's a known function in the file
330
+ const knownFunc = allFunctions.find((f) => f.node.name === funcName);
331
+ if (knownFunc) {
332
+ return {
333
+ function: knownFunc.node,
334
+ callLine: node.startPosition.row + 1,
335
+ callColumn: node.startPosition.column + 1,
336
+ type: callType,
337
+ count: 1,
338
+ };
339
+ }
340
+ // External call (not in this file)
341
+ if (includeExternal) {
342
+ return {
343
+ function: {
344
+ name: funcName,
345
+ path: "external",
346
+ line: 0,
347
+ className,
348
+ },
349
+ callLine: node.startPosition.row + 1,
350
+ callColumn: node.startPosition.column + 1,
351
+ type: callType,
352
+ count: 1,
353
+ };
354
+ }
355
+ return null;
356
+ }
357
+ /**
358
+ * Find callers of a function within the same file
359
+ */
360
+ function findCallersInFile(targetFuncName, allFunctions, _source, _filePath) {
361
+ const callerCounts = new Map();
362
+ for (const func of allFunctions) {
363
+ // Skip the function itself
364
+ if (func.node.name === targetFuncName)
365
+ continue;
366
+ const bodyNode = func.astNode.childForFieldName("body");
367
+ if (!bodyNode)
368
+ continue;
369
+ let callsTarget = false;
370
+ let callLine = 0;
371
+ let callColumn = 0;
372
+ let callCount = 0;
373
+ function visit(node) {
374
+ if (node.type === "call") {
375
+ const functionNode = node.childForFieldName("function");
376
+ let calledName;
377
+ if (functionNode?.type === "identifier") {
378
+ calledName = functionNode.text;
379
+ }
380
+ else if (functionNode?.type === "attribute") {
381
+ const attr = functionNode.childForFieldName("attribute");
382
+ if (attr) {
383
+ calledName = attr.text;
384
+ }
385
+ }
386
+ if (calledName === targetFuncName) {
387
+ callsTarget = true;
388
+ callCount++;
389
+ if (callLine === 0) {
390
+ callLine = node.startPosition.row + 1;
391
+ callColumn = node.startPosition.column + 1;
392
+ }
393
+ }
394
+ }
395
+ for (const child of node.children) {
396
+ visit(child);
397
+ }
398
+ }
399
+ visit(bodyNode);
400
+ if (callsTarget) {
401
+ const key = func.node.name;
402
+ callerCounts.set(key, {
403
+ function: func.node,
404
+ callLine,
405
+ callColumn,
406
+ type: "direct",
407
+ count: callCount,
408
+ });
409
+ }
410
+ }
411
+ return Array.from(callerCounts.values());
412
+ }
413
+ /**
414
+ * Factory function to create a customized getCallGraph tool
415
+ */
416
+ export function createGetCallGraphTool(options) {
417
+ const { defaultDirection = "both", defaultMaxDepth = 2, defaultIncludeExternal = false, } = options ?? {};
418
+ return defineTool({
419
+ name: "get_call_graph_python",
420
+ description: TOOL_DESCRIPTION,
421
+ inputSchema: TOOL_INPUT_SCHEMA,
422
+ execute: async (input) => {
423
+ return executeGetCallGraph({
424
+ ...input,
425
+ direction: input.direction ?? defaultDirection,
426
+ maxDepth: input.maxDepth ?? defaultMaxDepth,
427
+ includeExternal: input.includeExternal ?? defaultIncludeExternal,
428
+ });
429
+ },
430
+ });
431
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * getClassHierarchy Tool
3
+ *
4
+ * Analyze class inheritance hierarchy in Python codebases.
5
+ * Finds parent classes (ancestors) and child classes (descendants).
6
+ */
7
+ import type { Tool } from "@compilr-dev/agents";
8
+ /**
9
+ * Direction for hierarchy traversal
10
+ */
11
+ export type HierarchyDirection = "ancestors" | "descendants" | "both";
12
+ /**
13
+ * Input for getClassHierarchy tool
14
+ */
15
+ export interface GetClassHierarchyInput {
16
+ /** Class name to analyze */
17
+ name: string;
18
+ /** File where the class is defined (improves accuracy) */
19
+ file?: string;
20
+ /** Direction: 'ancestors' | 'descendants' | 'both' (default: 'both') */
21
+ direction?: HierarchyDirection;
22
+ /** Maximum depth to traverse (default: 5) */
23
+ maxDepth?: number;
24
+ /** Scope for searching descendants */
25
+ scope?: string;
26
+ }
27
+ /**
28
+ * getClassHierarchy tool - Analyze class inheritance
29
+ */
30
+ export declare const getClassHierarchyTool: Tool<GetClassHierarchyInput>;
31
+ /**
32
+ * Factory function to create a customized getClassHierarchy tool
33
+ */
34
+ export declare function createGetClassHierarchyTool(options?: {
35
+ defaultDirection?: HierarchyDirection;
36
+ defaultMaxDepth?: number;
37
+ }): Tool<GetClassHierarchyInput>;
38
+ //# sourceMappingURL=get-class-hierarchy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-class-hierarchy.d.ts","sourceRoot":"","sources":["../../src/tools/get-class-hierarchy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EAAE,IAAI,EAAuB,MAAM,qBAAqB,CAAC;AAQrE;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,WAAW,GAAG,aAAa,GAAG,MAAM,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAsCD;;GAEG;AACH,eAAO,MAAM,qBAAqB,8BAKhC,CAAC;AA+QH;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,CAAC,EAAE;IACpD,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAiB/B"}