Christian Robinson

Software engineer, dad, making things for the web. Based in England.


Notes

Building an AI Programming Assistant in Ruby

I recently read this fantastic blog post which inspired me to build my very own AI Programming Assistant. The first thing I thought when reading the blog post was how simple it actually is to build an agent which underpins all this.

As someone who uses Zed, I use their Agent feature heavily in my day to day work which often feels like magic. Being able to offload much of the ‘grunt’ work to the agent is a huge time-saver and makes building software more enjoyable. I was pleasantly surprised to realise this isn’t simply magic!

In the blog post it provides an example written in Python. I thought it’d be interesting to write something similar in Ruby. For my agent I decided I would use RubyLLM but this could easily have been built without it.

One of the benefits of this library is it makes switching between different models easy. Rather than having to figure out the intricacies of each model provider’s API, everything is nicely abstracted with a simple RubyLLM.chat call. Making it ideal for experimenting with different models.

Loop + Tools = Agent

require 'ruby_llm'
require 'dotenv/load'

class EditFile < RubyLLM::Tool
  description "Edit the contents of a file. Before editing, use the read file tool."
  param :path, desc: "The path to the file"
  param :new_content, desc: "The new content to write to the file"

  def execute(path:, new_content:)
    begin
      File.write(path, new_content)
      { success: true, content: File.read(path) }
    rescue => e
      { error: e.message }
    end
  end
end

class FindPath < RubyLLM::Tool
  description "Glob search for paths by filename (supports `**/*.rb`, `src/**`, etc.).
    Best when you know part of a path but not its exact location."
  param :glob, desc: "The glob pattern to search for"

  def execute(glob:)
    begin
      matches = Dir.glob(glob).map { |path| File.expand_path(path) }
      { success: true, matches: matches }
    rescue => e
      { error: e.message }
    end
  end
end

class ReadFile < RubyLLM::Tool
  description "Read the contents of a file."
  param :path, desc: "The path to the file"

  def execute(path:)
    begin
      { success: true, content: File.read(path) }
    rescue => e
      { error: e.message }
    end
  end
end

RubyLLM.configure do |config|
  config.openai_api_key = ENV['OPENAI_API_KEY']
end

TOOLSET = [
  FindPath,
  ReadFile,
  EditFile
]

chat = RubyLLM.chat(model: "gpt-4.1")
chat.with_tools(*TOOLSET)

while true
  puts "You:"
  command = gets.chomp
  response = chat.ask(command)
  puts "Agent: #{response.content}"
end

A Working Agent

In my example I wrote three tools. FindPath, ReadFile and EditFile. The main ‘agent’ is simply a while loop. Inside this I collect user input which is passed to the LLM and from there it provides a response.

The model has context of the available tools, so it can decide to use them if it thinks it’s necessary. Executing a tool call is again nicely handled by RubyLLM.

So there you have it. By combining three simple tools, a loop and an LLM - I can instruct an agent from my terminal to make changes directly to my code. Pretty cool if you ask me!

You can find the gist for this example here.

Simple Rails Blogging Stack

I’ve been wanting to write more on this blog but it’s always been a hassle to get anything published. Mainly because this site is a rails app and by default it’s not great for managing and publishing content.

It’s much easier and quicker to use something like HEY or Pagecord. Writing, editing and publishing is frictionless! Exactly what you need when trying to create a new habit.

Despite this, it’s fairly trivial to close the gap between your rails app and the aforementioned blogging tools. Plus, there’s something appealing about having no limits on the customizations you can undertake.

Want to use your favourite text editor theme to render code blocks? Write using markdown? File based content managment? Use a web based editor?

You can get all this easily in rails with some well chosen libraries and gems and IMO is worth the extra effort. Give it a go!

The Stack

sitepress

File based content manager. Think frontmatter, human readable slugs, layouts (create a blog post layout and render markdown inside 😍) and loads more great features which make managing content easy. If you used Jekyll you’ll feel right at home. For a good overview of Sitepress, watch this video.

markdown-rails

Works hand in hand with Sitepress. Let’s you create *.html.md files so you can write in markdown and it’ll all render out of the box without any custom config, but it’s worth extending ApplicationMarkdown to customize this.

PrismJS

JavaScript library to render beautiful code blocks.

prism-themes

A collection of CSS files to use with PrismJS.a

RSS

As usual, rails makes it easy to add an RSS feed to your app. No gem required. Just a simple route, controller and *.rss.builder file.