@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,289 @@
1
+ /**
2
+ * findDuplicates Tool
3
+ *
4
+ * Detect duplicate code blocks in Python files using content hashing.
5
+ * Helps identify opportunities for refactoring and code reuse.
6
+ */
7
+ import * as fs from "node:fs/promises";
8
+ import * as path from "node:path";
9
+ import * as crypto from "node:crypto";
10
+ import { defineTool, createSuccessResult, createErrorResult, } from "@compilr-dev/agents";
11
+ // Tool description
12
+ const TOOL_DESCRIPTION = `Detect duplicate code blocks in Python files.
13
+ Uses content hashing to find similar code patterns.
14
+ Useful for identifying refactoring opportunities and reducing code duplication.`;
15
+ // Tool input schema
16
+ const TOOL_INPUT_SCHEMA = {
17
+ type: "object",
18
+ properties: {
19
+ path: {
20
+ type: "string",
21
+ description: "Directory to analyze",
22
+ },
23
+ minLines: {
24
+ type: "number",
25
+ description: "Minimum lines for a duplicate (default: 6)",
26
+ default: 6,
27
+ },
28
+ minTokens: {
29
+ type: "number",
30
+ description: "Minimum tokens for a duplicate (default: 50)",
31
+ default: 50,
32
+ },
33
+ ignoreIdenticalFiles: {
34
+ type: "boolean",
35
+ description: "Ignore identical files (default: true)",
36
+ default: true,
37
+ },
38
+ maxFiles: {
39
+ type: "number",
40
+ description: "Maximum files to analyze (default: 100)",
41
+ default: 100,
42
+ },
43
+ },
44
+ required: ["path"],
45
+ };
46
+ // Default exclusions
47
+ const DEFAULT_EXCLUDE = [
48
+ "node_modules",
49
+ "__pycache__",
50
+ ".git",
51
+ "venv",
52
+ ".venv",
53
+ "env",
54
+ "dist",
55
+ "build",
56
+ ".tox",
57
+ ".eggs",
58
+ ];
59
+ /**
60
+ * findDuplicates tool
61
+ */
62
+ export const findDuplicatesTool = defineTool({
63
+ name: "find_duplicates_python",
64
+ description: TOOL_DESCRIPTION,
65
+ inputSchema: TOOL_INPUT_SCHEMA,
66
+ execute: executeFindDuplicates,
67
+ });
68
+ /**
69
+ * Execute the findDuplicates tool
70
+ */
71
+ async function executeFindDuplicates(input) {
72
+ const { path: inputPath, minLines = 6, minTokens = 50, ignoreIdenticalFiles = true, maxFiles = 100, } = input;
73
+ try {
74
+ const resolvedPath = path.resolve(inputPath);
75
+ // Check if path exists
76
+ try {
77
+ await fs.access(resolvedPath);
78
+ }
79
+ catch {
80
+ return createErrorResult(`Path not found: ${resolvedPath}`);
81
+ }
82
+ const stats = await fs.stat(resolvedPath);
83
+ if (!stats.isDirectory()) {
84
+ return createErrorResult("findDuplicates requires a directory path");
85
+ }
86
+ // Collect files
87
+ const files = [];
88
+ await collectFiles(resolvedPath, files, maxFiles);
89
+ // Check for identical files first (if not ignoring)
90
+ const fileHashes = new Map();
91
+ if (!ignoreIdenticalFiles) {
92
+ for (const file of files) {
93
+ const content = await fs.readFile(file, "utf-8");
94
+ const hash = crypto.createHash("md5").update(content).digest("hex");
95
+ const existing = fileHashes.get(hash) ?? [];
96
+ existing.push(file);
97
+ fileHashes.set(hash, existing);
98
+ }
99
+ }
100
+ // Extract code blocks from each file
101
+ const allBlocks = [];
102
+ let totalLines = 0;
103
+ for (const file of files) {
104
+ const blocks = await extractCodeBlocks(file, minLines, minTokens);
105
+ allBlocks.push(...blocks);
106
+ // Count total lines
107
+ const content = await fs.readFile(file, "utf-8");
108
+ totalLines += content.split("\n").length;
109
+ }
110
+ // Group by hash
111
+ const hashGroups = new Map();
112
+ for (const block of allBlocks) {
113
+ const existing = hashGroups.get(block.hash) ?? [];
114
+ existing.push(block);
115
+ hashGroups.set(block.hash, existing);
116
+ }
117
+ // Filter to only groups with duplicates
118
+ const duplicateGroups = [];
119
+ let groupId = 1;
120
+ let totalDuplicateLines = 0;
121
+ for (const [_hash, blocks] of hashGroups) {
122
+ if (blocks.length < 2)
123
+ continue;
124
+ // Skip if all from same file
125
+ const uniqueFiles = new Set(blocks.map((b) => b.path));
126
+ if (uniqueFiles.size < 2)
127
+ continue;
128
+ const locations = blocks.map((b) => ({
129
+ path: b.path,
130
+ startLine: b.startLine,
131
+ endLine: b.endLine,
132
+ }));
133
+ const lines = blocks[0].endLine - blocks[0].startLine + 1;
134
+ totalDuplicateLines += lines * (blocks.length - 1); // Count extra copies
135
+ duplicateGroups.push({
136
+ id: `DUP-${String(groupId++)}`,
137
+ lines,
138
+ tokens: blocks[0].tokens,
139
+ locations,
140
+ sample: truncateSample(blocks[0].content, 5), // First 5 lines
141
+ });
142
+ }
143
+ // Sort by lines (largest first)
144
+ duplicateGroups.sort((a, b) => b.lines - a.lines);
145
+ // Limit results
146
+ const limitedGroups = duplicateGroups.slice(0, 20);
147
+ const percentageDuplicate = totalLines > 0
148
+ ? Math.round((totalDuplicateLines / totalLines) * 10000) / 100
149
+ : 0;
150
+ const result = {
151
+ path: resolvedPath,
152
+ duplicates: limitedGroups,
153
+ stats: {
154
+ filesAnalyzed: files.length,
155
+ duplicateGroups: duplicateGroups.length,
156
+ totalDuplicateLines,
157
+ percentageDuplicate,
158
+ },
159
+ };
160
+ return createSuccessResult(result);
161
+ }
162
+ catch (error) {
163
+ return createErrorResult(`Failed to find duplicates: ${error instanceof Error ? error.message : String(error)}`);
164
+ }
165
+ }
166
+ /**
167
+ * Collect files to analyze
168
+ */
169
+ async function collectFiles(dirPath, files, maxFiles, currentDepth = 0) {
170
+ if (currentDepth > 10 || files.length >= maxFiles)
171
+ return;
172
+ try {
173
+ const entries = await fs.readdir(dirPath, { withFileTypes: true });
174
+ for (const entry of entries) {
175
+ if (files.length >= maxFiles)
176
+ break;
177
+ const fullPath = path.join(dirPath, entry.name);
178
+ if (entry.isDirectory()) {
179
+ if (DEFAULT_EXCLUDE.includes(entry.name))
180
+ continue;
181
+ await collectFiles(fullPath, files, maxFiles, currentDepth + 1);
182
+ }
183
+ else if (entry.isFile()) {
184
+ // Only include Python files
185
+ if (fullPath.endsWith(".py") && !fullPath.endsWith(".pyi")) {
186
+ files.push(fullPath);
187
+ }
188
+ }
189
+ }
190
+ }
191
+ catch {
192
+ // Ignore permission errors
193
+ }
194
+ }
195
+ /**
196
+ * Extract code blocks from a file using sliding window
197
+ */
198
+ async function extractCodeBlocks(filePath, minLines, minTokens) {
199
+ try {
200
+ const content = await fs.readFile(filePath, "utf-8");
201
+ const lines = content.split("\n");
202
+ const blocks = [];
203
+ // Use sliding window approach
204
+ for (let start = 0; start <= lines.length - minLines; start++) {
205
+ // Try different block sizes
206
+ for (let size = minLines; size <= Math.min(minLines * 3, lines.length - start); size++) {
207
+ const blockLines = lines.slice(start, start + size);
208
+ const blockContent = blockLines.join("\n");
209
+ // Normalize content for comparison
210
+ const normalized = normalizeCode(blockContent);
211
+ // Count tokens (simplified: split on whitespace and punctuation)
212
+ const tokens = countTokens(normalized);
213
+ if (tokens < minTokens)
214
+ continue;
215
+ // Skip if mostly empty/comments
216
+ const significantLines = blockLines.filter((l) => l.trim() && !l.trim().startsWith("#"));
217
+ if (significantLines.length < minLines / 2)
218
+ continue;
219
+ const hash = crypto.createHash("md5").update(normalized).digest("hex");
220
+ blocks.push({
221
+ hash,
222
+ path: filePath,
223
+ startLine: start + 1,
224
+ endLine: start + size,
225
+ content: blockContent,
226
+ tokens,
227
+ });
228
+ }
229
+ }
230
+ return blocks;
231
+ }
232
+ catch {
233
+ return [];
234
+ }
235
+ }
236
+ /**
237
+ * Normalize code for comparison (remove variable names, whitespace differences)
238
+ */
239
+ function normalizeCode(code) {
240
+ return (code
241
+ // Remove comments
242
+ .replace(/#.*$/gm, "")
243
+ // Remove docstrings (simplified)
244
+ .replace(/"""[\s\S]*?"""/g, '""')
245
+ .replace(/'''[\s\S]*?'''/g, "''")
246
+ // Normalize whitespace
247
+ .replace(/\s+/g, " ")
248
+ // Remove string literals (replace with placeholder)
249
+ .replace(/"[^"]*"/g, '""')
250
+ .replace(/'[^']*'/g, "''")
251
+ // Normalize numbers
252
+ .replace(/\b\d+\b/g, "0")
253
+ .trim());
254
+ }
255
+ /**
256
+ * Count tokens in code
257
+ */
258
+ function countTokens(code) {
259
+ // Split on whitespace and common punctuation
260
+ return code.split(/[\s{}()[\];,.:=+\-*/<>]+/).filter((t) => t.length > 0)
261
+ .length;
262
+ }
263
+ /**
264
+ * Truncate sample to first N lines
265
+ */
266
+ function truncateSample(content, maxLines) {
267
+ const lines = content.split("\n");
268
+ if (lines.length <= maxLines)
269
+ return content;
270
+ return lines.slice(0, maxLines).join("\n") + "\n...";
271
+ }
272
+ /**
273
+ * Factory function to create a customized findDuplicates tool
274
+ */
275
+ export function createFindDuplicatesTool(options) {
276
+ return defineTool({
277
+ name: "find_duplicates_python",
278
+ description: TOOL_DESCRIPTION,
279
+ inputSchema: TOOL_INPUT_SCHEMA,
280
+ execute: async (input) => {
281
+ const modifiedInput = {
282
+ ...input,
283
+ minLines: input.minLines ?? options?.defaultMinLines,
284
+ maxFiles: input.maxFiles ?? options?.defaultMaxFiles,
285
+ };
286
+ return executeFindDuplicates(modifiedInput);
287
+ },
288
+ });
289
+ }
@@ -0,0 +1,71 @@
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 type { Tool } from "@compilr-dev/agents";
8
+ /**
9
+ * Input for findImplementations tool
10
+ */
11
+ export interface FindImplementationsInput {
12
+ /** Base class, ABC, or Protocol name to find implementations of */
13
+ name: string;
14
+ /** Directory or file to search in (defaults to current directory) */
15
+ scope?: string;
16
+ /** Include abstract classes that partially implement (default: false) */
17
+ includeAbstract?: boolean;
18
+ /** Maximum files to search (default: 100) */
19
+ maxFiles?: number;
20
+ }
21
+ /**
22
+ * Implementation info
23
+ */
24
+ export interface ImplementationInfo {
25
+ /** Class name */
26
+ name: string;
27
+ /** File path */
28
+ path: string;
29
+ /** Line number */
30
+ line: number;
31
+ /** Kind of implementation */
32
+ kind: "class" | "dataclass";
33
+ /** Is abstract */
34
+ isAbstract?: boolean;
35
+ /** List of implemented methods */
36
+ implementedMethods?: string[];
37
+ /** List of missing abstract methods */
38
+ missingMethods?: string[];
39
+ }
40
+ /**
41
+ * Result of findImplementations
42
+ */
43
+ export interface FindImplementationsResult {
44
+ /** Target base class/protocol name */
45
+ target: string;
46
+ /** Location of target definition */
47
+ targetLocation?: {
48
+ path: string;
49
+ line: number;
50
+ };
51
+ /** Found implementations */
52
+ implementations: ImplementationInfo[];
53
+ /** Statistics */
54
+ stats: {
55
+ filesSearched: number;
56
+ implementationsFound: number;
57
+ timeMs: number;
58
+ };
59
+ }
60
+ /**
61
+ * findImplementations tool
62
+ */
63
+ export declare const findImplementationsTool: Tool<FindImplementationsInput>;
64
+ /**
65
+ * Factory function to create a customized findImplementations tool
66
+ */
67
+ export declare function createFindImplementationsTool(options?: {
68
+ defaultScope?: string;
69
+ defaultMaxFiles?: number;
70
+ }): Tool<FindImplementationsInput>;
71
+ //# sourceMappingURL=find-implementations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"find-implementations.d.ts","sourceRoot":"","sources":["../../src/tools/find-implementations.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EAAE,IAAI,EAAuB,MAAM,qBAAqB,CAAC;AAOrE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,IAAI,EAAE,OAAO,GAAG,WAAW,CAAC;IAC5B,kBAAkB;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kCAAkC;IAClC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,uCAAuC;IACvC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,cAAc,CAAC,EAAE;QACf,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;IACF,4BAA4B;IAC5B,eAAe,EAAE,kBAAkB,EAAE,CAAC;IACtC,iBAAiB;IACjB,KAAK,EAAE;QACL,aAAa,EAAE,MAAM,CAAC;QACtB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAgDD;;GAEG;AACH,eAAO,MAAM,uBAAuB,gCAKlC,CAAC;AAwYH;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,CAAC,EAAE;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,IAAI,CAAC,wBAAwB,CAAC,CAcjC"}