What a year eh?
For research purposes, over the last couple of months I’ve started using LLMs heavily for throwaway code. My main goal was to train myself to write good prompts and find what works (and what doesn’t) with different agents—since that’s going to be my job from now on, after all.
My setup was very similar for all of these projects: all were made using
cagent, either with a hand-crafted agent
or by simply running cagent run --model ....
Most of the agents only had access to the filesystem and the shell. I’ve found that newer LLMs don’t really need much else. In the GitHub clone, once I had the issues feature and the CLI working, I would ask the agent to plan its steps, create issues for everything using the CLI, and then implement the feature I asked for. There were zero MCPs involved. MCP as a spec is great, but the implementations out there are almost all pretty bad.
I’ve mostly used 3 models:
- Opus for pretty much everything. This one is a BEAST and can stay focused for a long, long time.
- GPT 5.1 and 5.2. Sometimes Opus will get confused about a fix that is needed and struggle to find the issue. In these cases I would spin up GPT, and most of the time it would take a looong time to analyze but was pretty much always able to find the issue and fix it with a minimal change.
- Gemini 3 for frontend things. It’s very good at frontend work.
These models are great for everyday work and can do a lot of things very well.
However, they all have issues. Opus, for example, would sometimes end up very
confused about the codebase, miss things, and end up in a “But wait!” loop when
the feature is big or the bug is nasty. GPT is just very slow and almost always
ends up in a strange state where it tells me to Write "do it now" if you want me to do this—very strange. Gemini can be dumb easily and struggles to follow
instructions.
When I started doing this for fun I did not expect much, but I was proven wrong quickly: the newest models are genuinely useful tools for today’s developers.
Below I’m showing some of the projects I did lately. These include a Linux-like kernel, a GitHub clone, a markdown editor, a GameBoy Color emulator, a container runtime, etc. I got way better results if I knew what I was talking about; I was able to give the agent keywords and provide semi-detailed (I’m lazy) instructions on how I wanted it implemented. In cases where I had no idea what I was talking about (GameBoy emulator), we would struggle for a bit because all I could say was pretty much “It doesn’t work”, but we did manage to make something that works in the end, it was a struggle though.
Without further ado and in no specific order, here is the list of some of the vibe-coded projects.
ARM64 kernel
This was a wild and fun one. With the agent, I managed to create a working kernel that was able to boot and run a busybox shell.
Features include:
- IRQs
- VMM
- VFS, with working tarfs and FAT32 filesystems
- An initrd
- Character devices
- Block devices
- ELF loader
- A very simple (round-robin) scheduler
Once all of that was implemented, I managed to cross-compile a small subset of busybox and run it as the init process of the system.
I plan to slow down LLM usage on this one and continue playing with it manually;
I’ve already started to implement a very simple proc filesystem. Switching
from LLM-based to finger-based coding is quite interesting. Things that I know
the LLM could make in a matter of minutes can take me hours, but it’s all cool
to have a working starting playground.
1; make run-disk
2
3Starting QEMU with VirtIO disks... (Press Ctrl+A then X to exit)
4vda = tarfs disk (2MB)
5vdb = FAT32 disk (4MB)
6=================================================================
7
8---
9
10| |/ / _ \| \ | | |
11| ' /| |_) | \| | |
12| . \| \_ <| |\ | |**_
13|_|\_\_| \_\_| \_|\_\_\_**|
14
15# Minimal ARM64 Kernel v0.8
16
17[BOOT] Kernel loaded successfully
18[BOOT] DTB not passed in x0, scanning memory...
19[FDT] Scanning for DTB in memory...
20[FDT] No DTB found in memory scan
21[BOOT] DTB pointer: (nil)
22[IRQ] GIC initialized
23[IRQ] Exception vectors installed at 0xffff000040081000
24[TIMER] Counter frequency: 62500000 Hz
25[TIMER] Tick interval: 625000 counts (100 Hz)
26[IRQ] Enabled IRQ 30 with priority 128
27[TIMER] Timer started, 100 ticks/second
28[PMM] Initializing physical memory manager
29[PMM] Stack top at: VA 0xffff000040133000 (PA 0x40133000)
30[PMM] Managed region: PA 0x40133000 - 0x48080000 (127 MB)
31[PMM] Total pages: 32768 (128 MB)
32[PMM] Bitmap size: 4096 bytes
33[PMM] Bitmap at: VA 0xffff000040133000 (PA 0x40133000 - 0x40134000)
34[PMM] First free page: PA 0x40134000 (index 180)
35[PMM] Free pages: 32588 (127 MB)
36[PMM] Used pages: 180 (kernel + bitmap)
37[PMM] Physical memory manager initialized!
38[KMALLOC] Initializing kernel heap
39[KMALLOC] Heap at 0xffff000040134000 - 0xffff000040174000 (256 KB)
40[KMALLOC] Kernel heap initialized!
41[MMU] Running at virtual address 0xffff0000400873f0
42[MMU] Kernel mapped: VA 0xffff000040080000 -> PA 0x40080000
43[MMU] Device memory: VA 0xffff000009000000 -> PA 0x9000000
44[MMU] Higher-half kernel initialized!
45[SYSCALL] System call interface initialized
46[SYSCALL] Supported: exit, read, write, brk, mmap, munmap, etc.
47[TTY] Console initialized (80x24, 115200 baud)
48[VFS] Initializing virtual filesystem...
49[CHARDEV] Initializing character device drivers...
50[VFS] Registered chrdev major 1: mem
51[VFS] Registered chrdev major 5: tty
52[CHARDEV] Character device drivers initialized
53[RAMFS] RAM filesystem initialized
54[RAMFS] Mounted ramfs filesystem
55[VFS] Mounted ramfs as root filesystem
56[DEVFS] Device filesystem initialized
57[DEVFS] Created /dev/console (major 5, minor 1)
58[DEVFS] Created /dev/tty (major 5, minor 0)
59[DEVFS] Created /dev/null (major 1, minor 3)
60[DEVFS] Created /dev/zero (major 1, minor 5)
61[DEVFS] Created /dev/full (major 1, minor 7)
62[VFS] Created /dev with device nodes
63[VFS] Created /tmp directory
64[PROCFS] procfs initialized
65[VFS] Created /proc directory
66[VFS] Virtual filesystem initialized
67[BLKDEV] Block device subsystem initialized
68[VIRTIO-BLK] Scanning for VirtIO block devices...
69[VIRTIO-BLK] Found block device at 0xffff00000a003c00 (version 2)
70[BLKDEV] Registered: /dev/vda (major 254, minor 0)
71[VIRTIO-BLK] vda: 2 MB (4096 sectors, 512 byte blocks)
72[VIRTIO-BLK] Found block device at 0xffff00000a003e00 (version 2)
73[BLKDEV] Registered: /dev/vdb (major 254, minor 1)
74[VIRTIO-BLK] vdb: 4 MB (8192 sectors, 512 byte blocks)
75[VIRTIO-BLK] Found 2 block device(s)
76[TARFS] TAR filesystem initialized
77[FAT32] FAT32 filesystem driver initialized
78[DEVFS] Created /dev/vda (block device, major 254, minor 0)
79[DEVFS] Created /dev/vdb (block device, major 254, minor 1)
80[BOOT] Mounting tarfs from /dev/vda...
81[VFS] Mounting /dev/vda on /mnt (type: tarfs)
82[TARFS] Parsed 30 entries
83[TARFS] Mounted vda (2097152 bytes)
84[VFS] Mounted /dev/vda on /mnt successfully
85[BOOT] Mounted tarfs on /mnt
86[BOOT] Mounting FAT32 from /dev/vdb...
87[VFS] Mounting /dev/vdb on /fat (type: fat32)
88[FAT32] Mounting filesystem from vdb
89[FAT32] Volume: KRNLDATA, ID: 12345678
90[FAT32] 512 bytes/sector, 1 sectors/cluster
91[FAT32] 8032 total clusters, root at cluster 2
92[FAT32] Filesystem mounted successfully
93[VFS] Mounted /dev/vdb on /fat successfully
94[BOOT] Mounted FAT32 on /fat
95[TASK] Task subsystem initialized
96[TASK] Initial task: PID 0 (kernel)
97[BOOT] Executing /mnt/bin/init...
98[USER] Loading ELF executable 'init' (88576 bytes)
99[ELF] Loading ELF: entry=0x100000, 1 program headers
100[ELF] Segment: vaddr=0x100000-0x104364 filesz=16076 memsz=17252 flags=RWX
101[ELF] Load complete, entry point: 0x100000
102[TASK] Created task: PID 1 (init), entry=0xffff000040084e40, stack=0xffff000040152f18-0xffff000040153f10
103[USER] Created ELF process: PID 1, entry=0x100000, brk=0x105000
104[BOOT] Started init as PID 1
105[BOOT] Enabling interrupts...
106[BOOT] Interrupts enabled!
107[BOOT] Scheduler enabled!
108
109[BOOT] Entering idle loop (task 0)
110[USER] Entering user mode: entry=0x100000, stack=0x7fffffe0
111
112=================================
113KRNL Init Process (PID 1)
114=================================
115
116[init] Testing FAT32 file creation...
117[init] Created /fat/testfile successfully!
118[init] Running readdir_test:
119[TASK] Created task: PID 2 (init), entry=0xffff000040084eb0, stack=0xffff00004013d718-0xffff000040141710
120[FORK] Created child process 2 (parent 1)
121[EXECVE] Failed to open '/mnt/bin/readdir_test': error 2
122[SYSCALL] Task 2 (init) exiting with status 127
123[TASK] Task 2 (init) exiting with status 127
124[TASK] Destroyed task: PID 2 (init)
125[init] Running ls /mnt test:
126[TASK] Created task: PID 3 (init), entry=0xffff000040084eb0, stack=0xffff00004013e390-0xffff000040142390
127[FORK] Created child process 3 (parent 1)
128[ELF] Loading ELF: entry=0x400000, 2 program headers
129[ELF] Segment: vaddr=0x400000-0x427100 filesz=160000 memsz=160000 flags=R-X
130[ELF] Segment: vaddr=0x438000-0x438f90 filesz=296 memsz=3984 flags=RW-
131[ELF] Load complete, entry point: 0x400000
132[EXECVE] Loaded 'ls' at entry=0x400000, sp=0x7ffffe80
133/mnt/bin/ls
134
135/mnt:
136hello.txt etc sbin bin
137[SYSCALL] Task 3 (ls) exiting with status 0
138[TASK] Task 3 (ls) exiting with status 0
139[TASK] Destroyed task: PID 3 (ls)
140[init] BusyBox found, will use ash shell
141[init] Starting BusyBox ash shell...
142
143BusyBox v1.36.1 (2025-12-17 21:48:14 CET) built-in shell (ash)
144
145krnl$ ls -l /
146ls: ls
147ls: -l/
148/:
149fat bin mnt proc tmp dev
Lung language
I also made a language (or rather, the LLM did—I just told it my wishes). It looks something like this:
package main
import "fmt"
import "./other.lung"
import "./sub/toto.lung"
def main(): void {
const message: string = "Hello, Lung!"
fmt.println(message)
let x: int = 10
let y: int = 20
x += y
fmt.println("x + y =", x)
let arr: [int] = [1, 2, 3, 4, 5]
fmt.println("Array:", arr)
fmt.println("Length:", len(arr))
if x > 25 {
fmt.println("x is greater than 25")
} else {
fmt.println("x is not greater than 25")
}
let i: int = 0
while i < 3 {
fmt.println("Loop iteration:", i)
i += 1
}
fmt.println("my function is", my_function())
fmt.println("sub: ", sub.my_sub_func())
}
The implementation happened in multiple phases. First, I had a working parser,
lexer, and stack-based interpreter. Then came the modules that you can import.
Finally, I decided I wanted to have even more fun and ended up compiling
binaries using cranelift. Of course, I didn’t stop there and ended up with the
ability to compile and link to other dynamic libraries. Here’s, for example, a
mini-game in lung with raylib:
package main
// Static raylib FFI sanity check:
// - white background
// - two rectangles
// - some text
// --- Minimal raylib FFI surface ---
extern "C" def InitWindow(width: int32, height: int32, title: *u8): void
extern "C" def CloseWindow(): void
extern "C" def WindowShouldClose(): bool
extern "C" def BeginDrawing(): void
extern "C" def EndDrawing(): void
@repr(C)
struct Color { r: u8, g: u8, b: u8, a: u8 }
extern "C" def ClearBackground(color: Color): void
extern "C" def SetTargetFPS(fps: int32): void
extern "C" def DrawRectangle(x: int32, y: int32, w: int32, h: int32, color: Color): void
extern "C" def DrawText(text: *u8, x: int32, y: int32, fontSize: int32, color: Color): void
// Run:
// /tmp/lung_raygame_test
def main(): int32 {
InitWindow(800, 450, cstr("Lung + raylib (FFI)"))
SetTargetFPS(60)
// fixed positions
let px: int32 = 100
let py: int32 = 100
let ps: int32 = 32
let cx: int32 = 400
let cy: int32 = 200
let cs: int32 = 14
while WindowShouldClose() == false {
BeginDrawing()
ClearBackground(Color { r: 245, g: 245, b: 245, a: 255 })
DrawRectangle(px, py, ps, ps, Color { r: 0, g: 255, b: 0, a: 255 })
DrawRectangle(cx, cy, cs, cs, Color { r: 255, g: 215, b: 0, a: 255 })
DrawText(cstr("Collect the coin!"), 16, 16, 20, Color { r: 0, g: 0, b: 130, a: 255 })
DrawText(cstr("Score:"), 16, 44, 20, Color { r: 0, g: 0, b: 0, a: 255 })
EndDrawing()
}
CloseWindow()
return 0
}
It’s very far from perfect, but I did learn a bunch because I asked the LLM way too many questions about different parts of its implementation.
Container runtime
I work at Docker, so of course I had to try making a container runtime. This time in Rust. The code for this one is available on my GitHub. The architecture is inspired by containerd but, y’know, completely generated by an LLM.
The project contains a CLI and a daemon that talk to each other over gRPC. The
daemon runs natively on Linux or macOS. On Linux, it uses runc directly to
create and run containers. On macOS, each container runs inside a micro-VM
thanks to libkrun.
The neat part of this experiment was the moment I decided I wanted faster networking in the libkrun case. After a lot of back and forth and multiple model switches, I managed to have a user-space networking stack that was ~20 times faster than what I had at the beginning. Don’t ask me how it works, but it works.
Docker TUI
Speaking of Docker, here’s a neat little TUI for your Docker engine.




Markdown editor
Of all the projects listed here, this one is the most useful. And I even use it daily now. It’s a simple and beautiful markdown editor. From the readme:
I made this just for me, there are plenty of other markdown editors, I’m pretty sure others would be better for you and will have less bugs.
Typr has one feature I could not find in any other: the ability to freely rearrange the notes in the sidebar. This is literally why typr exists.
I actually generated this twice, with the same agent and model. In the first try, I ended up with a big HTML file with JavaScript and CSS shoved inside; it was a huge mess. After a reset I retried, and this time after a couple of minutes I had a neat little Electron project. To my surprise, it was working really well and there wasn’t that much code. Basically, I learned of the existence of the excellent tiptap library.
Raytracer
I guess I was bored and thinking about my university days and how I spent months back in the day implementing a raytracer in Java. These days you can get one in less than an hour.

GameBoy emulator
Why not, right?



GitHub clone
Another one that goes into the “why not” category. I made a small GitHub clone, but without the social part. I’m kinda planning on cleaning up the code, adding some more tests, and then just deploying this on a server at home so that my code has a second place to call home. I’m very happy about the CI that came out of it; it’s YAML-based like GitHub Actions, but only uses images, and you can also run it locally with no fuss. The project has a server that listens to git events, syncs its own database, and runs CI when needed. There’s a nice UI for it and also a CLI.
I was particularly impressed when I decided I didn’t want my server to be Echo-based anymore, but wanted to use ConnectRPC instead. The agent was able to—one-shot—analyze the existing code, generate protobuf files, and convert the server, CLI, and UI to the new library. If I remember correctly, the whole thing took less than an hour and worked on the first try. Truly impressive.
Repository view

Issues, with epics 
Pull requests 
GitHub actions inspired CI

Commits with diff views

Sessions share for cagent
This was an easy and fun one. I pointed the agent to cagent’s repository and asked it to add a “Share” command that would send the current session to a remote server. I then asked the agent to make a React/Tailwind/shadcn/ui frontend that is capable of showing the session. If memory serves, less than 10 minutes later I had everything working and was able to share my sessions from cagent to a server deployed to Fly.io.

