Code
This is a members-only repo. How to join →
The thinnest possible parallel dev environments for coding agents. No Docker, no VMs, no expensive cloud, just processes, files, env variables. Very debuggable.
cli
Find a file
nobody bf4c959f9c
fix: use attempt-based log URL for Forgejo 13+
Forgejo 13 changed the Actions log endpoint to require an attempt
segment (/attempt/1/logs). Falls back to the old URL format for
older versions.
2026-05-14 07:39:47 -07:00
.claude/commands Add contributor setup: direnv, gitignore, test fixtures, CI helper 2026-05-10 19:16:08 -07:00
.forgejo/workflows refactor: move dist/ into apps/nushell-thinslice-cli/installer/ 2026-05-12 04:57:19 -07:00
apps refactor: move dist/ into apps/nushell-thinslice-cli/installer/ 2026-05-12 04:57:19 -07:00
bin fix: use attempt-based log URL for Forgejo 13+ 2026-05-14 07:39:47 -07:00
docs feat: add workspace caching to test fixture and update docs 2026-05-11 21:47:20 -07:00
scripts refactor: move nushell CLI and zig port-probe into apps/nushell-thinslice-cli 2026-05-11 22:16:15 -07:00
.envrc Add contributor setup: direnv, gitignore, test fixtures, CI helper 2026-05-10 19:16:08 -07:00
.gitignore chore: remove download-page-kit stub and clean up config 2026-05-11 22:16:49 -07:00
.licenserc.yaml chore: remove vestigial slice-setup, stack-give-me-a-port, stack-source 2026-05-12 05:22:13 -07:00
AGENTS.md fix: support both old and new Forgejo session cookie names 2026-05-12 08:52:12 -07:00
check-license-headers Add license headers via skywalking-eyes 2026-04-23 07:45:13 -07:00
CLAUDE.md refactor: move nushell CLI and zig port-probe into apps/nushell-thinslice-cli 2026-05-11 22:16:15 -07:00
CONTRIBUTING.md refactor: move nushell CLI and zig port-probe into apps/nushell-thinslice-cli 2026-05-11 22:16:15 -07:00
default.do refactor: move nushell CLI and zig port-probe into apps/nushell-thinslice-cli 2026-05-11 22:16:15 -07:00
LICENSE
README.md Rewrite README motivation section and regenerate terminal demo 2026-05-10 20:08:25 -07:00
README.talk.md Rewrite README motivation section and regenerate terminal demo 2026-05-10 20:08:25 -07:00

thinslice cli

Get Started · Examples · Try the Demo · Docs · Discord

Bind any git worktree to a bag of state — its own postgres, redis, ports, config — and run your whole product. Detach, reattach different code. No Docker, no VMs. Just processes.

demo

Motivation

In the old days we worked on one branch at a time. You checked out main, start your dev servers where everything binds to the usual ports. There is a .env file with a DATABASE_URL pointed at the one Postgres running on your laptop. The frontend listens on 3000, and you move on to writing code.

Then the world changes. You're running a coding agent on a feature branch while reviewing a PR, and you want to see both of them running, hot-reloading, side by side. But both want port 3000, both want the same Postgres, and both assume they're the only copy of your app on this machine.

The root cause is that nobody treats port assignments, database locations, or Redis URLs as state. They get hardcoded in a .env file or baked into a config.toml and everyone moves on. When there's only one checkout that's fine, but when there are two and more it gets tedious.

The fix I'm proposing sounds strange at first so think about it a little. Stop running one Postgres on your laptop and start running several. Give each branch its own Postgres data directory, its own port, its own Redis, its own config. The same way you'd give each branch its own code checkout, give it its own state.

The Docker crowd got really close, but it's not lightweight. On macOS and Windows it runs a Linux VM underneath, and it's a greedy guest on a machine that's already running your browser, your editor, and Slack. But your laptop can handle a few extra Postgres processes without breaking a sweat.

/rant

Large companies tell us the answer to local dev getting complicated is to take it off your machine entirely. Remote dev environments give you a whole computer in the cloud, and they're making a profit off you every second you're using it. You can SSH in, sure, but your tools, your dotfiles, your workflow are all somewhere else. You're renting a machine to solve a problem your laptop can already handle.

It gets worse with managed agent sandboxes. They copy your code into an environment you often don't even get SSH access to. When the code doesn't work, or a state transition didn't happen the way you expected, you can't just pop in and look at your sqlite database. You sit and watch Anthropic spend your money try to figure out their mistakes. And if you don't like it, maybe you'd like to purchase an Ultraplan for $5 or a Ultrareview for $25. Yes Anthropic, when I go out to eat I love it when the waiter takes a shit in my food right at the table and then extoles the virtues of their Ultrafood upgrade to a (potentially) shit-free dining experience.

Remember underlying problem is a port conflict and the name of a database in an environment variable.

I think it's absolute fucking bonkers crazy to be renting computers, burning tokens, with the fuck you cherry on top of active internet connection.

The problem isn't code isolation (git worktrees). It's state isolation. And state isolation doesn't need containers. It needs directories, environment variables, and bookkeeping.

So I built thinslice.

How It Works

thinslice separates your repository into two halves: code and state.

Code lives in git worktrees — main/, dev/ for shared branches, local/<name> for your feature work, pr/<number> for reviews, tmp/<name> for throwaway experiments and agents.

State lives in state directories under +state+/. Each one is a self-contained bag of runtime state: a postgres data directory, redis dumps, config files, log files, pid files. A state directory has everything your processes need that isn't source code.

A slice.toml manifest in each state directory declares the environment variables and processes for that state:

[env]
DATABASE_URL = "postgres://localhost:5433/myapp"
PGDATA       = "$SLICE_STATE_DIR/postgres"
PORT         = "3001"

[programs.postgres]
start = "postgres -D $SLICE_STATE_DIR/postgres"
ports = [5433]

[programs.server]
start = "npm run dev"
ports = [3001]

The binding between a worktree and a state directory is explicit and exclusive — each state can be bound to at most one worktree at a time. slice use binds, slice detach unbinds. When you cd into a worktree, direnv loads the bound state's environment variables automatically.

Ports are allocated by a machine-wide registry (slice give-me-a-port) so nothing collides across projects. A thinslice.new-state script checked into your repo generates new state directories with fresh ports — it's just a shell script that prints TOML to stdout.

Getting Started

slice setup

slice setup converts an existing git checkout into a thinslice workspace — bare repo, state directories, worktree namespaces. It's idempotent; safe to re-run. See the docs for the full guide.

Contributing

See CONTRIBUTING.md.