/** * Project-agnostic tool resolver for evidence-based verification (ADR-051). * * `cleo verify --evidence "tool:"` historically hardcoded a pnpm/biome/tsc * table inside `evidence.ts`, violating the package-boundary contract that * `@cleocode/core` MUST be agnostic to any specific project type. This module * replaces the hardcoded `TOOL_COMMANDS` table with a resolver that: * * 1. Maps a logical (canonical) tool name to a runnable command. * 2. Sources the command from `.cleo/project-context.json` when the user * has captured a project-specific override (`testing.command`, * `build.command`, …). * 3. Falls back to per-`primaryType` defaults (node, python, rust, go, …) * when project-context.json is missing or does not specify the tool. * 4. Honours legacy aliases (`pnpm-test`, `tsc`, `biome`, …) for backwards * compatibility with already-stored evidence atoms. * * The resolved command always includes its `source` so audit and cache layers * can disambiguate "user-supplied" from "language-default" invocations. * * @task T1534 * @adr ADR-051 §3 * @adr ADR-061 */ import type { ProjectType } from '../store/project-detect.js'; /** * Canonical (project-agnostic) tool names accepted by `cleo verify * --evidence "tool:"`. * * Each canonical name maps to a project-specific command via * {@link resolveToolCommand}. Adding a new canonical tool requires: * * 1. Adding the name here. * 2. Adding a default for each {@link ProjectType} in `LANGUAGE_DEFAULTS`. * 3. Updating {@link checkGateEvidenceMinimum} if the new tool can satisfy * a verification gate. */ export declare const CANONICAL_TOOLS: readonly ["test", "build", "lint", "typecheck", "audit", "security-scan", "nexus-impact-full"]; /** * Type of a canonical (project-agnostic) tool name. * * @task T1534 */ export type CanonicalTool = (typeof CANONICAL_TOOLS)[number]; /** * Where a resolved command originated. Surfaced in cache keys and audit * trails so reviewers can distinguish project-supplied commands from CLEO * defaults. */ export type ResolutionSource = 'project-context' | 'language-default' | 'legacy-alias'; /** * A resolved tool command ready for spawning. `cmd` and `args` are * shell-escaping-free — callers MUST pass them to `child_process.spawn` * (NOT a shell) to avoid injection. * * @task T1534 */ export interface ResolvedToolCommand { /** Canonical tool name (post-alias resolution). */ canonical: CanonicalTool; /** Human-friendly tool name for stdout / audit (often equal to `canonical`). */ displayName: string; /** Executable to spawn. */ cmd: string; /** Arguments. */ args: string[]; /** Origin of this command — used by cache keys. */ source: ResolutionSource; /** When `source === 'language-default'`, the `primaryType` that was matched. */ primaryType?: ProjectType; } /** * Result envelope returned by {@link resolveToolCommand}. * * @task T1534 */ export type ResolveToolResult = { ok: true; command: ResolvedToolCommand; } | { ok: false; reason: string; codeName: 'E_TOOL_UNKNOWN' | 'E_TOOL_UNAVAILABLE'; }; /** * Set of all valid `tool:` payloads — canonical names plus legacy * aliases. Returned by {@link listValidToolNames} for help / validation * surfaces. * * @task T1534 */ export declare function listValidToolNames(): string[]; interface ResolveOptions { /** * Override for `primaryType` lookup — set in tests where no real * `project-context.json` is present. */ primaryTypeOverride?: ProjectType; } /** * Resolve a `tool:` evidence atom to a runnable command. * * Resolution order: * * 1. Map alias → canonical (e.g. `pnpm-test` → `test`). * 2. Check `.cleo/project-context.json`: * - `test` → `testing.command` * - `build` → `build.command` * 3. Read `primaryType` from `project-context.json` (or detect from cwd). * 4. Look up the canonical name in `LANGUAGE_DEFAULTS[primaryType]`. * 5. Verify the resolved binary exists on `PATH` (best-effort, non-fatal — * missing binaries are reported but do not block resolution; the * validator will surface the spawn error if the binary is truly absent). * * @param toolName - The user-supplied tool name (canonical or alias). * @param projectRoot - Absolute path to project root. * @param opts - Options for testing. * @returns Resolved command or a structured error. * * @example * ```ts * const r = resolveToolCommand('pnpm-test', '/repo'); * if (r.ok) { * // r.command.canonical === 'test' * // r.command.cmd === 'pnpm', r.command.args === ['run', 'test'] * // r.command.source === 'project-context' * } * ``` * * @task T1534 */ export declare function resolveToolCommand(toolName: string, projectRoot: string, opts?: ResolveOptions): ResolveToolResult; /** * Load project-context.json *without* relying on `loadProjectContext`'s * imports (escape-hatch for tests that need to inspect raw context). * * @internal */ export declare function readRawProjectContext(projectRoot: string): Record | null; export {}; //# sourceMappingURL=tool-resolver.d.ts.map