vibebuilt
/ Vibe Coding / Cursor AI Tutorial: From Empty Repo to Shipped Fix
Vibe Coding • • 14 min read

Cursor AI Tutorial: From Empty Repo to Shipped Fix

A Cursor AI tutorial that takes one failing test to a reviewed commit through Plan Mode, the Run Mode that decides whether tests run unasked, and Agent Review.

Cursor AI Tutorial: From Empty Repo to Shipped Fix

This Cursor AI tutorial takes an empty folder to a committed fix in one sitting, and the thing it teaches that the older tutorials can't is where Cursor asks you for permission and where it doesn't. You'll create a two-file Python fixture with two failing tests, pick a Run Mode before the agent runs a single command, plan the change with Shift+Tab, let Agent edit and run the tests, read the diff, run Agent Review, and commit it yourself. Every step names the control in Cursor's current docs that governs it, because the guides ranking for this query still describe a "YOLO mode" that the docs have since replaced with three named modes and a classifier.

The fixture and its test output are real. I ran them. The Cursor steps are the documented ones, and I'll be clear about which is which as we go.

What Cursor AI Is, And How You Use It

Cursor is a code editor with an agent in the side pane. The agent overview describes it as an assistant that can complete coding tasks independently, run terminal commands, and edit code, opened with Cmd+I on a Mac or Ctrl+I elsewhere. It has tools for searching and reading files, editing them, running shell commands, using a browser, and asking you questions mid-task, and the docs say there's no limit on how many tool calls it makes in one task. Tab completion and inline edits still exist. This tutorial uses the agent, because the agent is where the permission questions live.

The way you use it is the loop below. Describe a change with a check attached, let it plan, let it edit, make it prove the check passed, and review before anything reaches git. If you'd rather run that loop from a terminal, the Claude Code tutorial on this site does the same thing with a different fixture, and the Cursor vs Claude Code comparison covers when I reach for which.

Install, Then Pick A Run Mode Before Anything Else

Cursor's quickstart covers install for macOS 12 and later, Windows 10 and later, and Linux through apt, yum, or an AppImage, with the apt and yum routes recommended because they get automatic updates and the CLI tools. Sign in, open the folder we're about to create, and then, before the first prompt, open Settings, then Agents, then Approvals and Execution.

That screen is the Run Mode, and it decides what happens at the step in this tutorial where the agent wants to run your tests. The Run Modes page documents three.

Run Mode When The Agent Runs python3 -m unittest -v Sandbox Classifier What I'd Use It For
Auto-review Runs in the sandbox without asking if it can; otherwise a small classifier model decides, and you're prompted if it's unsure Yes, for shell commands Yes This tutorial, and most daily work
Allowlist Runs only if the command is on your allowlist; everything else prompts Optional No A repo where you want the same four commands and nothing else
Run Everything Runs, along with every other tool call No No Nothing on a machine that holds credentials

The docs call Auto-review "the safest useful setup for most people" and, in the same breath, say it "is not a security boundary" because the classifier can allow a call you'd have blocked. Both statements are true, and the second is why the fixture below has no network access and nothing worth stealing. The sandbox that Auto-review uses blocks network by default and protects paths like .git/config and .git/hooks, and it needs Cursor v2.0 or later.

Regardless of mode, the security page sets three defaults worth memorizing. Reading files needs no approval. Editing workspace files needs no approval and saves to disk immediately, which is the sentence that should make you commit before every agent session. Terminal commands need approval unless your Run Mode says otherwise.

One more file before we start. Auto-review reads plain-English instructions from .cursor/permissions.json in the project, and a project-level file can be committed. For this tutorial I'd add one line that keeps the agent honest about the test file:

{
  "autoRun": {
    "allow_instructions": ["Running the project's unittest command is fine."],
    "block_instructions": ["Any command that deletes or rewrites test files should go through approval first."]
  }
}

The schema is the docs' own, and each instruction is a sentence the classifier reads. It's guidance for the classifier rather than a hard rule, which the docs say plainly, so the review step later is still yours.

The Fixture

Two files, standard library only. duration.py parses strings like 1h30m into minutes and has a bug that the tests expose:

import re

PATTERN = re.compile(r"^(\d+)h(\d+)m$")


def parse_duration(text):
    """Turn '1h30m' into 90 minutes."""
    match = PATTERN.match(text.strip())
    if not match:
        raise ValueError(f"Bad duration: {text!r}")
    hours, minutes = match.groups()
    return int(hours) * 60 + int(minutes)

test_duration.py:

import unittest

from duration import parse_duration


class ParseDurationTest(unittest.TestCase):
    def test_hours_and_minutes(self):
        self.assertEqual(parse_duration("1h30m"), 90)

    def test_minutes_only(self):
        self.assertEqual(parse_duration("45m"), 45)

    def test_hours_only(self):
        self.assertEqual(parse_duration("2h"), 120)

    def test_rejects_garbage(self):
        with self.assertRaises(ValueError):
            parse_duration("soon")


if __name__ == "__main__":
    unittest.main()

Initialize the repo and commit the broken state, so the diff later is unambiguous and so a checkpoint restore is never your only way back:

git init && git add -A && git commit -m "fixture: parse_duration with two failing tests"
python3 -m unittest -v

With the tracebacks cut and the long test names shortened, the output is:

test_hours_and_minutes ... ok
test_hours_only ... ERROR
test_minutes_only ... ERROR
test_rejects_garbage ... ok

ValueError: Bad duration: '2h'
ValueError: Bad duration: '45m'

Ran 4 tests in 0.001s

FAILED (errors=2)

Two errors, both the same exception, because the regex insists on both parts. Copy that output somewhere. It's the first piece of evidence, and the agent's later claim that "the tests pass" means nothing without the before.

Step One: Read Before Editing

Open Agent with Cmd+I. The quickstart's own first prompt is worth using verbatim, "Explain this codebase. Point me to the main entry points, key modules, and anything I should read before making changes." On a two-file repo the answer will be short, and that's the point. You're checking that the agent found both files and understood that the tests are the contract, before it has touched anything.

If it starts editing at this step, press Cmd+Shift+Backspace, which the reviewing lesson in Cursor's course names as the stop shortcut, and rephrase. An explanation request that turns into an edit is the first thing to notice about any agent, and noticing it on a fixture is cheaper than noticing it on a real branch.

Step Two: Plan Mode With Shift+Tab

Press Shift+Tab in the agent input. The mode rotates to Plan Mode, which the docs describe as researching the codebase, asking clarifying questions, writing an implementation plan, and waiting for approval before building. The quickstart says jumping straight to Agent is fine for quick changes, and this one is quick, but I'd plan it anyway the first time, because the plan is where you see whether the agent intends to fix the parser or "fix" the tests.

The prompt is the contract, with the check attached:

test_minutes_only and test_hours_only fail. Make parse_duration accept
"2h" and "45m" as well as "1h30m", keeping "1h30m" at 90.
Change only duration.py. Do not edit test_duration.py.
Then run: python3 -m unittest -v
Show me the full output.

Read the plan it returns. The acceptable plan changes the regex to make each part optional and rejects a match where both parts are empty. A plan that mentions the test file, adds a dependency, or proposes a second function is a plan to send back. The docs' advice for a wrong build is to revert and refine the plan rather than patching through follow-ups, and that applies before the build too. Edit the plan text, then approve it.

Step Three: The Edit And The Test Run

Now watch. The diff view shows the edit as it lands, and the agent overview says a checkpoint is created automatically before significant changes, so the pre-edit state is one click away in the chat timeline. Then the agent reaches the line of your prompt that says run the tests, and the Run Mode from earlier decides what happens. On Auto-review, a python3 -m unittest -v inside the workspace with no network should run in the sandbox without a prompt. On Allowlist you'll be asked unless you added it. On Run Everything it runs, along with anything else the agent felt like running.

The fix I'd expect, and the one I applied and tested, is four lines:

-PATTERN = re.compile(r"^(\d+)h(\d+)m$")
+PATTERN = re.compile(r"^(?:(\d+)h)?(?:(\d+)m)?$")
...
-    if not match:
+    if not match or not any(match.groups()):
         raise ValueError(f"Bad duration: {text!r}")
     hours, minutes = match.groups()
-    return int(hours) * 60 + int(minutes)
+    return int(hours or 0) * 60 + int(minutes or 0)

And the output that follows it:

Ran 4 tests in 0.000s

OK

The any(match.groups()) line is the detail I'd read for. Without it, an empty string matches the all-optional regex and returns zero instead of raising, and test_rejects_garbage wouldn't catch that because it only tests "soon". The agent may well write that guard on its own. If it doesn't, that's a review finding, not a failure of the tutorial, and it's exactly the kind of thing the testing beyond happy paths post is about.

Your evidence at this point is the diff, the full test output with OK, and git status showing one modified file. If test_duration.py is in that status line, restore the checkpoint or git checkout the file and go back to the plan.

Step Four: Agent Review, Then Git

After the agent finishes, the course lesson says to click Review, then Find Issues, for a dedicated line-by-line review of the proposed edits. Do it even on four lines. Then ask a second question in the chat, tagging @Branch so it sees the whole diff, along the lines of "what input would this parser accept that it shouldn't?" A good answer mentions 0m returning zero and whitespace being stripped. Whether either matters is your call, which is the point of asking.

Then commit it yourself. Checkpoints are stored locally and separate from Git, and the docs are explicit that they're for undoing agent changes while Git is for permanent version control. A checkpoint that vanishes with the chat is not a record of what shipped.

git add duration.py && git commit -m "parse_duration: accept hours-only and minutes-only"

That's the shipped fix. Five things exist now that didn't an hour ago, and they're the reason to bother with the fixture at all.

Evidence Where It Came From
Failing output with two named errors Your terminal, before the agent
An approved plan that names one file Plan Mode
A one-file diff The diff view, then git diff
Full test output ending in OK The agent's sandboxed run, or yours
A review finding, even a minor one Review, then Find Issues, plus @Branch

Where This Goes Sideways

What Happens Why What To Do
The agent edits test_duration.py to make the tests pass Nothing physically stops workspace edits; only your prompt and the review do Restore the checkpoint, tighten the plan, and read the testing post on tests that pass for the wrong reason
The test command prompts for approval on Auto-review The classifier wasn't sure, or the command needed something outside the sandbox Approve once, then add an allow instruction to permissions.json
The test command is blocked Sandbox has no network by default; a pip install inside the run will fail Good. The fixture needs nothing installed. If a real project does, install it yourself first
The agent "fixes" more than the regex Scope drift on an unbounded task The plan should have named one file; if it did and the agent ignored it, that's a finding worth a rule
You restore a checkpoint and the chat still thinks the change exists Restoring reverts files only, not messages Tell the agent explicitly that you reverted
Cursor's answer differs from this article The docs move, and so does the model Trust the diff and the test output over any prose, including mine

Add A Rule Only After The Second Mistake

The rules page lists four kinds, project rules as .mdc files in .cursor/rules, user rules, team rules, and an AGENTS.md file as the simple markdown alternative. A plain .md dropped into .cursor/rules is ignored, which catches people. The docs' own advice is to start simple and add a rule only when the agent makes the same mistake repeatedly, and I'd follow it. If the fixture run above produced a test-file edit, that's one mistake. If it happens again on a real branch, write the rule, and the AGENTS.md template post has a shape for it that other agents read too.

The Same Loop From The Terminal

Cursor also ships a CLI. The CLI overview installs it with curl https://cursor.com/install -fsS | bash, starts a session with agent, and supports the same Agent, Plan, and Ask modes as the editor, with --mode=plan and --mode=ask flags and a --sandbox switch. Print mode, agent -p "...", is the non-interactive form for scripts and CI. The tutorial above translates directly. Plan with --mode=plan, then run the same contract prompt, then read the same two outputs. I'd learn it in the editor first, because the diff view and the Review button are the parts that teach you what to look for.

Is Cursor AI Better Than ChatGPT?

For changing code in a repository, yes, because ChatGPT the chat app can't run your tests or edit your files, and Cursor's agent does both under the modes above. For thinking a problem through before you touch a file, they're closer than the comparison suggests, and the Ask mode in Cursor is read-only exploration in the same spirit. The fair comparison is Cursor against another coding agent, and the Cursor vs Claude Code post is that, including the answer to whether Cursor is owned by Elon Musk, which is no.

Is There A Free Course On Cursor AI?

Cursor's own course at cursor.com/learn opened for me without a sign-in and sits at position one for this query as of the fetch date. It's two tracks, an AI foundations sequence on models, tokens, context, tool calling, and agents, then a coding-agents sequence that runs from understanding a codebase through developing, debugging, reviewing, and customizing. The reviewing lesson is where the stop shortcut, @Branch, and Find Issues above come from. The Hobby plan on the pricing page is also free, with no card and limited Agent requests, which is enough for this fixture several times over. Individual is $20 a month with extended Agent limits, and every plan is an included amount of model usage with on-demand billing after it.

Which AI Course Is Best For Beginners?

The one attached to a tool you'll use tomorrow, with a fixture you can break. Cursor's course qualifies on the first count. This article is my attempt at the second, and the honest recommendation is to do both, then stop taking courses and ship one real change with the same evidence table. My concern with beginner courses in general is that they teach the features and skip the permissions, and the permissions are the part that decides whether the tool is safe on your actual repository.

Make It Fail First Next Time

The reusable habit here has nothing to do with Cursor specifically. Write the failing check, commit the broken state, choose the mode that decides what runs unasked, plan before editing, and keep the before and after outputs. Run that on a real bug in a real branch this week, with the Run Mode you actually intend to live with, and read whichever prompt Cursor shows you at the test step. That prompt, or its absence, is the tutorial.