(args: z.infer<typeof SuggestImprovementsArgs>, ctx: ToolContext)
| 174 | argsSchema = SuggestImprovementsArgs; |
| 175 | |
| 176 | async execute(args: z.infer<typeof SuggestImprovementsArgs>, ctx: ToolContext): Promise<ToolResult> { |
| 177 | const root = args.path ? path.join(ctx.cwd, args.path) : ctx.cwd; |
| 178 | const maxFiles = args.max_files ?? 3000; |
| 179 | const largeFile = args.large_file_threshold ?? 500; |
| 180 | const longFn = args.long_function_threshold ?? 50; |
| 181 | |
| 182 | const files = await walkSource(root, maxFiles); |
| 183 | const suggestions: Suggestion[] = []; |
| 184 | let todoCount = 0; |
| 185 | |
| 186 | for (const f of files) { |
| 187 | // 1. Large files |
| 188 | if (f.lines >= largeFile) { |
| 189 | suggestions.push({ |
| 190 | severity: f.lines >= largeFile * 2 ? 'high' : 'medium', |
| 191 | kind: 'large_file', |
| 192 | file: f.rel, |
| 193 | detail: `${f.lines} lines — consider splitting into focused modules.`, |
| 194 | }); |
| 195 | } |
| 196 | |
| 197 | // 2. Long functions (heuristic: function declaration + naive matching brace count) |
| 198 | const lines = f.content.split('\n'); |
| 199 | const fnStart = /^(\s*)(?:export\s+)?(?:async\s+)?(?:function\s+\w+|const\s+\w+\s*=\s*(?:async\s*)?\(|\w+\s*\([^)]*\)\s*\{|def\s+\w+|class\s+\w+)/; |
| 200 | for (let i = 0; i < lines.length; i++) { |
| 201 | const line = lines[i]!; |
| 202 | const m = fnStart.exec(line); |
| 203 | if (m && line.includes('{')) { |
| 204 | const baseIndent = m[1]!.length; |
| 205 | // find matching closing brace via indent + 0-depth heuristic |
| 206 | let depth = 0; |
| 207 | for (const ch of line) { if (ch === '{') depth++; if (ch === '}') depth--; } |
| 208 | let end = i; |
| 209 | for (let j = i + 1; j < lines.length && j < i + 400; j++) { |
| 210 | for (const ch of lines[j]!) { if (ch === '{') depth++; if (ch === '}') depth--; } |
| 211 | if (depth <= 0) { end = j; break; } |
| 212 | } |
| 213 | const fnLen = end - i + 1; |
| 214 | if (fnLen >= longFn) { |
| 215 | const namePart = (line.match(/\b(function|const|class|def)\s+(\w+)/) || [, , line.slice(0, 80)])[2]; |
| 216 | suggestions.push({ |
| 217 | severity: fnLen >= longFn * 2 ? 'high' : 'medium', |
| 218 | kind: 'long_function', |
| 219 | file: f.rel, |
| 220 | line: i + 1, |
| 221 | detail: `${namePart} — ${fnLen} lines — consider extracting helpers.`, |
| 222 | }); |
| 223 | // jump past it to avoid double-counting nested |
| 224 | i = end; |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | |
| 229 | // 3. Deep nesting (heuristic: any line with 6+ levels of indent) |
| 230 | for (let i = 0; i < lines.length; i++) { |
| 231 | const line = lines[i]!; |
| 232 | if (line.trim() === '') continue; |
| 233 | const leading = line.match(/^( +|\t+)/); |
nothing calls this directly
no test coverage detected