Pre-release Harn is pre-1.0 — the language, standard library, and CLI may change between releases. See the release notes

Tutorial: build a code review agent

This tutorial shows a small but realistic review pipeline. The goal is not to rebuild a full IDE integration. Instead, we want a deterministic Harn program that can review a patch, inspect context, and return a concise report.

Use the companion example as a starting point:

harn run examples/code-reviewer.harn -- "$(git diff)"

1. Start with a tight review prompt#

The simplest useful reviewer is just an LLM call with a strong system prompt. Keep the instructions short, specific, and opinionated:

pipeline default(harness: Harness, task) {
  const system = """
You are a senior code reviewer.
Review the patch for correctness, security, maintainability, and tests.
Return:
- must-fix issues
- suggestions
- missing tests
End with a short verdict.
"""

  const review = harness.llm.call(task, system, {
    temperature: 0.2,
    max_tokens: 1200,
  })

  harness.stdio.log(review.text)
}

This is enough when the user pastes a diff directly into task.

The task parameter is supplied by whatever drives the pipeline — an editor host over ACP, harn serve, or a caller that runs the pipeline for you. harn run does not bind it, so a pipeline you want to run straight from the CLI reads its input from argv instead, the way the companion example above does. Pick the shape that matches how the reviewer will be invoked.

2. Add file context when you need it#

Real review agents usually need a bit of surrounding code. The simplest route is to read a small, explicit list of files and combine them with the patch. Keep the list short so the prompt stays focused.

pipeline default(harness: Harness, task) {
  const files = ["src/main.rs", "src/lib.rs"]
  let context = ""

  for file in files {
    context = context + "\n\n=== " + file + " ===\n"
      + harness.fs.read_text(file)
  }

  const review = harness.llm.call(
    "Patch:\n" + task + "\n\nContext:\n" + context,
    """
You are a strict code reviewer.
Flag correctness bugs first, then test gaps, then maintainability issues.
Do not invent missing context. If the context is insufficient, say so.
""",
    {temperature: 0.2, max_tokens: 1400}
  )

  harness.stdio.log(review.text)
}

If you want to review a directory tree instead, use harness.fs.list_dir() and parallel each to gather files concurrently, then trim the result to the most relevant ones before calling the model.

3. Make the review measurable#

Good review agents should record something observable, even if it is only a small heuristic. Use eval_metric() to track whether the agent found issues and how often it asked for more context.

pipeline default(harness: Harness, task) {
  const review = harness.llm.call(
    task,
    "You are a code reviewer. Return a concise bullet list.",
    {temperature: 0.2}
  )

  const has_issue = review.text.contains("issue")
    || review.text.contains("bug")
  eval_metric("review_has_issue", has_issue)
  eval_metric("review_chars", review.text.count)

  harness.stdio.log(review.text)
}

Recorded metrics are printed by the run and, for a workflow run, saved into the run record under .harn-runs/ that harn eval reads. See the eval pipeline tutorial for that path.

4. When to stop#

Use the agent loop when the review needs to gather context, but stop once the review itself is stable. For code review, that usually means:

  • inspect a small, explicit file set
  • keep the system prompt short
  • request concrete fixes, not a long essay
  • record metrics so you can compare review quality over time

If you need a richer workflow, combine this with the eval tutorial and the debugging tools.