TheFocus.AI TheFocus.AI
PROJECT

File Combiner

HANDS-ON

TUTOR WITH THEFOCUS.AI

Agent Integration

Copy this prompt into Claude, ChatGPT, or any external AI assistant. It points the assistant to the course instructions and links it to your student profile to track your progress and customize observations.

Please tutor me in this lesson using the following context. First, read the instructions at: https://courses.thefocus.ai/llms.txt My Student ID is: <none> The lesson markdown source is at: https://courses.thefocus.ai/run-ai-locally/projects/file-combiner.md

You are not enrolled yet. Enroll to generate a Student ID to track lesson completions and store learning notes.

Project: File Combiner

Source: 1-combind/
Model: gemma4:26b
Type: TypeScript (Bun runtime)

What It Does

The file combiner is the glue of our automation pipeline. It takes multiple files and wraps them in XML-like tags, producing a single output that can be fed to Ollama as context.

./1-combind/code -m "Fix this bug" prompt.txt error.txt code.py

Output:

<message>Fix this bug</message>
<prompt.txt>Write a bash script...</prompt.txt>
<error.txt>SyntaxError: line 42...</error.txt>
<code.py>#!/usr/bin/env python3...</code.py>

This is how we feed context back to the model during the self-improving loop — the original prompt, the generated code, and the error output all go in as tagged sections.

The Prompt

write a standalone typescript script that has a shebang header using bun

it takes a list of files as arguments and concatenes them together marking them

example

script [-m "message"] file1 file2 file3

will output

<message>message</message>
<file1>{{contents of file1}}</file1>
<file2>{{cotents of file2}}</file2>
<file3>{{contents of file1}}</file3>

the -m message is optional

output only code

The Code

#!/usr/bin/env bun

const args = Bun.argv.slice(2);
let message = "";
const files: string[] = [];

// Parse arguments
for (let i = 0; i < args.length; i++) {
  if (args[i] === "-m" && i + 1 < args.length) {
    message = args[i + 1];
    i++; // Skip the next argument as it's the message content
  } else {
    files.push(args[i]);
  }
}

async function run() {
  // Output message if provided
  if (message) {
    console.log(`<message>${message}</message>`);
  }

  // Process each file
  for (const filePath of files) {
    try {
      const file = Bun.file(filePath);

      if (!(await file.exists())) {
        console.error(`Error: File not found: ${filePath}`);
        continue;
      }

      const content = await file.text();
      // Using the filename as the tag name
      console.log(`<${filePath}>${content}</${filePath}>`);
    } catch (error) {
      console.error(
        `Error processing ${filePath}:`,
        error instanceof Error ? error.message : error,
      );
    }
  }
}

run();

How to Use

chmod +x 1-combind/code

# Combine a prompt and some files
./1-combind/code prompt.txt file1.py file2.md

# With a message
./1-combind/code -m "Here's the error" prompt.txt code.py error.txt

# Pipe directly to Ollama
./1-combind/code -m "Fix this" prompt.md code.py | ollama run gemma4:26b

The Test

The test file demonstrates the -m flag:

#!/bin/bash
./1-combind/code -m "This is a message" 0-weather/prompt.md

Expected output:

<message>This is a message</message>
<0-weather/prompt.md>write a bash script that uses wget to get the weather in new york city</0-weather/prompt.md>

Key Lessons

  • Bun for TypeScript. The model chose Bun runtime for its simple file I/O (Bun.file(path).text()).
  • Tagging provides structure. The XML-like tags (<filename>content</filename>) give the model clear boundaries between different pieces of context.
  • The -m flag is the channel. It provides human intent (“Fix this bug”) alongside raw file contents.
  • This is the feedback pipe. In our self-improving loop, this tool packs prompt + code + error into one message for the model.

Next: Data Extractor →