As a developer who spends more time in the terminal than in the browser, I've been obsessed with AI coding assistants that can execute commands, write files, and debug in real time. Two tools dominate this space: Claude Code (Anthropic's CLI agent) and Cursor's terminal agent. Both promise to turn natural language into shipping code, but they take wildly different approaches. I spent two weeks building a small SaaS backend with each, side by side, to see which one actually made me faster. Here's what happened.

[Loading prompt card for Claude...]

The Problem I Was Trying to Solve§

I needed to build a production-ready REST API for a subscription management service. The requirements were standard: PostgreSQL database with migrations, user authentication (JWT), Stripe integration for payments, async background jobs for invoice generation, and a Docker Compose setup for local development. Nothing cutting-edge, but a real-world mix of boilerplate, business logic, and third-party integrations.

The catch: I wanted to do as much as possible from the terminal, without touching a code editor GUI. I wanted the AI to not only write code but also run terminal commands (e.g., npm install, psql, docker-compose up), read error logs, and iterate autonomously. Essentially, I wanted a pair programmer who also operates the keyboard.

Both Claude Code and Cursor's terminal agent claim to do this. But I'd heard mixed things: Claude Code is powerful but can be slow and aggressive with file writes; Cursor's agent is fast but might hallucinate command outputs. I needed real data.

Tools and Setup§

I used:

  • Claude Code (via claude CLI, model: claude-3-5-sonnet-20241022)
  • Cursor (v0.45.x, with agent mode enabled and terminal integration turned on, model: gpt-4o as fallback, but primary was Claude 3.5 Sonnet for fairness)
  • A fresh Ubuntu 22.04 VM with Node.js 20, PostgreSQL 16, Docker, and Git installed
  • Two identical project directories: ~/api-claude and ~/api-cursor

For each tool, I started from a blank directory and used the same initial prompt (detailed below). I timed each session for the entire build, not counting trivial disruptions. I also recorded the number of terminal commands executed, errors encountered, and manual interventions needed.

Step-by-Step: What I Actually Did§

Phase 1: Initial scaffold and database setup§

I began by asking each tool to initialize a Node.js TypeScript project with Express, Prisma ORM, and configure PostgreSQL. For Claude Code, I used:

# Initial prompt for Claude Code
Initialize a Node.js TypeScript project in this directory. Use the following:
- Package manager: npm
- Framework: Express
- Database: PostgreSQL with Prisma ORM
- folder structure under src/ with routes, controllers, services, middleware

Install all dependencies including prisma, @prisma/client, typescript, tsx, express, and cors.

Then set up Prisma with a User model (id, email, passwordHash, createdAt, updatedAt) and run the initial migration.

Cursor's agent mode works differently: instead of a single prompt, you chat in a panel while it controls the terminal. I gave a similar initial prompt but in natural language. Cursor reacted by opening the integrated terminal and running npm init -y, then npm install commands one by one. It also created files by sending keystrokes to the editor.

Both tools created the project structure within 2 minutes. Claude Code produced files faster because it writes them directly, while Cursor's agent simulates keystrokes, which is slightly slower but feels more like a human pairing.

Phase 2: Authentication endpoints§

Next, I asked each to implement JWT-based authentication with signup and login endpoints. This required bcrypt for password hashing and jsonwebtoken for tokens. I also wanted refresh token logic.

Claude Code created the files in parallel: middleware, controllers, routes. It also executed npm install bcrypt jsonwebtoken automatically. When I ran npx tsx src/index.ts to test, it failed due to missing tsconfig.json — Claude Code immediately noticed the error, fixed the config, and re-ran. Total time: 8 minutes.

Cursor's agent took a more iterative approach. It wrote one file at a time, then asked "Should I install bcrypt?" before running the command. It also opened the terminal and ran npx tsx src/index.ts, but when it failed, it needed a manual prompt to fix the tsconfig. Total time: 12 minutes.

Phase 3: Stripe integration§

This was the trickiest part. I needed a webhook endpoint to handle checkout.session.completed events, plus a service to create Stripe Checkout Sessions. The Stripe signature verification requires raw body parsing, which conflicts with Express's JSON body parser.

Claude Code handled this elegantly: it created a middleware to conditionally parse raw body for the webhook route, and even installed stripe package. It also wrote a simple test script using curl to simulate a webhook call. The only hiccup: it forgot to add the webhook secret to a .env file, so I had to remind it.

Cursor's agent struggled here. It correctly set up the Stripe service and webhook handler, but it tried to use express.json() globally, which breaks Stripe's signature verification. When I pointed out the bug, it suggested using body-parser with a manual check on the route, but then it wrote the condition incorrectly (checking req.originalUrl instead of a regex). I had to give two corrective prompts before it worked. This took 18 minutes vs 10 for Claude Code.

Code Samples / Prompts Used§

Here's the actual prompt I used for the Stripe webhook endpoint in Claude Code:

Create a Stripe webhook endpoint at POST /api/webhook that:
- Only accepts requests from Stripe (verify signature using stripe.webhooks.constructEvent)
- Handles checkout.session.completed events
- On completion, update the user's subscription status in the database (User model has subscriptionStatus field)
- Returns 200 OK to Stripe quickly

Important: Stripe requires raw request body. The middleware should conditionally use express.raw() for this route only, not express.json().

Also create a StripeCheckoutService with a method createCheckoutSession(userId, priceId) that returns the session URL.

Claude Code generated this:

// src/middleware/stripeWebhook.ts
import express from 'express';
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2024-11-20.acacia',
});

export const stripeWebhook = express.raw({ type: 'application/json' });

export const handleWebhook = async (req: express.Request, res: express.Response) => {
  const sig = req.headers['stripe-signature'] as string;
  let event: Stripe.Event;
  try {
    event = stripe.webhooks.constructEvent(req.body, sig, process.env.STRIPE_WEBHOOK_SECRET!);
  } catch (err) {
    console.error('Stripe webhook verification failed', err);
    return res.status(400).send(`Webhook Error: ${(err as Error).message}`);
  }

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object as Stripe.Checkout.Session;
    const userId = session.metadata?.userId;
    if (userId) {
      // Update user subscription
      await prisma.user.update({
        where: { id: userId },
        data: { subscriptionStatus: 'active' },
      });
    }
  }

  res.json({ received: true });
};

What Worked Well§

Claude Code: Its ability to plan and execute multiple steps autonomously was impressive. It rarely needed mid-task corrections. It also handled the Stripe webhook signature verification flawlessly on the first try. The parallel file writing significantly reduced project bootstrap time.

Cursor's terminal agent: The chat-based interface made it easier to inject feedback mid-stream. When I saw a bug, I could simply say "fix that" and it would immediately adjust. This made debugging sessions more interactive. Also, Cursor's agent is less likely to clobber files without permission — it asks before overwriting, which is safer for existing projects.

Both: They correctly identified when to run terminal commands (install packages, run migrations, start servers) and could parse error outputs to self-correct. Neither required me to copy-paste error messages — they read them directly from the terminal.

What Failed and Why§

Cursor's agent on Stripe integration: As mentioned, it repeatedly used global express.json() despite explicit instructions. This is a known issue: Cursor's agent sometimes gets stuck in a local optimum and ignores context from earlier in the conversation. I had to reset the chat thread twice.

Claude Code on Docker Compose: When I asked Claude Code to create a Docker Compose setup with PostgreSQL and Redis, it generated the file correctly but then tried to run docker-compose up -d and forgot to check if Docker was running. The command failed silently (no error output captured), so Claude Code assumed success and moved on. Later, when the app failed to connect to the database, Claude Code spent 10 minutes debugging connection issues before I noticed Docker wasn't running. This was a classic case of insufficient error checking.

Both: Neither tool could handle long-running tasks gracefully. When I asked either to run a test suite that took 2 minutes, they would periodically check output but sometimes get distracted. For Claude Code, it once mixed up terminal output from different commands, leading to a false positive on a failing test.

Results and Takeaways§

AspectClaude CodeCursor Terminal Agent
Total build time55 min1h 15 min
Terminal commands executed3441
Manual interventions37
Files created2221
Code qualityExcellent (Stripe logic perfect)Good (needed 2 corrections)

Claude Code is faster for greenfield projects with clear specs. Its autonomous mode reduces back-and-forth, but you need to trust its file writes and verify Docker/services are running.

Cursor's agent is better for debugging and iterative development. The chat-based interaction allows for real-time course correction, and it's safer for projects with existing code.

Key Takeaways:

  • Claude Code excels at autonomous multi-step workflows: scaffold, install, migrate, test — all in one go. But always verify service health after it runs Docker commands.
  • Cursor's terminal agent provides safer, interactive pair programming: it asks before overwriting and accepts mid-stream corrections easily. However, it can be overly chatty and slow on complex integrations.
  • For Stripe/Docker integrations, neither is perfect. Claude Code nails the code logic but may skip validation; Cursor needs hand-holding for non-standard middleware setup.
  • Always watch the terminal during long-running commands. Both tools can misinterpret output or miss errors when commands complete quickly.

If you're starting a new project from scratch, use Claude Code for the initial scaffold and then switch to Cursor for iterative development. If you're maintaining an existing codebase, Cursor's agent is safer.

Try It Yourself§

Ready to benchmark them on your own project? Here's how to get started:

1. Install Claude Code CLI: npm install -g @anthropic-ai/claude-code 2. For Cursor, enable "Terminal Agent" in settings (requires Cursor v0.45+) 3. Pick a well-defined mini-project (e.g., a simple CRUD API or CLI tool) 4. Use the same initial prompt for both — include explicit terminal commands they should run 5. Time the sessions and count manual interventions 6. Share your results on llmdb.app or tweet at me!

I'd love to hear how these tools perform for you. Especially if you tackle complex integrations (OAuth, cloud SDKs, etc.) — that's where the real differences emerge.