Code Review Workflow

medium15 minbasic-workflow.ts

A review should always run the same steps — static checks, an LLM review, fixes only when there are enough issues, then a report.

What it shows

  • Wrap a tool, agents and a function as nodes with toolNode, agentNode and functionNode
  • Branch with addConditional() and join both branches
  • Run it with WorkflowExecutor and per-node callbacks

The code

basic-workflow.ts
import { Agent, Cogitator, tool } from '@cogitator-ai/core';
import { WorkflowBuilder, WorkflowExecutor, agentNode, functionNode, toolNode } from '@cogitator-ai/workflows';
import { z } from 'zod';

const apiKey = process.env.GOOGLE_API_KEY;
if (!apiKey) throw new Error('Set GOOGLE_API_KEY');

interface ReviewState {
  [key: string]: unknown;
  code: string;
  issues: string[];
  analysis: string;
  fixes: string;
  report: string;
}

const analyzeCode = tool({
  name: 'analyze_code',
  description: 'Find common issues in source code',
  parameters: z.object({ code: z.string() }),
  execute: async ({ code }) => {
    const issues: string[] = [];
    if (/for.*\n.*for/.test(code)) issues.push('Nested loops');
    if (/TODO|FIXME/.test(code)) issues.push('Unresolved TODO');
    if (/console\.log/.test(code)) issues.push('Console logging in production code');
    return { issues };
  },
});

const cog = new Cogitator({ llm: { providers: { google: { apiKey } } } });
const model = 'google/gemini-3.5-flash-lite';

const reviewer = new Agent({
  name: 'code-reviewer',
  model,
  instructions: 'You are a code reviewer. Explain why the found issues matter. Be concise.',
  temperature: 0.3,
});

const fixAdvisor = new Agent({
  name: 'fix-advisor',
  model,
  instructions: 'Suggest a concrete fix with a short code snippet for each issue.',
  temperature: 0.3,
});

const analyze = toolNode<ReviewState, { code: string }>(analyzeCode, {
  argsMapper: (state) => ({ code: state.code }),
  stateMapper: (result) => ({ issues: (result as { issues: string[] }).issues }),
});

const review = agentNode<ReviewState>(reviewer, {
  inputMapper: (state) => `Issues: ${state.issues.join(', ')}\n\nCode:\n${state.code}`,
  stateMapper: (result) => ({ analysis: result.output }),
});

const advise = agentNode<ReviewState>(fixAdvisor, {
  inputMapper: (state) => `${state.analysis}\n\nCode:\n${state.code}`,
  stateMapper: (result) => ({ fixes: result.output }),
});

const report = functionNode<ReviewState, string>(
  'report',
  async (state) => `Issues: ${state.issues.length}\n\n${state.analysis}\n\nFixes:\n${state.fixes}`,
  { stateMapper: (output) => ({ report: output as string }) }
);

const workflow = new WorkflowBuilder<ReviewState>('code-review')
  .initialState({ code: '', issues: [], analysis: '', fixes: '', report: '' })
  .addNode(analyze.name, analyze.fn)
  .addNode(review.name, review.fn, { after: [analyze.name] })
  .addConditional('severity', (state) => (state.issues.length > 1 ? advise.name : 'minor'), {
    after: [review.name],
  })
  .addNode(advise.name, advise.fn, { after: ['severity'] })
  .addNode('minor', async () => ({ state: { fixes: 'No significant fixes needed.' } }), {
    after: ['severity'],
  })
  .addNode(report.name, report.fn, { after: [advise.name, 'minor'] })
  .entryPoint(analyze.name)
  .build();

const code = `function process(orders) {
  for (const order of orders) {
    // TODO: validate
    for (const item of order.items) console.log(item);
  }
}`;

const result = await new WorkflowExecutor(cog).execute(workflow, { code }, {
  onNodeComplete: (node, _output, duration) => console.log(`done: ${node} (${duration}ms)`),
});

console.log(result.state.report);

await cog.close();

Run it

install · bash
pnpm add @cogitator-ai/core @cogitator-ai/workflows zod
EnvironmentGOOGLE_API_KEY
run · bash
GOOGLE_API_KEY=your-key npx tsx basic-workflow.ts

Or run the full example from a clone of the repository (keys in .env):

repository · bash
npx tsx examples/workflows/01-basic-workflow.ts

Learn more