shelley

Customizing

Self-modifying Shelley

Ask Shelley to change its own source. It builds the change, offers to install it, and rebases it onto new releases.

Shelley is open source, and it’s a coding agent, so the most direct way to customize it is to ask it to change itself. Open a conversation and say what you want: “make Shelley pink”, “make the UI high-contrast”, “add a toggle to the diff viewer that runs my review tool on each new commit”. Shelley checks out its own source, makes the change, builds a new binary, and offers to swap it in for the one you’re talking to. When a new release ships, one button in the Version dialog has Shelley rebase your changes onto it, so they carry forward.

The other pages in this section (AGENTS.md, skills, MCP, hooks) are the classic kind of customization: configuration and plug-in points. This is the other kind: edit the code. There’s no extension API to fit into, so a change can go anywhere: the UI, the built-in tools, the agent loop. The exe.dev blog makes the case in Customizing Shelley, Customizing Software and Devtools must be open source.

When to use it

Editing the source means a rebuild, a restart, and a rebase on every upgrade. When something lighter will do, use that:

You want to Use
Tell the agent your conventions AGENTS.md
Teach it a procedure it loads on demand Skills
Give it tools from another service MCP servers
Rewrite prompts, add slash commands, react when a turn ends Hooks
Change anything else: the UI, built-in tools, how Shelley behaves Its source, on this page

Shelley knows the difference too: for small tweaks, like changes to the system prompt or new-conversation defaults, it reaches for hooks, which need no rebuild.

How it goes

The built-in customizing-shelley skill does the work, and it’s always available, so there’s no setup. Ask for a change to Shelley and the model loads the skill and follows it. To read exactly what it’s told:

shelley skill cat customizing-shelley

1. A checkout of its own. Shelley clones its repository to ~/.config/shelley/shelley-customization and works on a branch called custom. Each change is a commit there, with a message that says what it’s for. That branch is your customization: it’s what gets rebuilt, and what gets rebased later.

2. A customized build. It builds with make build-custom, not plain make. That produces bin/shelley stamped as a customized build: the version reads like 0.1346.914604164-custom.1a2b3c4 (the release it’s based on, plus your HEAD), and shelley version reports "customized": true. Building needs Go, Node.js, and Python 3, like any build from source, plus a full clone with tags. Shelley runs the tests for the packages it touched before it offers to install anything.

3. Try it, or install it. You get two choices, and it installs only if you pick the second:

  • Run it off to the side. The new binary starts on a spare port (the skill suggests 8010) with a separate, throwaway database, in tmux so it outlives the turn. You try it at http://localhost:8010/, or on exe.dev at https://<vmname>.exe.xyz:8010/. Your real Shelley keeps running.
  • Install it over the running Shelley. Shelley asks itself where its binary lives, copies the new one next to it, and renames it into place. If it’s running under systemd, its last act is to tell the server to exit with a failure status, so Restart=on-failure starts the new binary right away. Conversations that were mid-turn are marked to continue, so the one that did the install picks up after the restart and tells you how it went. Not under systemd? It installs the binary and asks you to restart Shelley yourself.

On exe.dev, where Shelley runs under systemd with sudo available, the whole loop happens from the chat.

Upgrading a customized build

A customized Shelley can’t take the normal self-update: swapping in a release binary would throw your changes away, so the server refuses, and auto-upgrade skips customized builds. Upgrading means rebasing instead.

Open the ⋮ menu and choose Check for New Version. The Version dialog marks the build customized, says which release it’s based on, and lists your Customizations: the commits on custom that aren’t on mainline. When there’s a newer release, the button reads Rebase onto v0.N.… using model, with a model picker next to it. Pick a model you trust with merge conflicts and press it.

That starts a new conversation in the checkout. It fetches mainline, rebases custom onto it, resolves conflicts using your commit messages to work out what each change was for (and asks you when that’s unclear), rebuilds with make build-custom, runs the tests, and offers the same run-aside-or-install choice as before. If the checkout has gone missing, it clones a fresh one first.

Keeping rebases painless

The skill tells Shelley most of this already; it helps to know it too.

  • Commit everything. The rebase works on the custom branch. Uncommitted edits in the checkout get in the way.
  • Small, well-described commits. They’re the context the rebase has when mainline moved the code underneath them.
  • Leave the existing database schema alone. Mainline adds migrations all the time, and changes to shared tables are where rebases hurt most (and can leave your database out of step with what later migrations expect). If a feature needs to store something, add a new migration that creates a new table.
  • Always make build-custom. A plain make build produces an unstamped binary that looks like a normal release, so the Version dialog would offer a binary self-update, and taking it would silently drop your changes.
  • Keep the checkout where it is. The Version dialog looks for it at ~/.config/shelley/shelley-customization; make build-custom warns if you build anywhere else. Once you’re running a customized build, that checkout is the record of what’s deployed.

Doing it by hand

Nothing here needs the agent. The same steps, yourself:

git clone https://github.com/boldsoftware/shelley ~/.config/shelley/shelley-customization
cd ~/.config/shelley/shelley-customization
git checkout -b custom
# ...edit, test, commit...
make build-custom

Then install bin/shelley over your current binary and restart Shelley. To pick up a new release later:

git fetch origin main --tags
git rebase origin/main custom
make build-custom

Going back to mainline

Install a release binary over the customized one (see Get the binary) and restart. A release build isn’t stamped as customized, so normal upgrades come back. The checkout stays in ~/.config/shelley/shelley-customization in case you change your mind.

Sending changes upstream

If something you built would be good for everyone, contributions are welcome on GitHub (we require a CLA), and the Discord is a good place to show it off first. Either way, once it’s on mainline, it’s one less commit to rebase.