/** * code-scan.js — AGENT-DRIVEN, stack-smart deterministic code scanner (tier ①: * fully local — the code never leaves the box; no API, no OAuth, no browser). * * WHAT IT IS * ────────── * A hand-written single-tool skill (same shape as chartRender.js / kvMemory.js): * `serverName`, `allowedTools`, `tools[]`, `handleToolCall`, and a `resolve()` * that spawns the GENERIC bin/mcp-skill.mjs. The ONE tool — `scan_code` — is * called by the AGENT after it has a repo on disk (a clone). It AUTO-DETECTS the * stack and runs the matching deterministic linter(s), returning structured * findings the agent triages. * * WHY A SKILL, NOT A FLOOR PROBE * ────────────────────────────── * The code-review agent is GENERAL-PURPOSE (Go, Python, Rust, Java, JS/TS, …). * A language-specific linter therefore CANNOT be an always-run deterministic * floor step — that's wrong for a Go/Python repo and doesn't scale. Instead the * AGENT decides: it looks at the repo, calls scan_code, and the tool picks the * right scanner(s) by stack detection. This is exactly what a skill is for. * * NO HARDCODING — the SCANNERS registry is the single extension point * ────────────────────────────────────────────────────────────────── * Every scanner is ONE entry in the SCANNERS array: { id, detect, langs, bin, * args, parse }. Adding a tool (tsc, semgrep, eslint, clippy…) = one more entry, * no changes to scan_code itself. scan_code runs EVERY scanner whose detect(dir) * is true, scoped to files matching its `langs`, and merges the results. * * TWO scanners are WIRED + VERIFIED TODAY, both self-vendored (no image bake, no * upstream-npm binary trust): * - oxlint (JS/TS) via @zibby/bin-oxlint — a single static binary; see * resolveOxlintBin. * - semgrep (Java, Python, Go, Ruby, PHP) via @zibby/bin-semgrep — the OSS * Semgrep engine `semgrep-core` (LGPL-2.1), spawned DIRECTLY (no Python, no * `semgrep` CLI, no network, no telemetry, no registry) with a VENDORED curated * ruleset + a generated local targets file; see resolveSemgrepBin. It scans ALL * its languages in ONE invocation via a `-targets` file. JS/TS is intentionally * left to oxlint (semgrep EXCLUDES it) to avoid double-scanning. * ruff (Python) + staticcheck (Go) remain SCAFFOLD entries (registry + parser * present, clearly marked TODO) — semgrep now covers Python/Go for BREADTH; ruff/ * staticcheck can still be wired later for DEPTH. Best-effort throughout: a missing * binary (spawn ENOENT), an unreadable file, or a parser hiccup NEVER throws — the * scanner is skipped with a note and the others still run. */ /** * Build the semgrep-core `-targets` document for a set of RELATIVE file paths. This * is the internal ATD wire format semgrep-core expects (verified against the pinned * engine): a top-level variant ["Targets", [ ["CodeTarget", { path:{fpath,ppath}, * analyzer, products }], … ]]. One entry per file we can map to a language; files * of other languages are dropped. EXPORTED for unit-testing the shape. */ export declare function buildSemgrepTargets(relFiles: any): (string | any[])[]; /** * Parse `semgrep-core … -json` output into the SAME finding shape as parseOxlint * ({ file, line, severity, rule, message }). The engine prints progress DOTS * (".\n") before the JSON object, so we start at the first '{'. Shape (verified * against semgrep-core 1.169.0): { results: [ { check_id, path, start:{line,col}, * end, extra:{ message, severity? } } ], errors, paths }. Best-effort: empty / * unparseable → []. EXPORTED for direct unit-testing of the shape assumption. */ export declare function parseSemgrep(stdout: any): any; /** * Parse `oxlint --format json` output. oxlint emits ONE JSON object: * { diagnostics: [ { message, code:"eslint(no-cond-assign)", severity:"warning"|"error", * filename, help, url, labels:[ { span:{ offset, length, line, column } } ] } ], * number_of_files, … } * The line comes from the first label's span. VERIFIED against oxlint 1.73.0. * EXPORTED for direct unit-testing of the shape assumption. */ export declare function parseOxlint(stdout: any): any; /** * THE EXTENSION POINT. Each scanner is one self-contained entry: * id stable scanner id (labels its findings block) * detect (dir) => boolean — is this the repo's stack? (a marker file check) * langs file extensions this scanner handles * bin () => resolved binary path/name (env-overridable) * args (files) => string[] — argv (files are RELATIVE to the scanned dir) * parse (stdout, stderr, code) => [{ file, line, severity, rule, message }] * Adding a tool = ONE more entry here. NOTHING scanner-specific lives elsewhere. */ export declare const SCANNERS: { id: string; detect: (dir: any) => boolean; langs: string[]; bin: () => string; args: (files: any, ctx?: any) => any[]; parse: typeof parseOxlint; }[]; export declare const codeScanSkill: any;