(args: z.infer<typeof DiagnosticsArgs>, ctx: ToolContext)
| 35 | argsSchema = DiagnosticsArgs; |
| 36 | |
| 37 | async execute(args: z.infer<typeof DiagnosticsArgs>, ctx: ToolContext): Promise<ToolResult> { |
| 38 | const root = args.path ? path.resolve(ctx.cwd, args.path) : ctx.cwd; |
| 39 | const maxResults = args.max_results ?? 50; |
| 40 | const timeoutMs = args.timeout_ms ?? 120_000; |
| 41 | const want = args.checker ?? 'auto'; |
| 42 | |
| 43 | const files = await detectProjectFiles(root); |
| 44 | |
| 45 | let spec: CheckerSpec | undefined; |
| 46 | if (want !== 'auto') { |
| 47 | spec = CHECKERS.find(c => c.id === want); |
| 48 | } else { |
| 49 | spec = pickChecker(files); |
| 50 | } |
| 51 | |
| 52 | if (!spec) { |
| 53 | return { |
| 54 | content: `[DIAGNOSTICS_NO_CHECKER] No supported checker detected in ${root}. ` + |
| 55 | `Looked for: tsconfig.json (tsc), pyproject.toml/requirements.txt (ruff/pyright), ` + |
| 56 | `go.mod (go vet), Cargo.toml (cargo), eslint config (eslint). ` + |
| 57 | `Pass checker="tsc" (or another) explicitly if the project uses one without a standard config file.`, |
| 58 | isError: true, |
| 59 | }; |
| 60 | } |
| 61 | |
| 62 | const res = await runChecker(spec.argv, root, timeoutMs, ctx.signal); |
| 63 | |
| 64 | if (res.spawnError || res.code === 127) { |
| 65 | const bin = spec.argv[0] === 'npx' ? spec.argv[2] : spec.argv[0]; |
| 66 | return { |
| 67 | content: `[DIAGNOSTICS_TOOL_MISSING] Could not run '${spec.id}': ${res.spawnError ?? 'command not found'}. ` + |
| 68 | `Install it (e.g. \`${bin}\`) or choose a different checker. ` + |
| 69 | `tsc/eslint come from your project's devDependencies (\`npm i\`); ruff/pyright via pip; go/cargo with their toolchains.`, |
| 70 | isError: true, |
| 71 | }; |
| 72 | } |
| 73 | |
| 74 | const text = checkerText(spec, res); |
| 75 | |
| 76 | let diags: Diagnostic[]; |
| 77 | try { |
| 78 | diags = spec.parse(text); |
| 79 | } catch (e: any) { |
| 80 | const raw = (text || res.stderr || res.stdout || '').slice(0, 4000); |
| 81 | return { |
| 82 | content: `[DIAGNOSTICS_PARSE_NOTE] Ran ${spec.id} (exit ${res.code}) but couldn't parse structured output: ${e?.message}. Raw output:\n\n${raw}`, |
| 83 | isError: res.code !== 0, |
| 84 | }; |
| 85 | } |
| 86 | |
| 87 | return { |
| 88 | content: formatDiagnostics(diags, { checker: spec.id, maxResults }), |
| 89 | isError: false, |
| 90 | metadata: { checker: spec.id, total: diags.length, errors: diags.filter(d => d.severity === 'error').length, exitCode: res.code }, |
| 91 | }; |
| 92 | } |
| 93 | } |
nothing calls this directly
no test coverage detected