Guide

The vibe coding application where every turn is a real git commit

Most articles about vibe coding applications tell you what a vibe coded app is. Useful once. What they skip is how the tools behave when you change your mind and want the previous version back. mk0r gives every session its own git repo, commits each turn, and does forward-only undo. Here is exactly how.

m
mk0r
6 min
4.8from 10K+ creators
Real git per session
Forward-only undo
No signup

What a vibe coding application is, briefly

A vibe coding application is an app you build by describing it in plain language instead of writing code. The LLM writes the code, you react to the result, you describe the next change. The phrase comes from Andrej Karpathy, and the category now includes Cursor, Lovable, Bolt, v0, Base44, Replit Agent, and mk0r.

The interesting differences between these tools are not whether they build an app (they all do). The interesting differences are how they treat the twentieth turn, the one where you liked turn nineteen better. That is where mk0r looks different under the hood.

Every session provisions its own Freestyle git repo

When you start a VM session, mk0r calls Freestyle's API to create a fresh git repo, creates an identity, grants that identity write permission on the repo, and mints a per-session git token. The token goes into an HTTPS remote URL of the form https://x-access-token:<token>@git.freestyle.sh/<repoId>.

Inside the VM, the /app directory is a Vite plus React plus TypeScript project. The init script does git init, writes a small .gitignore that excludes node_modules, dist, .DS_Store, and *.log, makes an initial empty-allowed commit, then force-pushes to origin main exactly once to seed the remote.

You can read the whole provisioning block in src/core/freestyle.ts, function ensureSessionRepo. The resulting initial sha is pushed onto the session's historyStack and persisted to Firestore.

Every chat turn is a commit

When the agent finishes a turn, the server calls commitTurn, which executes a tiny script inside the VM:

cd /app git add -A if git diff --cached --quiet; then echo NOCHANGE; exit 0; fi git commit -q -m '<agent message>' git push -q origin main git rev-parse HEAD

The returned sha is pushed onto the session's historyStack, and activeIndex moves to the end. If the session was pointed at an earlier commit when the turn started (because you had clicked undo), the stack is sliced first so the abandoned future is dropped from the active path. Standard branching-from-a-past-state behavior.

The net effect: your session's git log is a faithful minute-by-minute record of what the agent did, including the turns you later decided were wrong.

Start a session

No account, no card. Describe the app you want; every turn lands as a real commit.

Open mk0r

Undo is a forward-only revert, not a reset

This is the part that makes mk0r's history behavior uncopyable with a copy-paste of the prompt. When you click undo, the /api/chat/undo route calls undoTurn, which looks up the previous sha in the historyStack and hands it to revertToSha. That function does this, in the VM:

cd /app git fetch -q origin git checkout <target-sha> -- . git add -A git commit -q --allow-empty -m 'Undo → <sha7>' git push -q origin main git rev-parse HEAD

Read that second line carefully. It is git checkout <sha> -- ., which restores the tree at that sha into the working directory without moving HEAD. The following commit is a new forward commit whose tree equals the old tree. No git reset --hard, no force-push, no rewritten history.

Redo works the same way against the next sha in the stack. So does the "revert to this turn" button on the history panel, which calls jumpToSha with the exact commit you clicked.

You can verify every line above in src/core/freestyle.tsaround line 1473.

Why this matters for a vibe coding workflow

Vibe coding sessions drift. You build something, steer it toward an idea, realize three turns ago was better, walk it back, push again. If the tool's history model is "whatever is on disk right now," walking back means retyping the old idea and hoping the agent lands on the same file layout. You will not get the same layout.

With a commit per turn and a forward-only revert, the old tree is one git checkout away, exactly as it was. The agent does not need to remember anything. git does.

And because nothing is ever force-pushed, a clone of the remote at the end of a session is a replayable record of the whole build, including every mistake. That is a nicer artifact than the output of most tools in this category.

What this is not

It is not a full branching UI. There is one active path through the historyStack at a time, and a new turn after an undo truncates the forward slice. If you want multiple parallel versions you clone the repo out and use git normally.

It also does not apply in Quick mode. Quick mode generates a single self-contained HTML file with Claude Haiku and streams it into a live preview; there is no VM and no repo. If you want turn-by-turn git-backed history, pick VM mode.

Frequently asked questions

What is a vibe coding application?

A vibe coding application is software you build by describing it in natural language and letting an LLM write the code. Andrej Karpathy coined the phrase for this workflow. mk0r is one example: you describe an app, it builds the app, and the result is a real Vite plus React plus TypeScript project you can keep iterating on.

What actually lives in the git repo behind my session?

A full /app directory: a Vite plus React plus TypeScript project, package.json, your components, and a .gitignore that excludes node_modules, dist, .DS_Store, and log files. The first commit is labelled 'Initial template' and every chat turn afterwards lands as its own commit with the agent's message.

How does undo work if there is no reset?

The undo route calls undoTurn, which calls revertToSha with the previous sha on the session's historyStack. revertToSha shells into the VM and runs git fetch, then git checkout <sha> -- . to restore the old tree into the working directory, then git add -A, git commit --allow-empty -m 'Undo → <sha7>', then git push origin main. The old commits stay in history. You can read it at src/core/freestyle.ts around line 1473.

Why forward-only instead of git reset?

Freestyle hosts the remote, and a force-push to a shared remote is an easy way to lose work if two things race. Forward-only means every undo, redo, and revert adds a commit rather than removing one, so the git log is a literal record of the session including the mistakes. If you decide the undo itself was wrong, redo is another forward commit.

Where does the session state get persisted?

To Firestore, via persistSession in src/core/freestyle.ts. Each document stores vmId, sessionId, host, repoId, identityId, gitToken, historyStack (an array of sha strings), and activeIndex (the current position in the stack). That is how the undo pointer survives even if the VM is torn down and rehydrated.

Can I pull the repo out and host it myself?

Yes. The repo is a normal git remote at git.freestyle.sh under your session's repoId, authenticated with the per-session gitToken. Because commits are never rewritten, a clone is a faithful replay of the build, one turn at a time.

What happens on redo after I edit post-undo?

commitTurn detects that activeIndex is behind the end of historyStack and truncates the forward slice before pushing the new commit. That is standard branching-off-a-past-state behavior: once you take a different turn from a rewound point, the abandoned future disappears from the active stack but remains in git history.

Do I need to sign up to use any of this?

No. Open mk0r.com, describe the app, and the session provisions its own git repo, identity, and write token through Freestyle's API. No email, no card, no onboarding wall.

Build a vibe coded application with a real git log behind it. No signup, no card, just describe and build.

Try mk0r