@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,477 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Parser using Tree-sitter
|
|
3
|
+
*
|
|
4
|
+
* Provides AST parsing and traversal for Python source files.
|
|
5
|
+
*/
|
|
6
|
+
import Parser from "tree-sitter";
|
|
7
|
+
import Python from "tree-sitter-python";
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
// Initialize parser
|
|
10
|
+
const parser = new Parser();
|
|
11
|
+
// Cast to any to work around tree-sitter type mismatches between versions
|
|
12
|
+
parser.setLanguage(Python);
|
|
13
|
+
/**
|
|
14
|
+
* Parse Python source code
|
|
15
|
+
*/
|
|
16
|
+
export function parseSource(source) {
|
|
17
|
+
const tree = parser.parse(source);
|
|
18
|
+
const errors = [];
|
|
19
|
+
// Find error nodes
|
|
20
|
+
const findErrors = (node) => {
|
|
21
|
+
if (node.type === "ERROR" || node.isMissing) {
|
|
22
|
+
errors.push({
|
|
23
|
+
message: node.isMissing ? `Missing: ${node.type}` : "Syntax error",
|
|
24
|
+
line: node.startPosition.row + 1,
|
|
25
|
+
column: node.startPosition.column,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
for (const child of node.children) {
|
|
29
|
+
findErrors(child);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
findErrors(tree.rootNode);
|
|
33
|
+
return {
|
|
34
|
+
tree,
|
|
35
|
+
source,
|
|
36
|
+
hasErrors: errors.length > 0,
|
|
37
|
+
errors,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parse Python file
|
|
42
|
+
*/
|
|
43
|
+
export async function parseFile(path) {
|
|
44
|
+
const source = await readFile(path, "utf-8");
|
|
45
|
+
return parseSource(source);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get text from a node
|
|
49
|
+
*/
|
|
50
|
+
export function getNodeText(node, source) {
|
|
51
|
+
return source.slice(node.startIndex, node.endIndex);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Extract docstring from a block
|
|
55
|
+
*/
|
|
56
|
+
export function extractDocstring(node, source) {
|
|
57
|
+
// Look for the first expression_statement containing a string
|
|
58
|
+
const block = node.childForFieldName("body");
|
|
59
|
+
if (!block)
|
|
60
|
+
return undefined;
|
|
61
|
+
const firstChild = block.firstChild;
|
|
62
|
+
if (!firstChild)
|
|
63
|
+
return undefined;
|
|
64
|
+
if (firstChild.type === "expression_statement") {
|
|
65
|
+
const expr = firstChild.firstChild;
|
|
66
|
+
if (expr && expr.type === "string") {
|
|
67
|
+
const text = getNodeText(expr, source);
|
|
68
|
+
// Remove quotes (single, double, triple)
|
|
69
|
+
return text.replace(/^['"]{1,3}|['"]{1,3}$/g, "").trim();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Parse decorators from a decorated_definition
|
|
76
|
+
*/
|
|
77
|
+
export function parseDecorators(node, source) {
|
|
78
|
+
const decorators = [];
|
|
79
|
+
for (const child of node.children) {
|
|
80
|
+
if (child.type === "decorator") {
|
|
81
|
+
let nameNode;
|
|
82
|
+
let argsNode;
|
|
83
|
+
// Check if decorator is a call (e.g., @lru_cache(maxsize=100))
|
|
84
|
+
const callNode = child.children.find((c) => c.type === "call");
|
|
85
|
+
if (callNode) {
|
|
86
|
+
// Get the function name from the call
|
|
87
|
+
nameNode = callNode.children.find((c) => c.type === "identifier" || c.type === "attribute");
|
|
88
|
+
// Get arguments from the call's argument_list
|
|
89
|
+
argsNode = callNode.children.find((c) => c.type === "argument_list");
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// Simple decorator (e.g., @property)
|
|
93
|
+
nameNode = child.children.find((c) => c.type === "identifier" || c.type === "attribute");
|
|
94
|
+
}
|
|
95
|
+
if (nameNode) {
|
|
96
|
+
const decorator = {
|
|
97
|
+
name: getNodeText(nameNode, source),
|
|
98
|
+
line: child.startPosition.row + 1,
|
|
99
|
+
};
|
|
100
|
+
// Parse arguments if present
|
|
101
|
+
if (argsNode) {
|
|
102
|
+
decorator.arguments = [];
|
|
103
|
+
for (const arg of argsNode.children) {
|
|
104
|
+
if (arg.type !== "(" && arg.type !== ")" && arg.type !== ",") {
|
|
105
|
+
decorator.arguments.push(getNodeText(arg, source));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
decorators.push(decorator);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return decorators;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Parse function parameters
|
|
117
|
+
*/
|
|
118
|
+
export function parseParameters(node, source) {
|
|
119
|
+
const params = [];
|
|
120
|
+
let seenSlash = false;
|
|
121
|
+
let seenStar = false;
|
|
122
|
+
const paramsNode = node.childForFieldName("parameters");
|
|
123
|
+
if (!paramsNode)
|
|
124
|
+
return params;
|
|
125
|
+
for (const child of paramsNode.children) {
|
|
126
|
+
if (child.type === "/" || getNodeText(child, source) === "/") {
|
|
127
|
+
seenSlash = true;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (child.type === "*" && !child.firstChild) {
|
|
131
|
+
seenStar = true;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (child.type === "identifier" ||
|
|
135
|
+
child.type === "typed_parameter" ||
|
|
136
|
+
child.type === "default_parameter" ||
|
|
137
|
+
child.type === "typed_default_parameter" ||
|
|
138
|
+
child.type === "list_splat_pattern" ||
|
|
139
|
+
child.type === "dictionary_splat_pattern") {
|
|
140
|
+
const param = {
|
|
141
|
+
name: "",
|
|
142
|
+
isArgs: child.type === "list_splat_pattern",
|
|
143
|
+
isKwargs: child.type === "dictionary_splat_pattern",
|
|
144
|
+
isPositionalOnly: !seenSlash,
|
|
145
|
+
isKeywordOnly: seenStar && !child.type.includes("splat"),
|
|
146
|
+
};
|
|
147
|
+
// Update positional only after we've seen /
|
|
148
|
+
if (seenSlash) {
|
|
149
|
+
param.isPositionalOnly = false;
|
|
150
|
+
}
|
|
151
|
+
// Get name - find the first identifier child
|
|
152
|
+
if (child.type === "identifier") {
|
|
153
|
+
param.name = getNodeText(child, source);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
// For typed_parameter, default_parameter, etc., find the identifier child
|
|
157
|
+
const nameNode = child.children.find((c) => c.type === "identifier");
|
|
158
|
+
if (nameNode) {
|
|
159
|
+
param.name = getNodeText(nameNode, source);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Skip 'self' and 'cls'
|
|
163
|
+
if (param.name === "self" || param.name === "cls") {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// Get type annotation
|
|
167
|
+
const typeNode = child.childForFieldName("type");
|
|
168
|
+
if (typeNode) {
|
|
169
|
+
param.type = getNodeText(typeNode, source);
|
|
170
|
+
}
|
|
171
|
+
// Get default value
|
|
172
|
+
const valueNode = child.childForFieldName("value");
|
|
173
|
+
if (valueNode) {
|
|
174
|
+
param.default = getNodeText(valueNode, source);
|
|
175
|
+
}
|
|
176
|
+
if (param.name) {
|
|
177
|
+
params.push(param);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return params;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Parse a function definition
|
|
185
|
+
*/
|
|
186
|
+
export function parseFunction(node, source, decorators = []) {
|
|
187
|
+
const nameNode = node.childForFieldName("name");
|
|
188
|
+
const returnTypeNode = node.childForFieldName("return_type");
|
|
189
|
+
// Check for async - either the node type or has an 'async' child
|
|
190
|
+
const isAsync = node.type === "async_function_definition" ||
|
|
191
|
+
node.parent?.type === "async_function_definition" ||
|
|
192
|
+
node.children.some((c) => c.type === "async");
|
|
193
|
+
// Check if generator (contains yield)
|
|
194
|
+
let isGenerator = false;
|
|
195
|
+
const checkYield = (n) => {
|
|
196
|
+
if (n.type === "yield")
|
|
197
|
+
return true;
|
|
198
|
+
for (const child of n.children) {
|
|
199
|
+
if (checkYield(child))
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
return false;
|
|
203
|
+
};
|
|
204
|
+
isGenerator = checkYield(node);
|
|
205
|
+
return {
|
|
206
|
+
name: nameNode ? getNodeText(nameNode, source) : "",
|
|
207
|
+
line: node.startPosition.row + 1,
|
|
208
|
+
endLine: node.endPosition.row + 1,
|
|
209
|
+
column: node.startPosition.column,
|
|
210
|
+
parameters: parseParameters(node, source),
|
|
211
|
+
returnType: returnTypeNode
|
|
212
|
+
? getNodeText(returnTypeNode, source)
|
|
213
|
+
: undefined,
|
|
214
|
+
decorators,
|
|
215
|
+
isAsync,
|
|
216
|
+
isGenerator,
|
|
217
|
+
docstring: extractDocstring(node, source),
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Parse a method (function inside a class)
|
|
222
|
+
*/
|
|
223
|
+
export function parseMethod(node, source, decorators = []) {
|
|
224
|
+
const func = parseFunction(node, source, decorators);
|
|
225
|
+
const decoratorNames = decorators.map((d) => d.name);
|
|
226
|
+
return {
|
|
227
|
+
...func,
|
|
228
|
+
isClassMethod: decoratorNames.includes("classmethod"),
|
|
229
|
+
isStaticMethod: decoratorNames.includes("staticmethod"),
|
|
230
|
+
isProperty: decoratorNames.includes("property"),
|
|
231
|
+
isAbstract: decoratorNames.includes("abstractmethod"),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Parse a class definition
|
|
236
|
+
*/
|
|
237
|
+
export function parseClass(node, source, decorators = []) {
|
|
238
|
+
const nameNode = node.childForFieldName("name");
|
|
239
|
+
const basesNode = node.childForFieldName("superclasses");
|
|
240
|
+
const bases = [];
|
|
241
|
+
if (basesNode) {
|
|
242
|
+
for (const child of basesNode.children) {
|
|
243
|
+
if (child.type === "identifier" || child.type === "attribute") {
|
|
244
|
+
bases.push(getNodeText(child, source));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
const methods = [];
|
|
249
|
+
const attributes = [];
|
|
250
|
+
// Parse body
|
|
251
|
+
const body = node.childForFieldName("body");
|
|
252
|
+
if (body) {
|
|
253
|
+
for (const child of body.children) {
|
|
254
|
+
if (child.type === "function_definition" ||
|
|
255
|
+
child.type === "async_function_definition") {
|
|
256
|
+
methods.push(parseMethod(child, source));
|
|
257
|
+
}
|
|
258
|
+
else if (child.type === "decorated_definition") {
|
|
259
|
+
const decs = parseDecorators(child, source);
|
|
260
|
+
const funcNode = child.children.find((c) => c.type === "function_definition" ||
|
|
261
|
+
c.type === "async_function_definition");
|
|
262
|
+
if (funcNode) {
|
|
263
|
+
methods.push(parseMethod(funcNode, source, decs));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
else if (child.type === "expression_statement") {
|
|
267
|
+
// Class-level assignments
|
|
268
|
+
const expr = child.firstChild;
|
|
269
|
+
if (expr &&
|
|
270
|
+
(expr.type === "assignment" || expr.type === "annotated_assignment")) {
|
|
271
|
+
const leftNode = expr.childForFieldName("left") || expr.firstChild;
|
|
272
|
+
if (leftNode && leftNode.type === "identifier") {
|
|
273
|
+
const attr = {
|
|
274
|
+
name: getNodeText(leftNode, source),
|
|
275
|
+
line: child.startPosition.row + 1,
|
|
276
|
+
isClassAttribute: true,
|
|
277
|
+
};
|
|
278
|
+
// Get type annotation
|
|
279
|
+
const typeNode = expr.childForFieldName("type");
|
|
280
|
+
if (typeNode) {
|
|
281
|
+
attr.type = getNodeText(typeNode, source);
|
|
282
|
+
}
|
|
283
|
+
// Get value
|
|
284
|
+
const valueNode = expr.childForFieldName("right") ||
|
|
285
|
+
expr.childForFieldName("value");
|
|
286
|
+
if (valueNode) {
|
|
287
|
+
attr.default = getNodeText(valueNode, source);
|
|
288
|
+
}
|
|
289
|
+
attributes.push(attr);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const decoratorNames = decorators.map((d) => d.name);
|
|
296
|
+
const isAbstract = bases.some((b) => b === "ABC" || b.endsWith(".ABC") || b === "ABCMeta");
|
|
297
|
+
const isDataclass = decoratorNames.includes("dataclass");
|
|
298
|
+
return {
|
|
299
|
+
name: nameNode ? getNodeText(nameNode, source) : "",
|
|
300
|
+
line: node.startPosition.row + 1,
|
|
301
|
+
endLine: node.endPosition.row + 1,
|
|
302
|
+
column: node.startPosition.column,
|
|
303
|
+
bases,
|
|
304
|
+
decorators,
|
|
305
|
+
methods,
|
|
306
|
+
attributes,
|
|
307
|
+
docstring: extractDocstring(node, source),
|
|
308
|
+
isAbstract,
|
|
309
|
+
isDataclass,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Parse import statement
|
|
314
|
+
*/
|
|
315
|
+
export function parseImport(node, source) {
|
|
316
|
+
if (node.type === "import_statement") {
|
|
317
|
+
const names = [];
|
|
318
|
+
let mainModule = "";
|
|
319
|
+
for (const child of node.children) {
|
|
320
|
+
if (child.type === "dotted_name") {
|
|
321
|
+
mainModule = getNodeText(child, source);
|
|
322
|
+
names.push({ name: mainModule });
|
|
323
|
+
}
|
|
324
|
+
else if (child.type === "aliased_import") {
|
|
325
|
+
const nameNode = child.childForFieldName("name");
|
|
326
|
+
const aliasNode = child.childForFieldName("alias");
|
|
327
|
+
if (nameNode) {
|
|
328
|
+
const entry = {
|
|
329
|
+
name: getNodeText(nameNode, source),
|
|
330
|
+
};
|
|
331
|
+
if (aliasNode) {
|
|
332
|
+
entry.alias = getNodeText(aliasNode, source);
|
|
333
|
+
}
|
|
334
|
+
names.push(entry);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
module: mainModule || (names[0]?.name ?? ""),
|
|
340
|
+
names,
|
|
341
|
+
line: node.startPosition.row + 1,
|
|
342
|
+
isFromImport: false,
|
|
343
|
+
isRelative: false,
|
|
344
|
+
relativeDots: 0,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
// import_from_statement
|
|
348
|
+
const moduleNode = node.childForFieldName("module_name");
|
|
349
|
+
const moduleName = moduleNode ? getNodeText(moduleNode, source) : "";
|
|
350
|
+
// Count relative dots
|
|
351
|
+
let relativeDots = 0;
|
|
352
|
+
for (const child of node.children) {
|
|
353
|
+
if (getNodeText(child, source) === ".") {
|
|
354
|
+
relativeDots++;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const names = [];
|
|
358
|
+
// Check for wildcard import
|
|
359
|
+
const wildcardNode = node.children.find((c) => getNodeText(c, source) === "*");
|
|
360
|
+
if (wildcardNode) {
|
|
361
|
+
names.push({ name: "*" });
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
// Parse imported names
|
|
365
|
+
for (const child of node.children) {
|
|
366
|
+
if (child.type === "dotted_name" && child !== moduleNode) {
|
|
367
|
+
names.push({ name: getNodeText(child, source) });
|
|
368
|
+
}
|
|
369
|
+
else if (child.type === "aliased_import") {
|
|
370
|
+
const nameNode = child.childForFieldName("name");
|
|
371
|
+
const aliasNode = child.childForFieldName("alias");
|
|
372
|
+
if (nameNode) {
|
|
373
|
+
const entry = {
|
|
374
|
+
name: getNodeText(nameNode, source),
|
|
375
|
+
};
|
|
376
|
+
if (aliasNode) {
|
|
377
|
+
entry.alias = getNodeText(aliasNode, source);
|
|
378
|
+
}
|
|
379
|
+
names.push(entry);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
module: moduleName,
|
|
386
|
+
names,
|
|
387
|
+
line: node.startPosition.row + 1,
|
|
388
|
+
isFromImport: true,
|
|
389
|
+
isRelative: relativeDots > 0,
|
|
390
|
+
relativeDots,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Parse variable assignment
|
|
395
|
+
*/
|
|
396
|
+
export function parseVariable(node, source) {
|
|
397
|
+
let nameNode = null;
|
|
398
|
+
let typeNode = null;
|
|
399
|
+
let valueNode = null;
|
|
400
|
+
if (node.type === "assignment") {
|
|
401
|
+
nameNode = node.childForFieldName("left");
|
|
402
|
+
valueNode = node.childForFieldName("right");
|
|
403
|
+
}
|
|
404
|
+
else if (node.type === "annotated_assignment") {
|
|
405
|
+
nameNode = node.childForFieldName("left") || node.firstChild;
|
|
406
|
+
typeNode = node.childForFieldName("type");
|
|
407
|
+
valueNode = node.childForFieldName("value");
|
|
408
|
+
}
|
|
409
|
+
if (!nameNode || nameNode.type !== "identifier") {
|
|
410
|
+
return null;
|
|
411
|
+
}
|
|
412
|
+
const name = getNodeText(nameNode, source);
|
|
413
|
+
// Check if constant (all uppercase)
|
|
414
|
+
const isConstant = /^[A-Z][A-Z0-9_]*$/.test(name);
|
|
415
|
+
return {
|
|
416
|
+
name,
|
|
417
|
+
type: typeNode ? getNodeText(typeNode, source) : undefined,
|
|
418
|
+
value: valueNode ? getNodeText(valueNode, source) : undefined,
|
|
419
|
+
line: node.startPosition.row + 1,
|
|
420
|
+
column: node.startPosition.column,
|
|
421
|
+
isConstant,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Walk the AST and collect all nodes of given types
|
|
426
|
+
*/
|
|
427
|
+
export function* walkTree(node, types) {
|
|
428
|
+
if (!types || types.includes(node.type)) {
|
|
429
|
+
yield node;
|
|
430
|
+
}
|
|
431
|
+
for (const child of node.children) {
|
|
432
|
+
yield* walkTree(child, types);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Find all nodes matching a predicate
|
|
437
|
+
*/
|
|
438
|
+
export function findNodes(node, predicate) {
|
|
439
|
+
const results = [];
|
|
440
|
+
const walk = (n) => {
|
|
441
|
+
if (predicate(n)) {
|
|
442
|
+
results.push(n);
|
|
443
|
+
}
|
|
444
|
+
for (const child of n.children) {
|
|
445
|
+
walk(child);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
walk(node);
|
|
449
|
+
return results;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Get the containing scope for a node (function or class)
|
|
453
|
+
*/
|
|
454
|
+
export function getContainingScope(node) {
|
|
455
|
+
let current = node.parent;
|
|
456
|
+
while (current) {
|
|
457
|
+
if (current.type === "function_definition" ||
|
|
458
|
+
current.type === "async_function_definition") {
|
|
459
|
+
const nameNode = current.childForFieldName("name");
|
|
460
|
+
return {
|
|
461
|
+
type: "function",
|
|
462
|
+
name: nameNode?.text ?? "",
|
|
463
|
+
node: current,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
if (current.type === "class_definition") {
|
|
467
|
+
const nameNode = current.childForFieldName("name");
|
|
468
|
+
return {
|
|
469
|
+
type: "class",
|
|
470
|
+
name: nameNode?.text ?? "",
|
|
471
|
+
node: current,
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
current = current.parent;
|
|
475
|
+
}
|
|
476
|
+
return null;
|
|
477
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Skills
|
|
3
|
+
*
|
|
4
|
+
* Skills for Python code analysis, best practices, and workflows.
|
|
5
|
+
*/
|
|
6
|
+
export { pythonBestPracticesSkill } from "./python-best-practices.js";
|
|
7
|
+
export { pythonCodeHealthSkill } from "./python-code-health.js";
|
|
8
|
+
export { pythonCodeStructureSkill } from "./python-code-structure.js";
|
|
9
|
+
export { pythonDependencyAuditSkill } from "./python-dependency-audit.js";
|
|
10
|
+
export { pythonRefactorImpactSkill } from "./python-refactor-impact.js";
|
|
11
|
+
import type { Skill } from "@compilr-dev/agents";
|
|
12
|
+
/**
|
|
13
|
+
* All Python skills
|
|
14
|
+
*/
|
|
15
|
+
export declare const pythonSkills: Skill[];
|
|
16
|
+
/**
|
|
17
|
+
* Python skills by name for selective use
|
|
18
|
+
*/
|
|
19
|
+
export declare const pythonSkillsMap: {
|
|
20
|
+
readonly bestPractices: Skill;
|
|
21
|
+
readonly codeHealth: Skill;
|
|
22
|
+
readonly codeStructure: Skill;
|
|
23
|
+
readonly dependencyAudit: Skill;
|
|
24
|
+
readonly refactorImpact: Skill;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/skills/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAOxE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEjD;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,EAM/B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe;;;;;;CAMlB,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Skills
|
|
3
|
+
*
|
|
4
|
+
* Skills for Python code analysis, best practices, and workflows.
|
|
5
|
+
*/
|
|
6
|
+
// Individual skill exports
|
|
7
|
+
export { pythonBestPracticesSkill } from "./python-best-practices.js";
|
|
8
|
+
export { pythonCodeHealthSkill } from "./python-code-health.js";
|
|
9
|
+
export { pythonCodeStructureSkill } from "./python-code-structure.js";
|
|
10
|
+
export { pythonDependencyAuditSkill } from "./python-dependency-audit.js";
|
|
11
|
+
export { pythonRefactorImpactSkill } from "./python-refactor-impact.js";
|
|
12
|
+
import { pythonBestPracticesSkill } from "./python-best-practices.js";
|
|
13
|
+
import { pythonCodeHealthSkill } from "./python-code-health.js";
|
|
14
|
+
import { pythonCodeStructureSkill } from "./python-code-structure.js";
|
|
15
|
+
import { pythonDependencyAuditSkill } from "./python-dependency-audit.js";
|
|
16
|
+
import { pythonRefactorImpactSkill } from "./python-refactor-impact.js";
|
|
17
|
+
/**
|
|
18
|
+
* All Python skills
|
|
19
|
+
*/
|
|
20
|
+
export const pythonSkills = [
|
|
21
|
+
pythonBestPracticesSkill,
|
|
22
|
+
pythonCodeHealthSkill,
|
|
23
|
+
pythonCodeStructureSkill,
|
|
24
|
+
pythonDependencyAuditSkill,
|
|
25
|
+
pythonRefactorImpactSkill,
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Python skills by name for selective use
|
|
29
|
+
*/
|
|
30
|
+
export const pythonSkillsMap = {
|
|
31
|
+
bestPractices: pythonBestPracticesSkill,
|
|
32
|
+
codeHealth: pythonCodeHealthSkill,
|
|
33
|
+
codeStructure: pythonCodeStructureSkill,
|
|
34
|
+
dependencyAudit: pythonDependencyAuditSkill,
|
|
35
|
+
refactorImpact: pythonRefactorImpactSkill,
|
|
36
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python-best-practices.d.ts","sourceRoot":"","sources":["../../src/skills/python-best-practices.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,wBAAwB,qCAuEnC,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Best Practices Skill
|
|
3
|
+
*
|
|
4
|
+
* Provides guidance for Python-specific coding patterns and analysis.
|
|
5
|
+
*/
|
|
6
|
+
import { defineSkill } from "@compilr-dev/agents";
|
|
7
|
+
export const pythonBestPracticesSkill = defineSkill({
|
|
8
|
+
name: "python-best-practices",
|
|
9
|
+
description: "Python-specific coding guidance and analysis workflow",
|
|
10
|
+
version: "1.0.0",
|
|
11
|
+
tags: ["python", "best-practices", "analysis"],
|
|
12
|
+
prompt: `You are in Python analysis mode. Use these tools for comprehensive code understanding:
|
|
13
|
+
|
|
14
|
+
## Code Structure Analysis
|
|
15
|
+
- **getFileStructure**: Get overview of classes, functions, imports, variables
|
|
16
|
+
- Use to understand file organization
|
|
17
|
+
- Identify public API surface
|
|
18
|
+
- See class hierarchies and method signatures
|
|
19
|
+
|
|
20
|
+
## Python-Specific Guidance
|
|
21
|
+
|
|
22
|
+
### Naming Conventions (PEP 8)
|
|
23
|
+
- \`snake_case\` for functions, variables, modules
|
|
24
|
+
- \`PascalCase\` for classes
|
|
25
|
+
- \`UPPER_CASE\` for constants
|
|
26
|
+
- \`_private\` for internal use
|
|
27
|
+
- \`__dunder__\` for special methods
|
|
28
|
+
|
|
29
|
+
### Type Hints (PEP 484, 585)
|
|
30
|
+
- Always add type hints to function signatures
|
|
31
|
+
- Use \`Optional[T]\` for nullable values
|
|
32
|
+
- Use \`Union[A, B]\` for multiple types
|
|
33
|
+
- Prefer \`list[T]\` over \`List[T]\` (Python 3.9+)
|
|
34
|
+
|
|
35
|
+
### Docstrings (PEP 257)
|
|
36
|
+
- Use triple quotes for all docstrings
|
|
37
|
+
- First line is a summary
|
|
38
|
+
- Blank line before detailed description
|
|
39
|
+
- Document parameters, returns, raises
|
|
40
|
+
|
|
41
|
+
### Best Practices
|
|
42
|
+
- Use dataclasses or attrs for data containers
|
|
43
|
+
- Prefer pathlib over os.path
|
|
44
|
+
- Use f-strings over .format()
|
|
45
|
+
- Use context managers for resources
|
|
46
|
+
- Avoid mutable default arguments: \`def foo(x=None)\` not \`def foo(x=[])\`
|
|
47
|
+
- Use \`__all__\` to define public API
|
|
48
|
+
|
|
49
|
+
### Anti-Patterns to Avoid
|
|
50
|
+
- Bare \`except:\` clauses (catch specific exceptions)
|
|
51
|
+
- \`from module import *\` (explicit imports only)
|
|
52
|
+
- Global variables
|
|
53
|
+
- Deeply nested code (refactor to smaller functions)
|
|
54
|
+
- Magic numbers (use named constants)
|
|
55
|
+
|
|
56
|
+
## Analysis Workflow
|
|
57
|
+
|
|
58
|
+
1. **Understand Structure**
|
|
59
|
+
\`\`\`
|
|
60
|
+
getFileStructure for overview
|
|
61
|
+
\`\`\`
|
|
62
|
+
|
|
63
|
+
2. **Review Complexity**
|
|
64
|
+
- Look for functions with many parameters
|
|
65
|
+
- Check for deeply nested code
|
|
66
|
+
- Identify god classes
|
|
67
|
+
|
|
68
|
+
3. **Check Code Quality**
|
|
69
|
+
- Look for missing type hints
|
|
70
|
+
- Check docstring coverage
|
|
71
|
+
- Find TODO/FIXME comments
|
|
72
|
+
|
|
73
|
+
4. **Suggest Improvements**
|
|
74
|
+
- Recommend refactoring for complex code
|
|
75
|
+
- Suggest type hints where missing
|
|
76
|
+
- Propose better naming when unclear
|
|
77
|
+
`,
|
|
78
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Code Health Skill
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates code quality analysis using complexity, dead code, duplicates, patterns, and other tools.
|
|
5
|
+
*/
|
|
6
|
+
export declare const pythonCodeHealthSkill: import("@compilr-dev/agents").Skill;
|
|
7
|
+
//# sourceMappingURL=python-code-health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"python-code-health.d.ts","sourceRoot":"","sources":["../../src/skills/python-code-health.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,eAAO,MAAM,qBAAqB,qCA0MhC,CAAC"}
|