Hosted onhyperacademia.orgvia theHypermedia Protocol

Customizations with Code

Read this article about how Exe.dev lets you customize their coding agent

It’s similar to our recent discussions about a dev kit.

It’s not a plugin system or runtime code evaluation. It basically just clones the repo and builds customized app locally. And when it updates itself, it rebases the changes on top of the new code.

It actually doesn’t even sound that crazy. Of course mainly because it’s just a binary that runs on Linux, not a full desktop app running on multiple platforms. But still.

AI Summary

No — it is not limited to UI. The “make Shelley pink” demo happens to modify the UI, but the mechanism supports changing Shelley’s Go server, agent behavior, tools, prompts, UI, or essentially any source-controlled part of the application.

What actually happens

Shelley does not possess a special runtime “rewrite myself” API. It uses its normal coding-agent capabilities:

    The request activates the built-in customizing-shelley skill.

    Shelley clones its own public repository into:

    It creates or uses a custom Git branch.

    The agent edits the source through its ordinary shell and file tools.

    It commits the changes.

    It runs:

    That builds an entirely new Shelley executable.

    With user approval, it atomically replaces the currently installed executable.

    It schedules a delayed systemctl restart shelley, allowing the current response to finish before the process kills itself.

The skill explicitly says it applies to Shelley’s “code, UI, tools, or behavior beyond what hooks allow.”

Why UI changes are included in the executable

The build target first builds the frontend and then compiles the Go program:

build-custom: ui templates

The UI step runs pnpm install and pnpm run build; the resulting frontend assets are then part of the final Shelley build.

So changing CSS is only one possible customization. A request could instead change:

    Go HTTP handlers.

    Database behavior.

    Tool implementations.

    Conversation orchestration.

    Built-in skills.

    Model-selection logic.

    The Vue frontend.

    Anything else that can be rebuilt into Shelley.

For smaller prompt/default-behavior changes, the skill recommends Shelley hooks because those avoid rebuilding the binary.

How it replaces a running copy of itself

It cannot overwrite the inode of the running executable directly because Linux may return ETXTBSY — “Text file busy.” The skill therefore uses a side-by-side replacement:

DEST=$(curl -s "$SHELLEY_URL/version-check" | jq -r .executable_path)


sudo cp bin/shelley "$DEST.new"

sudo chown --reference="$DEST" "$DEST.new"

sudo chmod --reference="$DEST" "$DEST.new"

sudo mv "$DEST.new" "$DEST"

The final mv replaces the directory entry atomically. The old Shelley process continues executing the already-mapped old binary; the next process invocation loads the new one.

This is a normal Unix self-update pattern — it is not modifying the code of the live process.

Shelley’s standard non-custom update path uses the same basic idea. It downloads and verifies a released binary, attempts selfupdate.Apply, and falls back to copying a .new binary and atomically renaming it over the executable.

How it restarts without cutting off its own answer

The customization skill tells the agent to finish its response and then launch a detached delayed restart:

tmux new-session -d 'sleep 5 && sudo systemctl restart shelley'

The currently running Shelley answers the user, exits the turn, and is killed a few seconds later by systemd. Systemd starts the executable now present at the original path — which is the customized binary.

Conceptually:

old Shelley process

    ├─ builds new Shelley

    ├─ renames new binary over /usr/local/bin/shelley

    ├─ sends final response

    └─ schedules systemctl restart

             ↓

old process terminated

             ↓

systemd execs new /usr/local/bin/shelley

What

build-custom

adds

A customized binary is stamped using Go linker variables:

This matters because a normal binary update would overwrite and discard the customization. The marker makes the server and UI switch to a rebase-based upgrade flow instead.

The server exposes:

    Whether the binary is customized.

    The canonical customization checkout.

    The executable path.

    Custom commits relative to origin/main.

    Whether it runs under systemd.

It also refuses the ordinary binary updater when Customized is true.

How future upgrades work

The “Upgrade: rebase onto latest” button does not itself perform a rebase. It constructs a prompt and starts a new Shelley conversation in the customization checkout. That new agent turn is instructed to:

git fetch origin main --tags

git rebase origin/main custom

make build-custom

Then it resolves conflicts, tests, and offers to install the rebuilt executable.

The persistent model is therefore:

origin/main

    \

     custom commit 1

     custom commit 2

     custom commit 3

When upstream changes:

git rebase origin/main custom

The agent reapplies those customization commits onto the new upstream tip.

The important distinction

There are two levels of “self-modification” here:

Application-level self-modification: Shelley, acting as a coding agent with shell access, edits its own repository and builds a replacement binary.

Process-level self-modification: It does not do this. The live process does not rewrite its loaded machine code or hot-reload arbitrary server changes. It builds a successor and asks the service manager to replace the process.

The clever part is mostly packaging and agent instructions:

    A canonical self-checkout.

    A dedicated customization branch.

    Custom-build metadata.

    Atomic binary replacement.

    Deferred restart.

    Agent-driven rebasing during upgrades.

The underlying mutation mechanism is ordinary Git, compiler invocation, filesystem replacement, and systemd.


Do you like what you are reading? Subscribe to receive updates.

Unsubscribe anytime