Claude AI coding, one turn at a time
Most write-ups about this list Claude’s products. Few show what a Claude coding turn actually is when something else is holding the project. In mk0r, one turn is one local git commit on a Vite + React project, with the first line of your prompt as the commit message. Undo is also a commit. Redo is also a commit. Read along, the file paths are real.
Direct answer (verified 2026-05-01)
There are three live ways to use Claude AI for coding right now:
- Chat at claude.com. Paste your code into the conversation, get edited code back, copy it out by hand. The loop runs through your eyeballs.
- The Claude Code CLI from Anthropic (anthropic.com/claude-code). A terminal agent you point at a local repo. Claude reads and edits files on your machine, you keep the shell.
- A hosted sandbox where Claude edits files inside a project the host runs, with a dev server, browser, and git already wired up. mk0r is one example. The rest of this page is about the third mode, because the third mode is the one nobody else writes about.
Source for the surface list: anthropic.com/claude-code.
Three places Claude can write code, side by side
The three surfaces above feel similar in chat and very different in plumbing. Here is the same task (“add a TODO list component to a React app”) walked through each one.
Same Claude, three coding loops
| Feature | Chat or CLI | Hosted sandbox (mk0r) |
|---|---|---|
| Where the code lives | claude.com chat scratchpad, or your local repo | /app inside an E2B VM you do not own |
| Who runs the dev server | You run it (or there is no dev server) | Vite on :5173 with HMR, prewarmed |
| What an undo means | Hit Cmd+Z in your editor (CLI), or copy from chat history | git checkout to the prior SHA, then a new commit |
| Browser verification | You open the browser yourself | Playwright MCP attached to Chromium on CDP :9222 |
| Model defaults | Whatever your account picks (CLI) or chat session uses | Haiku 4.5 free, Sonnet/Opus on subscription |
| Cost on first try | Anthropic plan needed (chat or CLI) to do anything serious | Zero, anonymous Firebase session, host pays the inference |
One Claude coding turn, end to end
From the moment your prompt lands in mk0r’s chat input to the moment a new SHA shows up in the history bar, here is what actually happens. Every step is in source you can read.
- The browser POSTs your prompt to
/api/chat. The route checks Firebase auth and either reuses your Claude OAuth tokens or falls back to the sharedANTHROPIC_API_KEY. - The route calls into the ACP bridge running inside your VM. Claude (the model the session is pinned to, default Haiku 4.5) receives the prompt plus the system prompt at
src/core/e2b.ts:148and the in-VM CLAUDE.md files. - Claude reads files in
/app/src, writes new ones, runs npm if needed, and (for UI work) openshttp://localhost:5173via Playwright MCP to verify the result. - When Claude reports done, the chat route sends a final
doneevent to the browser, then immediately callscommitTurn(sessionKey, msg)at line 946. commitTurnrunsgit add -A && git commit -q -m ‘...’inside the VM, gets the new SHA, and pushes it ontosession.historyStack.- The SHA flows back to the browser as a
versionevent. The history bar adds a chip. The undo button is re-enabled.
One chat turn, two artifacts: a streamed reply and a git SHA
The anchor fact: your prompt IS the commit message
This is the bit nobody else writing about Claude AI coding mentions. Inside mk0r, the commit message is not generated, not summarized, and not auto-named “agent turn 47.” It is the first 120 characters of the first line of the prompt you typed. That choice is in src/app/api/chat/route.ts at lines 942 to 945:
If you typed “add a dark mode toggle to the header”, then git log --oneline in the VM later shows add a dark mode toggle to the header with the SHA next to it. The chat history and the git history are two views of the same list. Empty diffs are caught early: git diff --cached --quiet (e2b.ts:1648) skips the commit and emits NOCHANGE so the history bar does not collect ghost commits.
The repo has no remote. src/core/vm-claude-md.ts line 318 spells this out for the in-VM agent: “Shell: Debian with bash, Node 20, npm, git (no remote configured).” That is intentional. The repo is the unit of state for the session, not a publish target. Publishing happens through a separate code path (POST /api/publish) that emails an operator today.
“The commit message is the first 120 characters of the first line of your prompt. The chat scrollback and git log show the same list of edits, in the same order.”
src/app/api/chat/route.ts:942-945, src/core/e2b.ts:1635-1689
Undo is a commit. Redo is a commit. So is everything in between.
The most surprising part of this design is what happens when you click undo. POST /api/chat/undo does not run git reset. It does not detach HEAD. It walks session.activeIndex back by one, then runs git checkout <prior-sha> -- . && git commit --allow-empty -m ‘Undo to <sha7>’. The working tree is restored to the prior state and the act of restoring is itself a commit.
That choice has two consequences worth knowing about. First, git log inside the VM is a faithful audit log: it records the order of human decisions (forward edit, undo, redo, forward edit again), not just the final state. Second, if you start a fresh edit after undoing, the future tail of historyStack is truncated (e2b.ts:1671 to 1677) so the alternate timeline is gone from the chat surface, even though the orphaned commits are still reachable via raw git inside the VM.
If you have ever used the undo stack in Figma or VS Code, that is the model. The interesting part is that the underlying artifact is not an in-memory diff buffer. It is a real local git repository that survives a sandbox restore.
What this means for you, in practice
The reason any of this is worth describing in detail is that it changes the texture of working with Claude on code. When the chat surface is also a git surface, you can:
- Ask Claude to do three things, undo two of them, and ship just the first, without losing the other two from the chat scrollback.
- Treat your prompts as commit messages from the start, which gently pushes you toward writing prompts that are also good first lines (specific verb, scoped noun, no preamble).
- Skip the “copy this code into your editor” round trip that the claude.com chat surface still requires.
- Stop reasoning about whether your previous turn “took.” If the version event arrived with a SHA, it took. If you got
NOCHANGE, Claude said something but did not edit anything.
Default model: Haiku 4.5
FREE_MODEL = 'haiku' at src/app/api/chat/model/route.ts:5. Anonymous and free signed-in sessions stay on Haiku for unlimited turns. Subscription unlocks Sonnet and Opus via /api/chat/model.
Repo path: /app
Vite + React + TypeScript + Tailwind v4. Claude only edits files under /app, with the dev server already running on :5173 with HMR.
No remote
git is local to the VM. Publish goes through a separate POST /api/publish path, not git push. Mentioned in the in-VM CLAUDE.md at vm-claude-md.ts:318.
Browser already booted
Chromium with CDP on :9222, Playwright MCP attached. Claude can open localhost:5173 itself and assert against the rendered DOM before reporting done.
Empty turns are skipped
git diff --cached --quiet (e2b.ts:1648) returns NOCHANGE if Claude wrote no files. The history bar never collects empty SHAs from chatty turns.
Sessions persist across restarts
session.historyStack and session.activeIndex are persisted to Firestore via persistSession (e2b.ts:1681). A reload restores the timeline, including the active commit.
When mk0r is the wrong shape for “Claude AI coding”
A hosted sandbox is right when you want to start from a blank project, iterate quickly, and not deal with local setup. It is wrong in a few cases worth saying out loud. If you already have a large existing codebase and you want Claude to read across it, the Claude Code CLI on your laptop is the better surface: it sees your whole tree, your real terminal, your actual node version. If your work is mostly “help me understand this error” or “rewrite this snippet,” the chat at claude.com is fine and you do not need a project at all. If you need native iOS or Android, complex backend logic that the Vite + React stack will not cover, or a different language than TypeScript, none of these three modes are an honest answer today.
The honest pitch for the third mode is: you are building a small, sharp, web-shaped thing, and you would rather have the git history happen automatically than fight a setup. Open mk0r.com and write the first line of what you want. That line becomes the first commit message. The next line becomes the second.
Want to walk a Claude coding turn together?
Twenty minutes, one prompt, one commit, on a screen share. Bring an idea.
Frequently asked questions
What does it actually mean to use Claude for coding in 2026?
Three live surfaces, in order of how much glue you write yourself. First, chat at claude.com: you paste code in, get back code, copy it out. Second, the Claude Code CLI from Anthropic (anthropic.com/claude-code): a terminal agent you point at your local repo, with permission to read and edit files. Third, a hosted sandbox like mk0r: you describe what you want, Claude edits files inside a Vite + React project running on a remote VM, and the host handles git, the dev server, the browser, and the model. The chat surface is the same in all three. The thing that differs is who owns the loop.
When mk0r calls Claude to write code, where does the code actually go?
Into a real Vite + React + TypeScript + Tailwind v4 project at /app inside an E2B sandbox. The dev server runs on port 5173 with HMR. Claude edits /app/src/ via Claude Code's native file tools, the dev server hot-reloads, and Playwright MCP (attached to a Chromium with CDP on port 9222) lets the agent open http://localhost:5173 to verify its own work. The /app directory is a git repo from boot: src/core/e2b.ts:624 runs 'git commit -q --allow-empty -m \'Initial template\'' as part of the template setup. There is no git remote. The repo lives only inside the VM, so every commit is local.
What turns one Claude turn into one commit?
The chat route does it explicitly. After the agent reports done, src/app/api/chat/route.ts at line 946 calls commitTurn(sessionKey, msg). The msg is computed at lines 942 to 945 as prompt.split('\n')[0].trim().slice(0, 120) || 'Agent turn'. So the commit message is the first 120 characters of the first line of your prompt. commitTurn itself is in src/core/e2b.ts at line 1635 and runs 'git add -A && git commit -q -m \'<msg>\' && git rev-parse HEAD' inside /app via execInVm. The returned SHA is pushed onto session.historyStack at line 1678 and emitted to the client as a 'version' event at chat/route.ts line 947.
What does undo do? Is it a real git operation?
It is a real git operation that ends in a new commit. POST /api/chat/undo (src/app/api/chat/undo/route.ts) calls undoTurn(sessionKey) at line 14. undoTurn (e2b.ts:1731) walks session.activeIndex back by one and looks up session.historyStack[idx - 1] for the prior SHA. It then calls revertToSha (e2b.ts:1691) which runs 'git checkout <prior-sha> -- . && git add -A && git commit -q --allow-empty -m \'Undo to <sha7>\'' inside /app. So undo is forward-only in commits and reversible in state. Redo is symmetric: redoTurn (e2b.ts:1744) advances activeIndex by one and reverts to the next SHA, again as a new commit.
Which Claude model writes the code?
By default, Claude Haiku 4.5. src/app/api/chat/model/route.ts line 5 declares FREE_MODEL = 'haiku'. Anonymous and free signed-in sessions stay on Haiku, and src/core/e2b.ts line 1275 posts modelId: 'haiku' when a fresh sandbox is initialized so prewarmed VMs come up on the free model. A signed-in user with an active subscription can call POST /api/chat/model with modelId of 'sonnet' or 'opus' and the route swaps the active model on the live ACP session. Free users who try to switch get a 403 with error: 'subscription_required' (chat/model/route.ts lines 17 to 25).
Do I need a Claude account to try this?
No. Open mk0r.com and start typing. src/components/auth-provider.tsx signs you in anonymously through Firebase, no email, no password. Inference for that session runs through the host's shared ANTHROPIC_API_KEY (src/app/api/chat/route.ts line 187) and is billed to the host, not to you. If you want to use your own Claude.com Pro or Max subscription instead, sign in with Google and connect Claude via the OAuth PKCE flow at /api/claude/oauth/start. Once connected, the chat route prefers your OAuth token over the shared key on every turn.
How is this different from running the Claude Code CLI on my laptop?
Same agent, different surface area for setup. The Claude Code CLI assumes a terminal, an authenticated Anthropic account, a local repo, and that you trust it with your filesystem. A hosted runtime moves all of that to a remote VM. Trade-off: you give up direct shell access in exchange for a fresh sandbox per app, a dev server already running, browser verification wired in, and a project repo you cannot accidentally damage. The flip side is you cannot drop into bash and grep your own /etc the way the CLI lets you do on your machine.
Why is the commit history a stack, not just plain HEAD?
Because the chat surface needs cheap branchy navigation. session.historyStack and session.activeIndex (initialized in commitTurn at e2b.ts:1670 and 1679) let undo and redo move along a known timeline without resetting HEAD. When a new turn lands while the user is rewound (activeIndex < length - 1), commitTurn at e2b.ts:1671 to 1677 truncates the future tail, so a new edit on top of an undo discards the alternate timeline cleanly. That is a familiar shape if you have ever used multi-step undo in a code editor: the new commit reshapes history exactly the way the user expects.
See your prompt land as a real git SHA
Open mk0r, type one line, watch the preview change, then check the version chip in the history bar. That chip is a git SHA on a real repo at /app inside a sandbox you do not have to manage.
Open mk0r