# 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](https://blog.exe.dev/customizing-shelley)
and [Devtools must be open source](https://blog.exe.dev/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](/docs/agents-md) |
| Teach it a procedure it loads on demand | [Skills](/docs/skills) |
| Give it tools from another service | [MCP servers](/docs/mcp) |
| Rewrite prompts, add slash commands, react when a turn ends | [Hooks](/docs/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](/docs/skills) 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:

```sh
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](/docs/install#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](/docs/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](/docs/install#upgrading) 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:

```sh
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:

```sh
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](/docs/install#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](https://github.com/boldsoftware/shelley) (we require a CLA), and
the [Discord](https://discord.gg/jc9WQUfaxf) is a good place to show it off
first. Either way, once it's on mainline, it's one less commit to rebase.
