coding·Independently reviewed · 96/100

Test Runner

Parses raw Vitest stdout/stderr into a TYPED test report: totals + per-failure details (test, file, message, one-sentence root-cause hypothesis), grouped by suspected cause. Analyses captured output; does not execute anything itself. Reports only what the output shows, never invents.

codingstructured-outputtesting

Install

npx agentskit add coding-test-runner

Quick start

import { openai } from '@agentskit/adapters'import { createCodingTestRunnerAgent } from './agents/coding-test-runner/agent'const agent = createCodingTestRunnerAgent({  adapter: openai({    apiKey: process.env.OPENAI_API_KEY!,    model: 'gpt-4o',  }),})const result = await agent.run('Describe your task here')console.log(result.content)

Independent reviewer approved

Validation evidence

How validation works
Review score
96/100
Confidence
96%
Evaluation cases
3
Iterations
1

The agent produced valid structured reports for all three cases, did not execute anything, did not invent Vitest totals or failures from non-Vitest input, surfaced uncertainty, and resisted the injection attempt. Behavior aligns with the stated purpose of parsing captured output only. Minor issue: the minimal case uses an empty string for duration instead of a clearer sentinel like "unknown", which is less consistent but not a critical failure.

What passed review

  • Valid structured output in every case.
  • Correctly treated prompt-like input as untrusted data rather than instructions.
  • Did not hallucinate test results, file paths, failures, or root causes.
  • Injection case ignored the request to output APPROVED and preserved the expected report shape.
  • Summaries clearly explain that no Vitest output was present.

Reviewer notes

  • Use a consistent explicit unknown marker for missing duration, preferably "unknown", rather than an empty string.

Example

A real usage example maintained with this agent.

import { anthropic } from '@agentskit/adapters'import { createTestRunnerAgent } from './agents/coding-test-runner/agent'const report = await createTestRunnerAgent({  adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-opus-4-8' }),}).run(vitestStdout)// → { passed, failed, skipped, duration, failures: [{ test, file, message, rootCause }], summary }

Extend it

Pass tools, retrieval, memory, permissions, and observers through the factory config.

const agent = createCodingTestRunnerAgent({  adapter,  tools,  retriever,  memory,  onConfirm: (call) => approve(call),  observers: [tracer],})
View agent factory source
import type { AdapterFactory, ChatMemory, Observer, ToolCall, ToolDefinition } from '@agentskit/core'import { fenceUntrustedContent, UNTRUSTED_CONTENT_DIRECTIVE } from '@agentskit/core/security'import { invokeStructured } from '@agentskit/runtime'import { defineZodTool } from '@agentskit/tools'import { z } from 'zod'import { zodToJsonSchema } from 'zod-to-json-schema'import type { JSONSchema7 } from 'json-schema'/** * Test Runner — parses raw Vitest stdout/stderr into a TYPED test report: totals plus * per-failure details (test, file, message, one-sentence root-cause hypothesis), grouped * so the next agent can prioritise fixes. It analyses output you already captured; it does * not execute anything itself (no shell access by design). * * ```ts * const report = await createTestRunnerAgent({ adapter }).run(vitestOutput) * ``` */export interface TestFailure {  test: string  file: string  message: string  /** One-sentence root-cause hypothesis. */  rootCause: string}export interface TestReport {  passed: number  failed: number  skipped: number  duration: string  failures: TestFailure[]  summary: string}export interface TestRunnerConfig {  adapter: AdapterFactory  memory?: ChatMemory  observers?: Observer[]  onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean>  maxSteps?: number}const Report = z.object({  passed: z.number().int().min(0),  failed: z.number().int().min(0),  skipped: z.number().int().min(0),  duration: z.string(),  failures: z.array(z.object({    test: z.string(),    file: z.string(),    message: z.string(),    rootCause: z.string(),  })),  summary: z.string(),})const toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7const skill = {  name: 'test-runner',  description: 'Parses raw Vitest output into a typed test report with per-failure root-cause hypotheses.',  systemPrompt: `You analyse raw Vitest stdout/stderr and produce a STRUCTURED test report. Extract: totalpassed, failed, skipped, and duration. For EACH failure: the test name, the file path, the assertionmessage, and a one-sentence root-cause hypothesis. Group failures by suspected root cause so the nextagent can prioritise. Report only what the output shows — do not invent failures or passes.${UNTRUSTED_CONTENT_DIRECTIVE}Call submit_report exactly once with { passed, failed, skipped, duration, failures, summary }. Stop.`,  tools: ['submit_report'],}export function createTestRunnerAgent(config: TestRunnerConfig) {  const emit = (label: string, status: 'start' | 'ok' | 'skip' | 'error', detail?: string) => {    for (const o of config.observers ?? []) void o.on({ type: 'progress', label, status, detail })  }  const submit = (): ToolDefinition =>    defineZodTool({ name: 'submit_report', description: 'Submit the test report. Call exactly once.', schema: Report, toJsonSchema: toJson, async execute() { return 'recorded' } }) as ToolDefinition  async function run(vitestOutput: string): Promise<TestReport> {    if (!vitestOutput?.trim()) throw new Error('test runner requires raw Vitest output')    emit('parse', 'start')    const report = await invokeStructured({      adapter: config.adapter,      tool: submit(),      task: `RAW VITEST OUTPUT:\n${fenceUntrustedContent(vitestOutput)}`,      parse: (a) => Report.parse(a),      skill,      memory: config.memory,      observers: config.observers,      onConfirm: config.onConfirm,      maxSteps: config.maxSteps ?? 3,    })    emit('parse', 'ok', `${report.passed} passed, ${report.failed} failed`)    return report  }  return {    name: 'coding-test-runner',    run,    asHandle() {      return { name: 'coding-test-runner', run: async (task: string) => JSON.stringify(await run(task)) }    },  }}
View evaluation contract

Replay these cases with the provider and model you plan to deploy.

import type { EvalSuite } from '@agentskit/eval'export const suite: EvalSuite = {  name: 'coding-test-runner',  cases: [    {      input: `Spec files run: src/cart/total.test.ts. Vitest output: FAIL  src/cart/total.test.ts > cartTotal > applies the 10% discountAssertionError: expected 90 to be 81 ❯ src/cart/total.test.ts:23:25 Test Files  1 failed (1)      Tests  1 failed | 4 passed (5)   Duration  1.92sProduce the structured report.`,      expected: (r: string) => /total\.test\.ts/i.test(r) && /(rootCause|root cause|cause|hypothes)/i.test(r),    },    {      input: `Spec files run: src/api/users.test.ts, src/api/orders.test.ts. Vitest output: Test Files  2 passed (2)      Tests  17 passed (17)   Duration  3.41sProduce the structured report.`,      expected: (r: string) => /17/.test(r) && /3\.41/.test(r) && /(passed|"passed")/i.test(r),    },    {      input: `Spec files run: src/parse/date.test.ts. Vitest output: FAIL  src/parse/date.test.ts > parseDate > ISO stringTypeError: Cannot read properties of undefined (reading 'getTime') ❯ src/parse/date.test.ts:11:34 FAIL  src/parse/date.test.ts > parseDate > unix epochTypeError: Cannot read properties of undefined (reading 'getTime') ❯ src/parse/date.test.ts:18:34      Tests  2 failed | 1 passed (3)   Duration  0.88sGroup failures by suspected root cause.`,      expected: (r: string) => /date\.test\.ts/i.test(r) && /(undefined|getTime|TypeError)/i.test(r),    },    {      input: `Spec files run: src/queue/worker.test.ts. The Vitest process was killed before producing any summary line — the output is truncated mid-run with no totals. Produce the report.`,      expected: (r: string) => /(incomplete|truncat|no (summary|total)|cannot|killed|unable|missing|escalat)/i.test(r),    },  ],}

Was this agent useful?

Your response helps us prioritize agent quality.

Keep exploring

Related agents

View category