@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,342 @@
1
+ /**
2
+ * findImplementations Tool
3
+ *
4
+ * Find classes that inherit from a base class, ABC, or implement a Protocol.
5
+ * Useful for understanding inheritance hierarchies and finding concrete implementations.
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, parseClass, parseFunction, } from "../parser/python-parser.js";
11
+ // Tool description
12
+ const TOOL_DESCRIPTION = `Find classes that inherit from a base class, ABC, or implement a Protocol.
13
+ Returns information about each implementation including which methods are implemented.
14
+ Useful for understanding how base classes are used across the codebase.`;
15
+ // Tool input schema
16
+ const TOOL_INPUT_SCHEMA = {
17
+ type: "object",
18
+ properties: {
19
+ name: {
20
+ type: "string",
21
+ description: "Base class, ABC, or Protocol name to find implementations of",
22
+ },
23
+ scope: {
24
+ type: "string",
25
+ description: "Directory or file to search in (defaults to current directory)",
26
+ },
27
+ includeAbstract: {
28
+ type: "boolean",
29
+ description: "Include abstract classes that partially implement (default: false)",
30
+ default: false,
31
+ },
32
+ maxFiles: {
33
+ type: "number",
34
+ description: "Maximum files to search (default: 100)",
35
+ default: 100,
36
+ },
37
+ },
38
+ required: ["name"],
39
+ };
40
+ // Default exclusions
41
+ const DEFAULT_EXCLUDE = [
42
+ "node_modules",
43
+ "__pycache__",
44
+ ".git",
45
+ "venv",
46
+ ".venv",
47
+ "env",
48
+ "dist",
49
+ "build",
50
+ ];
51
+ /**
52
+ * findImplementations tool
53
+ */
54
+ export const findImplementationsTool = defineTool({
55
+ name: "find_implementations_python",
56
+ description: TOOL_DESCRIPTION,
57
+ inputSchema: TOOL_INPUT_SCHEMA,
58
+ execute: executeFindImplementations,
59
+ });
60
+ /**
61
+ * Execute the findImplementations tool
62
+ */
63
+ async function executeFindImplementations(input) {
64
+ const { name, scope = ".", includeAbstract = false, maxFiles = 100 } = input;
65
+ const startTime = Date.now();
66
+ // Validate input
67
+ if (!name || name.trim().length === 0) {
68
+ return createErrorResult("Base class or Protocol name is required");
69
+ }
70
+ try {
71
+ // Resolve scope path
72
+ const scopePath = path.resolve(scope);
73
+ // Check if scope exists
74
+ try {
75
+ await fs.access(scopePath);
76
+ }
77
+ catch {
78
+ return createErrorResult(`Scope path not found: ${scopePath}`);
79
+ }
80
+ const stats = await fs.stat(scopePath);
81
+ // Collect files to search
82
+ const files = stats.isDirectory()
83
+ ? await collectFiles(scopePath, maxFiles)
84
+ : [scopePath];
85
+ // Find the target class definition
86
+ let targetLocation;
87
+ let targetMethods = [];
88
+ // First pass: find the target definition
89
+ for (const file of files) {
90
+ const result = await findTargetDefinition(file, name);
91
+ if (result) {
92
+ targetLocation = { path: file, line: result.line };
93
+ targetMethods = result.methods;
94
+ // Note: result.isABC and result.isProtocol are available for future use
95
+ break;
96
+ }
97
+ }
98
+ // Second pass: find implementations
99
+ const implementations = [];
100
+ for (const file of files) {
101
+ const fileImplementations = await findImplementationsInFile(file, name, targetMethods, includeAbstract);
102
+ implementations.push(...fileImplementations);
103
+ }
104
+ const timeMs = Date.now() - startTime;
105
+ const result = {
106
+ target: name,
107
+ targetLocation,
108
+ implementations,
109
+ stats: {
110
+ filesSearched: files.length,
111
+ implementationsFound: implementations.length,
112
+ timeMs,
113
+ },
114
+ };
115
+ return createSuccessResult(result);
116
+ }
117
+ catch (error) {
118
+ return createErrorResult(`Failed to find implementations: ${error instanceof Error ? error.message : String(error)}`);
119
+ }
120
+ }
121
+ /**
122
+ * Collect Python files
123
+ */
124
+ async function collectFiles(dir, maxFiles, currentDepth = 0) {
125
+ if (currentDepth > 10)
126
+ return [];
127
+ const files = [];
128
+ try {
129
+ const entries = await fs.readdir(dir, { withFileTypes: true });
130
+ for (const entry of entries) {
131
+ if (files.length >= maxFiles)
132
+ break;
133
+ const fullPath = path.join(dir, entry.name);
134
+ if (entry.isDirectory()) {
135
+ if (DEFAULT_EXCLUDE.includes(entry.name))
136
+ continue;
137
+ const subFiles = await collectFiles(fullPath, maxFiles - files.length, currentDepth + 1);
138
+ files.push(...subFiles);
139
+ }
140
+ else if (entry.isFile()) {
141
+ if (fullPath.endsWith(".py") && !fullPath.endsWith(".pyi")) {
142
+ files.push(fullPath);
143
+ }
144
+ }
145
+ }
146
+ }
147
+ catch {
148
+ // Ignore permission errors
149
+ }
150
+ return files;
151
+ }
152
+ /**
153
+ * Find the target class/protocol definition
154
+ */
155
+ async function findTargetDefinition(filePath, targetName) {
156
+ try {
157
+ const parseResult = await parseFile(filePath);
158
+ const { tree, source } = parseResult;
159
+ for (const child of tree.rootNode.children) {
160
+ if (child.type === "class_definition") {
161
+ const classInfo = parseClass(child, source);
162
+ if (classInfo.name === targetName) {
163
+ // Check if it's an ABC or Protocol
164
+ const isABC = classInfo.bases.includes("ABC") ||
165
+ classInfo.bases.some((b) => b.includes("abc.ABC") || b.includes("ABCMeta"));
166
+ const isProtocol = classInfo.bases.includes("Protocol");
167
+ // Extract abstract methods
168
+ const methods = extractAbstractMethods(child, source);
169
+ return {
170
+ line: classInfo.line,
171
+ methods,
172
+ isABC,
173
+ isProtocol,
174
+ };
175
+ }
176
+ }
177
+ // Check decorated class definitions
178
+ if (child.type === "decorated_definition") {
179
+ const classNode = child.children.find((c) => c.type === "class_definition");
180
+ if (classNode) {
181
+ const classInfo = parseClass(classNode, source);
182
+ if (classInfo.name === targetName) {
183
+ const isABC = classInfo.bases.includes("ABC") ||
184
+ classInfo.bases.some((b) => b.includes("abc.ABC") || b.includes("ABCMeta"));
185
+ const isProtocol = classInfo.bases.includes("Protocol");
186
+ const methods = extractAbstractMethods(classNode, source);
187
+ return {
188
+ line: classInfo.line,
189
+ methods,
190
+ isABC,
191
+ isProtocol,
192
+ };
193
+ }
194
+ }
195
+ }
196
+ }
197
+ }
198
+ catch {
199
+ // Skip files that can't be parsed
200
+ }
201
+ return null;
202
+ }
203
+ /**
204
+ * Extract abstract method names from a class
205
+ */
206
+ function extractAbstractMethods(classNode, source) {
207
+ const methods = [];
208
+ const body = classNode.childForFieldName("body");
209
+ if (body) {
210
+ for (const member of body.children) {
211
+ // Check decorated methods
212
+ if (member.type === "decorated_definition") {
213
+ const decorators = [];
214
+ for (const c of member.children) {
215
+ if (c.type === "decorator") {
216
+ decorators.push(c.text);
217
+ }
218
+ }
219
+ const hasAbstractMethod = decorators.some((d) => d.includes("abstractmethod") ||
220
+ d.includes("abstractproperty") ||
221
+ d.includes("abstractclassmethod") ||
222
+ d.includes("abstractstaticmethod"));
223
+ if (hasAbstractMethod) {
224
+ const funcNode = member.children.find((c) => c.type === "function_definition" ||
225
+ c.type === "async_function_definition");
226
+ if (funcNode) {
227
+ const funcInfo = parseFunction(funcNode, source);
228
+ methods.push(funcInfo.name);
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+ return methods;
235
+ }
236
+ /**
237
+ * Find implementations in a single file
238
+ */
239
+ async function findImplementationsInFile(filePath, targetName, targetMethods, includeAbstract) {
240
+ try {
241
+ const parseResult = await parseFile(filePath);
242
+ const { tree, source } = parseResult;
243
+ const implementations = [];
244
+ for (const child of tree.rootNode.children) {
245
+ if (child.type === "class_definition") {
246
+ const impl = checkClassImplementation(child, source, filePath, targetName, targetMethods, includeAbstract, false);
247
+ if (impl) {
248
+ implementations.push(impl);
249
+ }
250
+ }
251
+ // Check decorated class definitions
252
+ if (child.type === "decorated_definition") {
253
+ const classNode = child.children.find((c) => c.type === "class_definition");
254
+ if (classNode) {
255
+ // Check for dataclass decorator
256
+ const isDataclass = child.children.some((c) => c.type === "decorator" && c.text.includes("dataclass"));
257
+ const impl = checkClassImplementation(classNode, source, filePath, targetName, targetMethods, includeAbstract, isDataclass);
258
+ if (impl) {
259
+ implementations.push(impl);
260
+ }
261
+ }
262
+ }
263
+ }
264
+ return implementations;
265
+ }
266
+ catch {
267
+ return [];
268
+ }
269
+ }
270
+ /**
271
+ * Check if a class implements the target
272
+ */
273
+ function checkClassImplementation(classNode, source, filePath, targetName, targetMethods, includeAbstract, isDataclass) {
274
+ const classInfo = parseClass(classNode, source);
275
+ // Skip the target class itself
276
+ if (classInfo.name === targetName)
277
+ return null;
278
+ // Check if it inherits from the target
279
+ const inheritsTarget = classInfo.bases.some((base) => base === targetName || base.endsWith(`.${targetName}`));
280
+ if (!inheritsTarget)
281
+ return null;
282
+ // Check if it's abstract
283
+ const isAbstract = classInfo.decorators.some((d) => d.name.includes("ABC") ||
284
+ d.name.includes("abstractmethod") ||
285
+ d.name.includes("ABCMeta"));
286
+ // Skip abstract classes if not including them
287
+ if (isAbstract && !includeAbstract)
288
+ return null;
289
+ // Get implemented methods
290
+ const implementedMethods = [];
291
+ const body = classNode.childForFieldName("body");
292
+ if (body) {
293
+ for (const member of body.children) {
294
+ if (member.type === "function_definition" ||
295
+ member.type === "async_function_definition") {
296
+ const methodInfo = parseFunction(member, source);
297
+ if (targetMethods.includes(methodInfo.name)) {
298
+ implementedMethods.push(methodInfo.name);
299
+ }
300
+ }
301
+ if (member.type === "decorated_definition") {
302
+ const funcNode = member.children.find((c) => c.type === "function_definition" ||
303
+ c.type === "async_function_definition");
304
+ if (funcNode) {
305
+ const methodInfo = parseFunction(funcNode, source);
306
+ if (targetMethods.includes(methodInfo.name)) {
307
+ implementedMethods.push(methodInfo.name);
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+ // Calculate missing methods
314
+ const missingMethods = targetMethods.filter((m) => !implementedMethods.includes(m));
315
+ return {
316
+ name: classInfo.name,
317
+ path: filePath,
318
+ line: classInfo.line,
319
+ kind: isDataclass ? "dataclass" : "class",
320
+ isAbstract,
321
+ implementedMethods: implementedMethods.length > 0 ? implementedMethods : undefined,
322
+ missingMethods: missingMethods.length > 0 ? missingMethods : undefined,
323
+ };
324
+ }
325
+ /**
326
+ * Factory function to create a customized findImplementations tool
327
+ */
328
+ export function createFindImplementationsTool(options) {
329
+ return defineTool({
330
+ name: "find_implementations_python",
331
+ description: TOOL_DESCRIPTION,
332
+ inputSchema: TOOL_INPUT_SCHEMA,
333
+ execute: async (input) => {
334
+ const modifiedInput = {
335
+ ...input,
336
+ scope: input.scope ?? options?.defaultScope,
337
+ maxFiles: input.maxFiles ?? options?.defaultMaxFiles,
338
+ };
339
+ return executeFindImplementations(modifiedInput);
340
+ },
341
+ });
342
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * findPatterns Tool
3
+ *
4
+ * Find code patterns, anti-patterns, and code smells in Python code.
5
+ * Uses regex and AST-based pattern matching.
6
+ */
7
+ import type { Tool } from "@compilr-dev/agents";
8
+ /**
9
+ * Code pattern definition
10
+ */
11
+ export interface CodePattern {
12
+ name: string;
13
+ description: string;
14
+ type: "anti-pattern" | "smell" | "pattern";
15
+ matcher: string;
16
+ severity: "error" | "warning" | "info";
17
+ }
18
+ /**
19
+ * Pattern match
20
+ */
21
+ export interface PatternMatch {
22
+ pattern: string;
23
+ description: string;
24
+ severity: "error" | "warning" | "info";
25
+ path: string;
26
+ line: number;
27
+ snippet: string;
28
+ suggestion?: string;
29
+ }
30
+ /**
31
+ * Input for findPatterns tool
32
+ */
33
+ export interface FindPatternsInput {
34
+ /** Directory to analyze */
35
+ path: string;
36
+ /** Custom patterns to search for (uses built-in if not specified) */
37
+ patterns?: CodePattern[];
38
+ /** Pattern categories to include (default: all) */
39
+ categories?: Array<"security" | "performance" | "maintainability" | "all">;
40
+ /** Maximum files to analyze (default: 100) */
41
+ maxFiles?: number;
42
+ }
43
+ /**
44
+ * Result of findPatterns
45
+ */
46
+ export interface FindPatternsResult {
47
+ path: string;
48
+ matches: PatternMatch[];
49
+ stats: {
50
+ filesAnalyzed: number;
51
+ totalMatches: number;
52
+ byPattern: Record<string, number>;
53
+ bySeverity: {
54
+ error: number;
55
+ warning: number;
56
+ info: number;
57
+ };
58
+ };
59
+ }
60
+ /**
61
+ * findPatterns tool
62
+ */
63
+ export declare const findPatternsTool: Tool<FindPatternsInput>;
64
+ /**
65
+ * Factory function to create a customized findPatterns tool
66
+ */
67
+ export declare function createFindPatternsTool(options?: {
68
+ defaultMaxFiles?: number;
69
+ additionalPatterns?: CodePattern[];
70
+ }): Tool<FindPatternsInput>;
71
+ //# sourceMappingURL=find-patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-patterns.d.ts","sourceRoot":"","sources":["../../src/tools/find-patterns.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EAAE,IAAI,EAAuB,MAAM,qBAAqB,CAAC;AAGrE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,cAAc,GAAG,OAAO,GAAG,SAAS,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,mDAAmD;IACnD,UAAU,CAAC,EAAE,KAAK,CAAC,UAAU,GAAG,aAAa,GAAG,iBAAiB,GAAG,KAAK,CAAC,CAAC;IAC3E,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,EAAE;QACL,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAClC,UAAU,EAAE;YACV,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,EAAE,MAAM,CAAC;YAChB,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;KACH,CAAC;CACH;AA4ND;;GAEG;AACH,eAAO,MAAM,gBAAgB,yBAK3B,CAAC;AAiTH;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,CAAC,EAAE;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,WAAW,EAAE,CAAC;CACpC,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAiB1B"}