@compilr-dev/agents-coding-go 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/go-parser.d.ts +104 -0
  6. package/dist/parser/go-parser.d.ts.map +1 -0
  7. package/dist/parser/go-parser.js +492 -0
  8. package/dist/parser/index.d.ts +6 -0
  9. package/dist/parser/index.d.ts.map +1 -0
  10. package/dist/parser/index.js +5 -0
  11. package/dist/parser/node-types.d.ts +130 -0
  12. package/dist/parser/node-types.d.ts.map +1 -0
  13. package/dist/parser/node-types.js +4 -0
  14. package/dist/skills/go-best-practices.d.ts +7 -0
  15. package/dist/skills/go-best-practices.d.ts.map +1 -0
  16. package/dist/skills/go-best-practices.js +78 -0
  17. package/dist/skills/go-code-health.d.ts +7 -0
  18. package/dist/skills/go-code-health.d.ts.map +1 -0
  19. package/dist/skills/go-code-health.js +209 -0
  20. package/dist/skills/go-code-structure.d.ts +7 -0
  21. package/dist/skills/go-code-structure.d.ts.map +1 -0
  22. package/dist/skills/go-code-structure.js +155 -0
  23. package/dist/skills/go-dependency-audit.d.ts +7 -0
  24. package/dist/skills/go-dependency-audit.d.ts.map +1 -0
  25. package/dist/skills/go-dependency-audit.js +246 -0
  26. package/dist/skills/go-refactor-impact.d.ts +7 -0
  27. package/dist/skills/go-refactor-impact.d.ts.map +1 -0
  28. package/dist/skills/go-refactor-impact.js +232 -0
  29. package/dist/skills/index.d.ts +26 -0
  30. package/dist/skills/index.d.ts.map +1 -0
  31. package/dist/skills/index.js +36 -0
  32. package/dist/tools/extract-docstrings.d.ts +51 -0
  33. package/dist/tools/extract-docstrings.d.ts.map +1 -0
  34. package/dist/tools/extract-docstrings.js +292 -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 +380 -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 +34 -0
  57. package/dist/tools/get-class-hierarchy.d.ts.map +1 -0
  58. package/dist/tools/get-class-hierarchy.js +250 -0
  59. package/dist/tools/get-complexity.d.ts +53 -0
  60. package/dist/tools/get-complexity.d.ts.map +1 -0
  61. package/dist/tools/get-complexity.js +357 -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 +389 -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 +172 -0
  71. package/dist/tools/get-imports.d.ts +30 -0
  72. package/dist/tools/get-imports.d.ts.map +1 -0
  73. package/dist/tools/get-imports.js +420 -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 +408 -0
  81. package/dist/tools/types.d.ts.map +1 -0
  82. package/dist/tools/types.js +4 -0
  83. package/package.json +86 -0
@@ -0,0 +1,250 @@
1
+ /**
2
+ * getClassHierarchy Tool
3
+ *
4
+ * Analyze type hierarchies in Go code.
5
+ * Go uses composition (embedding) rather than inheritance.
6
+ * This tool finds embedded types and types that embed a given type.
7
+ */
8
+ import * as fs from "node:fs/promises";
9
+ import * as path from "node:path";
10
+ import { defineTool, createSuccessResult, createErrorResult, } from "@compilr-dev/agents";
11
+ import { parseFile, parseClass, walkTree } from "../parser/go-parser.js";
12
+ // Tool description
13
+ const TOOL_DESCRIPTION = `Analyze type hierarchies in Go code.
14
+ Go uses composition (embedding) rather than inheritance.
15
+ Returns embedded types and types that embed the target type.`;
16
+ // Tool input schema
17
+ const TOOL_INPUT_SCHEMA = {
18
+ type: "object",
19
+ properties: {
20
+ name: {
21
+ type: "string",
22
+ description: "Type name to analyze (struct or interface)",
23
+ },
24
+ path: {
25
+ type: "string",
26
+ description: "File containing the type (optional - will search if not provided)",
27
+ },
28
+ scope: {
29
+ type: "string",
30
+ description: "Directory to search for type relationships",
31
+ },
32
+ direction: {
33
+ type: "string",
34
+ enum: ["embeds", "embeddedBy", "both"],
35
+ description: "Direction of hierarchy (default: both)",
36
+ default: "both",
37
+ },
38
+ maxDepth: {
39
+ type: "number",
40
+ description: "Maximum depth to traverse (default: 3)",
41
+ default: 3,
42
+ },
43
+ },
44
+ required: ["name"],
45
+ };
46
+ /**
47
+ * getClassHierarchy tool - Analyze type embedding in Go
48
+ */
49
+ export const getClassHierarchyTool = defineTool({
50
+ name: "get_class_hierarchy_go",
51
+ description: TOOL_DESCRIPTION,
52
+ inputSchema: TOOL_INPUT_SCHEMA,
53
+ execute: executeGetClassHierarchy,
54
+ });
55
+ /**
56
+ * Execute the getClassHierarchy tool
57
+ */
58
+ async function executeGetClassHierarchy(input) {
59
+ const { name: typeName, path: inputPath, scope, direction = "both", maxDepth = 3, } = input;
60
+ if (!typeName) {
61
+ return createErrorResult("Type name is required");
62
+ }
63
+ try {
64
+ // Find the type definition
65
+ let typeFile;
66
+ let typeLine;
67
+ let isInterface = false;
68
+ let isStruct = false;
69
+ let embeds = [];
70
+ if (inputPath) {
71
+ // Check specified file
72
+ const typeInfo = await findTypeInFile(inputPath, typeName);
73
+ if (!typeInfo) {
74
+ return createErrorResult(`Type ${typeName} not found in ${inputPath}`);
75
+ }
76
+ typeFile = inputPath;
77
+ typeLine = typeInfo.line;
78
+ isInterface = typeInfo.isInterface;
79
+ isStruct = typeInfo.isStruct;
80
+ embeds = typeInfo.embeds;
81
+ }
82
+ else {
83
+ // Search in scope or current directory
84
+ const searchDir = scope || process.cwd();
85
+ const found = await findTypeInDirectory(searchDir, typeName);
86
+ if (!found) {
87
+ return createErrorResult(`Type ${typeName} not found in ${searchDir}`);
88
+ }
89
+ typeFile = found.path;
90
+ typeLine = found.line;
91
+ isInterface = found.isInterface;
92
+ isStruct = found.isStruct;
93
+ embeds = found.embeds;
94
+ }
95
+ // Find types that embed this type
96
+ const embeddedBy = [];
97
+ if (direction === "embeddedBy" || direction === "both") {
98
+ const searchDir = scope || path.dirname(typeFile);
99
+ const embedders = await findEmbedders(searchDir, typeName, maxDepth);
100
+ embeddedBy.push(...embedders);
101
+ }
102
+ const result = {
103
+ typeName,
104
+ path: typeFile,
105
+ line: typeLine,
106
+ isInterface,
107
+ isStruct,
108
+ embeds,
109
+ embeddedBy,
110
+ };
111
+ return createSuccessResult(result);
112
+ }
113
+ catch (error) {
114
+ const message = error instanceof Error ? error.message : String(error);
115
+ return createErrorResult(`Failed to analyze type hierarchy: ${message}`);
116
+ }
117
+ }
118
+ /**
119
+ * Find a type in a specific file
120
+ */
121
+ async function findTypeInFile(filePath, typeName) {
122
+ try {
123
+ const parseResult = await parseFile(filePath);
124
+ const { tree, source } = parseResult;
125
+ for (const node of walkTree(tree.rootNode, ["type_declaration"])) {
126
+ const typeInfo = parseClass(node, source);
127
+ if (typeInfo.name === typeName) {
128
+ return {
129
+ line: typeInfo.line,
130
+ isInterface: typeInfo.isInterface ?? false,
131
+ isStruct: typeInfo.isStruct ?? false,
132
+ embeds: typeInfo.bases.map((name) => ({
133
+ name,
134
+ isExternal: name.includes("."),
135
+ isPointer: false, // Would need more parsing to detect
136
+ })),
137
+ };
138
+ }
139
+ }
140
+ }
141
+ catch {
142
+ // Skip files that can't be parsed
143
+ }
144
+ return null;
145
+ }
146
+ /**
147
+ * Find a type in a directory
148
+ */
149
+ async function findTypeInDirectory(dir, typeName) {
150
+ async function search(currentDir) {
151
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
152
+ for (const entry of entries) {
153
+ const fullPath = path.join(currentDir, entry.name);
154
+ if (entry.isDirectory()) {
155
+ // Skip vendor and hidden directories
156
+ if (entry.name === "vendor" ||
157
+ entry.name.startsWith(".") ||
158
+ entry.name === "testdata") {
159
+ continue;
160
+ }
161
+ const found = await search(fullPath);
162
+ if (found)
163
+ return found;
164
+ }
165
+ else if (entry.isFile() && entry.name.endsWith(".go")) {
166
+ const info = await findTypeInFile(fullPath, typeName);
167
+ if (info) {
168
+ return { ...info, path: fullPath };
169
+ }
170
+ }
171
+ }
172
+ return null;
173
+ }
174
+ return search(dir);
175
+ }
176
+ /**
177
+ * Find types that embed a given type
178
+ */
179
+ async function findEmbedders(dir, typeName, maxDepth) {
180
+ const results = [];
181
+ let depth = 0;
182
+ async function search(currentDir) {
183
+ if (depth >= maxDepth)
184
+ return;
185
+ depth++;
186
+ try {
187
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
188
+ for (const entry of entries) {
189
+ const fullPath = path.join(currentDir, entry.name);
190
+ if (entry.isDirectory()) {
191
+ if (entry.name === "vendor" ||
192
+ entry.name.startsWith(".") ||
193
+ entry.name === "testdata") {
194
+ continue;
195
+ }
196
+ await search(fullPath);
197
+ }
198
+ else if (entry.isFile() && entry.name.endsWith(".go")) {
199
+ await searchFileForEmbedders(fullPath, typeName, results);
200
+ }
201
+ }
202
+ }
203
+ catch {
204
+ // Skip directories that can't be read
205
+ }
206
+ depth--;
207
+ }
208
+ await search(dir);
209
+ return results;
210
+ }
211
+ /**
212
+ * Search a file for types that embed the target type
213
+ */
214
+ async function searchFileForEmbedders(filePath, typeName, results) {
215
+ try {
216
+ const parseResult = await parseFile(filePath);
217
+ const { tree, source } = parseResult;
218
+ for (const node of walkTree(tree.rootNode, ["type_declaration"])) {
219
+ const typeInfo = parseClass(node, source);
220
+ // Check if this type embeds the target
221
+ if (typeInfo.bases.includes(typeName)) {
222
+ results.push({
223
+ name: typeInfo.name,
224
+ path: filePath,
225
+ line: typeInfo.line,
226
+ });
227
+ }
228
+ }
229
+ }
230
+ catch {
231
+ // Skip files that can't be parsed
232
+ }
233
+ }
234
+ /**
235
+ * Factory function to create a customized getClassHierarchy tool
236
+ */
237
+ export function createGetClassHierarchyTool(options) {
238
+ const { defaultMaxDepth = 3 } = options ?? {};
239
+ return defineTool({
240
+ name: "get_class_hierarchy_go",
241
+ description: TOOL_DESCRIPTION,
242
+ inputSchema: TOOL_INPUT_SCHEMA,
243
+ execute: async (input) => {
244
+ return executeGetClassHierarchy({
245
+ ...input,
246
+ maxDepth: input.maxDepth ?? defaultMaxDepth,
247
+ });
248
+ },
249
+ });
250
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * getComplexity Tool
3
+ *
4
+ * Calculate cyclomatic and cognitive complexity for Go functions.
5
+ * Identifies complex code that may need refactoring.
6
+ */
7
+ import type { Tool } from "@compilr-dev/agents";
8
+ import type { FileComplexityResult } from "./types.js";
9
+ /**
10
+ * Input for getComplexity tool
11
+ */
12
+ export interface GetComplexityInput {
13
+ /** File or directory to analyze */
14
+ path: string;
15
+ /** Recursive analysis for directories (default: false) */
16
+ recursive?: boolean;
17
+ /** Complexity threshold for warnings (default: 10) */
18
+ threshold?: number;
19
+ /** Only return items above threshold (default: false) */
20
+ onlyAboveThreshold?: boolean;
21
+ /** Maximum files to analyze (default: 50) */
22
+ maxFiles?: number;
23
+ }
24
+ /**
25
+ * Complexity result for multiple files
26
+ */
27
+ export interface GetComplexityResult {
28
+ /** Analyzed path */
29
+ path: string;
30
+ /** Results per file */
31
+ files: FileComplexityResult[];
32
+ /** Overall stats */
33
+ stats: {
34
+ totalFiles: number;
35
+ totalFunctions: number;
36
+ complexFunctions: number;
37
+ averageComplexity: number;
38
+ maxComplexity: number;
39
+ threshold: number;
40
+ };
41
+ }
42
+ /**
43
+ * getComplexity tool - Calculate code complexity
44
+ */
45
+ export declare const getComplexityTool: Tool<GetComplexityInput>;
46
+ /**
47
+ * Factory function to create a customized getComplexity tool
48
+ */
49
+ export declare function createGetComplexityTool(options?: {
50
+ defaultThreshold?: number;
51
+ defaultMaxFiles?: number;
52
+ }): Tool<GetComplexityInput>;
53
+ //# sourceMappingURL=get-complexity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-complexity.d.ts","sourceRoot":"","sources":["../../src/tools/get-complexity.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EAAE,IAAI,EAAuB,MAAM,qBAAqB,CAAC;AAQrE,OAAO,KAAK,EACV,oBAAoB,EAGrB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,oBAAoB;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAC9B,oBAAoB;IACpB,KAAK,EAAE;QACL,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC;QACzB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,aAAa,EAAE,MAAM,CAAC;QACtB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAuCD;;GAEG;AACH,eAAO,MAAM,iBAAiB,0BAK5B,CAAC;AAuWH;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE;IAChD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAiB3B"}
@@ -0,0 +1,357 @@
1
+ /**
2
+ * getComplexity Tool
3
+ *
4
+ * Calculate cyclomatic and cognitive complexity for Go functions.
5
+ * Identifies complex code that may need refactoring.
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, parseMethod, walkTree, } from "../parser/go-parser.js";
11
+ // Tool description
12
+ const TOOL_DESCRIPTION = `Calculate cyclomatic and cognitive complexity for Go functions.
13
+ Returns complexity metrics and identifies functions that may need refactoring.
14
+ Helps find overly complex code that's hard to test and maintain.`;
15
+ // Tool input schema
16
+ const TOOL_INPUT_SCHEMA = {
17
+ type: "object",
18
+ properties: {
19
+ path: {
20
+ type: "string",
21
+ description: "Go file or directory to analyze",
22
+ },
23
+ recursive: {
24
+ type: "boolean",
25
+ description: "Recursively analyze directories (default: false)",
26
+ default: false,
27
+ },
28
+ threshold: {
29
+ type: "number",
30
+ description: "Cyclomatic complexity threshold for warnings (default: 10)",
31
+ default: 10,
32
+ },
33
+ onlyAboveThreshold: {
34
+ type: "boolean",
35
+ description: "Only return functions above threshold (default: false)",
36
+ default: false,
37
+ },
38
+ maxFiles: {
39
+ type: "number",
40
+ description: "Maximum files to analyze (default: 50)",
41
+ default: 50,
42
+ },
43
+ },
44
+ required: ["path"],
45
+ };
46
+ /**
47
+ * getComplexity tool - Calculate code complexity
48
+ */
49
+ export const getComplexityTool = defineTool({
50
+ name: "get_complexity_go",
51
+ description: TOOL_DESCRIPTION,
52
+ inputSchema: TOOL_INPUT_SCHEMA,
53
+ execute: executeGetComplexity,
54
+ });
55
+ /**
56
+ * Execute the getComplexity tool
57
+ */
58
+ async function executeGetComplexity(input) {
59
+ const { path: inputPath, recursive = false, threshold = 10, onlyAboveThreshold = false, maxFiles = 50, } = input;
60
+ if (!inputPath) {
61
+ return createErrorResult("Path is required");
62
+ }
63
+ try {
64
+ const stats = await fs.stat(inputPath);
65
+ const files = [];
66
+ if (stats.isFile()) {
67
+ if (inputPath.endsWith(".go")) {
68
+ const result = await analyzeFileComplexity(inputPath, threshold, onlyAboveThreshold);
69
+ files.push(result);
70
+ }
71
+ else {
72
+ return createErrorResult("File must be a .go file");
73
+ }
74
+ }
75
+ else if (stats.isDirectory()) {
76
+ const goFiles = await collectGoFiles(inputPath, recursive, maxFiles);
77
+ for (const filePath of goFiles) {
78
+ const result = await analyzeFileComplexity(filePath, threshold, onlyAboveThreshold);
79
+ files.push(result);
80
+ }
81
+ }
82
+ else {
83
+ return createErrorResult("Path must be a file or directory");
84
+ }
85
+ // Calculate overall stats
86
+ const allFunctions = files.flatMap((f) => f.functions);
87
+ const complexFunctions = allFunctions.filter((f) => f.isComplex);
88
+ const avgComplexity = allFunctions.length > 0
89
+ ? allFunctions.reduce((sum, f) => sum + f.metrics.cyclomatic, 0) /
90
+ allFunctions.length
91
+ : 0;
92
+ const maxComplexity = allFunctions.length > 0
93
+ ? Math.max(...allFunctions.map((f) => f.metrics.cyclomatic))
94
+ : 0;
95
+ const result = {
96
+ path: inputPath,
97
+ files,
98
+ stats: {
99
+ totalFiles: files.length,
100
+ totalFunctions: allFunctions.length,
101
+ complexFunctions: complexFunctions.length,
102
+ averageComplexity: Math.round(avgComplexity * 100) / 100,
103
+ maxComplexity,
104
+ threshold,
105
+ },
106
+ };
107
+ return createSuccessResult(result);
108
+ }
109
+ catch (error) {
110
+ const message = error instanceof Error ? error.message : String(error);
111
+ return createErrorResult(`Failed to analyze complexity: ${message}`);
112
+ }
113
+ }
114
+ /**
115
+ * Collect Go files from a directory
116
+ */
117
+ async function collectGoFiles(dir, recursive, maxFiles) {
118
+ const files = [];
119
+ async function walk(currentDir) {
120
+ if (files.length >= maxFiles)
121
+ return;
122
+ const entries = await fs.readdir(currentDir, { withFileTypes: true });
123
+ for (const entry of entries) {
124
+ if (files.length >= maxFiles)
125
+ break;
126
+ const fullPath = path.join(currentDir, entry.name);
127
+ if (entry.isDirectory()) {
128
+ if (recursive &&
129
+ !entry.name.startsWith(".") &&
130
+ entry.name !== "vendor" &&
131
+ entry.name !== "testdata") {
132
+ await walk(fullPath);
133
+ }
134
+ }
135
+ else if (entry.isFile() && entry.name.endsWith(".go")) {
136
+ files.push(fullPath);
137
+ }
138
+ }
139
+ }
140
+ await walk(dir);
141
+ return files;
142
+ }
143
+ /**
144
+ * Analyze complexity for a single file
145
+ */
146
+ async function analyzeFileComplexity(filePath, threshold, onlyAboveThreshold) {
147
+ const parseResult = await parseFile(filePath);
148
+ const { tree, source } = parseResult;
149
+ const rootNode = tree.rootNode;
150
+ const functions = [];
151
+ // Find all functions and methods
152
+ for (const node of walkTree(rootNode)) {
153
+ // Regular functions
154
+ if (node.type === "function_declaration") {
155
+ const funcInfo = parseFunction(node, source);
156
+ const metrics = calculateComplexity(node, source);
157
+ const isComplex = metrics.cyclomatic >= threshold;
158
+ if (!onlyAboveThreshold || isComplex) {
159
+ functions.push({
160
+ name: funcInfo.name,
161
+ path: filePath,
162
+ line: funcInfo.line,
163
+ metrics,
164
+ isComplex,
165
+ });
166
+ }
167
+ }
168
+ // Methods (functions with receivers)
169
+ if (node.type === "method_declaration") {
170
+ const methodInfo = parseMethod(node, source);
171
+ const metrics = calculateComplexity(node, source);
172
+ const isComplex = metrics.cyclomatic >= threshold;
173
+ if (!onlyAboveThreshold || isComplex) {
174
+ const name = methodInfo.receiverType
175
+ ? `${methodInfo.receiverType}.${methodInfo.name}`
176
+ : methodInfo.name;
177
+ functions.push({
178
+ name,
179
+ path: filePath,
180
+ line: methodInfo.line,
181
+ metrics,
182
+ isComplex,
183
+ receiverType: methodInfo.receiverType,
184
+ });
185
+ }
186
+ }
187
+ }
188
+ // Calculate file-level averages
189
+ const totalCyclomatic = functions.reduce((sum, f) => sum + f.metrics.cyclomatic, 0);
190
+ const avgComplexity = functions.length > 0 ? totalCyclomatic / functions.length : 0;
191
+ const maxComplexity = functions.length > 0
192
+ ? Math.max(...functions.map((f) => f.metrics.cyclomatic))
193
+ : 0;
194
+ return {
195
+ path: filePath,
196
+ functions,
197
+ averageComplexity: Math.round(avgComplexity * 100) / 100,
198
+ maxComplexity,
199
+ stats: {
200
+ totalFunctions: functions.length,
201
+ complexFunctions: functions.filter((f) => f.isComplex).length,
202
+ simpleAverage: avgComplexity,
203
+ },
204
+ };
205
+ }
206
+ /**
207
+ * Calculate complexity metrics for a function/method
208
+ */
209
+ function calculateComplexity(node, _source) {
210
+ let cyclomatic = 1; // Start with 1 for the function itself
211
+ let cognitive = 0;
212
+ let maxNesting = 0;
213
+ let parameters = 0;
214
+ // Count parameters
215
+ const params = node.childForFieldName("parameters");
216
+ if (params) {
217
+ for (const child of params.children) {
218
+ if (child.type === "parameter_declaration") {
219
+ // Count identifiers in parameter declaration
220
+ for (const c of child.children) {
221
+ if (c.type === "identifier") {
222
+ parameters++;
223
+ }
224
+ }
225
+ }
226
+ }
227
+ }
228
+ // Walk the function body to count complexity
229
+ function walk(n, nesting) {
230
+ maxNesting = Math.max(maxNesting, nesting);
231
+ switch (n.type) {
232
+ // Control flow - add to cyclomatic
233
+ case "if_statement":
234
+ cyclomatic++;
235
+ cognitive += 1 + nesting; // Cognitive adds nesting penalty
236
+ for (const child of n.children) {
237
+ walk(child, nesting + 1);
238
+ }
239
+ return;
240
+ case "else_clause":
241
+ // "else" doesn't add to cyclomatic but adds to cognitive
242
+ cognitive += 1;
243
+ for (const child of n.children) {
244
+ walk(child, nesting);
245
+ }
246
+ return;
247
+ case "for_statement":
248
+ case "range_clause":
249
+ cyclomatic++;
250
+ cognitive += 1 + nesting;
251
+ for (const child of n.children) {
252
+ walk(child, nesting + 1);
253
+ }
254
+ return;
255
+ case "switch_statement":
256
+ case "type_switch_statement":
257
+ cyclomatic++;
258
+ cognitive += 1 + nesting;
259
+ for (const child of n.children) {
260
+ walk(child, nesting + 1);
261
+ }
262
+ return;
263
+ case "case_clause":
264
+ case "default_case":
265
+ cyclomatic++;
266
+ for (const child of n.children) {
267
+ walk(child, nesting);
268
+ }
269
+ return;
270
+ case "select_statement":
271
+ cyclomatic++;
272
+ cognitive += 1 + nesting;
273
+ for (const child of n.children) {
274
+ walk(child, nesting + 1);
275
+ }
276
+ return;
277
+ case "communication_case":
278
+ cyclomatic++;
279
+ for (const child of n.children) {
280
+ walk(child, nesting);
281
+ }
282
+ return;
283
+ // Boolean operators
284
+ case "binary_expression": {
285
+ const operator = n.children.find((c) => c.type === "&&" || c.type === "||");
286
+ if (operator) {
287
+ cyclomatic++;
288
+ cognitive++;
289
+ }
290
+ for (const child of n.children) {
291
+ walk(child, nesting);
292
+ }
293
+ return;
294
+ }
295
+ // Go keyword (goroutine)
296
+ case "go_statement":
297
+ cognitive += 1 + nesting; // Concurrency adds cognitive load
298
+ for (const child of n.children) {
299
+ walk(child, nesting);
300
+ }
301
+ return;
302
+ // Defer
303
+ case "defer_statement":
304
+ cognitive++; // Defer adds cognitive load (execution order)
305
+ for (const child of n.children) {
306
+ walk(child, nesting);
307
+ }
308
+ return;
309
+ // Recover (error handling)
310
+ case "call_expression": {
311
+ const funcName = n.childForFieldName("function");
312
+ if (funcName?.text === "recover") {
313
+ cognitive++; // Panic/recover adds cognitive load
314
+ }
315
+ for (const child of n.children) {
316
+ walk(child, nesting);
317
+ }
318
+ return;
319
+ }
320
+ default:
321
+ for (const child of n.children) {
322
+ walk(child, nesting);
323
+ }
324
+ }
325
+ }
326
+ const body = node.childForFieldName("body");
327
+ if (body) {
328
+ walk(body, 0);
329
+ }
330
+ // Count lines of code
331
+ const linesOfCode = node.endPosition.row - node.startPosition.row + 1;
332
+ return {
333
+ cyclomatic,
334
+ cognitive,
335
+ linesOfCode,
336
+ parameters,
337
+ nestingDepth: maxNesting,
338
+ };
339
+ }
340
+ /**
341
+ * Factory function to create a customized getComplexity tool
342
+ */
343
+ export function createGetComplexityTool(options) {
344
+ const { defaultThreshold = 10, defaultMaxFiles = 50 } = options ?? {};
345
+ return defineTool({
346
+ name: "get_complexity_go",
347
+ description: TOOL_DESCRIPTION,
348
+ inputSchema: TOOL_INPUT_SCHEMA,
349
+ execute: async (input) => {
350
+ return executeGetComplexity({
351
+ ...input,
352
+ threshold: input.threshold ?? defaultThreshold,
353
+ maxFiles: input.maxFiles ?? defaultMaxFiles,
354
+ });
355
+ },
356
+ });
357
+ }