Back to CourseLesson 9 of 12

Building a Code Assistant

In this hands-on tutorial, you will build a code assistant agent that can read files, analyze code structure, generate diffs, and write tests. This agent demonstrates how to work with file system tools and structured code output.

Defining the Code Tools

A code assistant needs tools for reading files, listing directories, and analyzing code. Here are the core tools:

import { createToolWithValidation } from '@waymakerai/aicofounder-core';
import { z } from 'zod';
import { readFile, readdir, stat } from 'fs/promises';
import { join } from 'path';

const readFileTool = createToolWithValidation({
  name: 'read_file',
  description: 'Read the contents of a file. Use this to examine source code, configs, or any text file.',
  schema: z.object({
    path: z.string().min(1),
    startLine: z.number().int().min(1).optional(),
    endLine: z.number().int().min(1).optional(),
  }),
  execute: async ({ path, startLine, endLine }) => {
    const content = await readFile(path, 'utf-8');
    const lines = content.split('\n');
    const selected = startLine && endLine
      ? lines.slice(startLine - 1, endLine)
      : lines;
    return JSON.stringify({
      path,
      totalLines: lines.length,
      content: selected.join('\n'),
      range: startLine ? `${startLine}-${endLine || lines.length}` : 'full',
    });
  },
});

const listDirectoryTool = createToolWithValidation({
  name: 'list_directory',
  description: 'List files and directories in a given path. Use this to explore project structure.',
  schema: z.object({
    path: z.string().min(1),
    recursive: z.boolean().default(false),
  }),
  execute: async ({ path, recursive }) => {
    const entries = await readdir(path, { withFileTypes: true });
    const items = entries.map((e) => ({
      name: e.name,
      type: e.isDirectory() ? 'directory' : 'file',
      path: join(path, e.name),
    }));
    return JSON.stringify(items);
  },
});

Code Analysis Tool

For deeper analysis, add a tool that extracts structure from source files -- functions, classes, imports, and exports:

const analyzeCodeTool = createToolWithValidation({
  name: 'analyze_code',
  description: 'Analyze a source file to extract its structure: imports, exports, functions, and classes. Use this before making suggestions about code organization.',
  schema: z.object({
    path: z.string().min(1),
  }),
  execute: async ({ path }) => {
    const content = await readFile(path, 'utf-8');
    const lines = content.split('\n');

    const imports = lines.filter((l) => l.match(/^import /));
    const exports = lines.filter((l) => l.match(/^export /));
    const functions = lines
      .map((l, i) => ({ line: i + 1, match: l.match(/(?:function|const|let)\s+(\w+)/) }))
      .filter((l) => l.match)
      .map((l) => ({ name: l.match![1], line: l.line }));

    return JSON.stringify({
      path,
      lineCount: lines.length,
      imports: imports.length,
      exports: exports.length,
      functions,
      language: path.endsWith('.ts') ? 'typescript' : path.endsWith('.py') ? 'python' : 'unknown',
    });
  },
});

Assembling the Code Assistant

Wire the tools together with a system prompt designed for code review:

import { createAgent } from '@waymakerai/aicofounder-core';

const codeAssistant = createAgent({
  name: 'code-assistant',
  model: 'gpt-4o',
  temperature: 0.2, // Low temperature for precise code analysis
  maxSteps: 20,
  systemPrompt: `You are an expert code reviewer and assistant. You can:

1. Read and analyze source files
2. Explore project structure
3. Identify bugs, performance issues, and code smells
4. Suggest improvements with specific code changes
5. Write tests for existing code

When reviewing code:
- Start by understanding the project structure with list_directory
- Read relevant files to understand context
- Use analyze_code for structural overview
- Provide specific, actionable feedback with code examples
- Format code suggestions as diffs when possible`,
  tools: [readFileTool, listDirectoryTool, analyzeCodeTool],
  memory: { type: 'sliding-window', maxMessages: 40 },
});

Generating Diffs and Suggestions

The agent uses its understanding of the code to generate specific improvement suggestions. Here is how to prompt it for different tasks:

// Code review
const review = await codeAssistant.run(
  'Review the file at src/utils/auth.ts for security issues and suggest fixes.'
);

// Test generation
const tests = await codeAssistant.run(
  'Read src/services/userService.ts and write comprehensive unit tests for it using Jest.'
);

// Refactoring suggestions
const refactor = await codeAssistant.run(
  'Analyze the src/components directory and suggest how to reduce code duplication.'
);

Security Considerations

File system tools are powerful and potentially dangerous. Always apply safety guards:

  • Restrict file access to specific directories using a base path allowlist.
  • Make the agent read-only -- do not include write or delete tools unless you have robust review gates.
  • Sanitize paths to prevent directory traversal attacks (../../../etc/passwd).
  • Use CoFounder's built-in security guards to block PII exposure in tool results.