All Articles

How I use AI tools like Cursor and Warp in my daily workflow

When tools understand intent and context, code becomes conversation.

Before these AI editors everyone of us used to spend a lot of our mental bandwidth and context‐switching:

  • Jumping from editor → browser → terminal → Slack → back to editor.
  • Googling library functions, reading docs, switching tabs.
  • Writing shell commands, debugging, waiting for builds.

It felt like half my day was just wiring plumbing not building.

When I discovered Cursor and Warp, I didn’t expect magic, but I found a streamlined, conversational flow between idea → code → terminal → test. Over time, I tuned them to my habits and guardrails so they feel like extensions of my brain.

For the last few months, I’ve leaned on two AI-assisted tools every single day:

  • Cursor for editor-native help with reading, refactoring, generating, and explaining code.
  • Warp for terminal-native help with shell commands, explanations, and quick one-off scripts.

This post shares concrete ways I use them, with prompts, examples, and image callouts you can mirror in your own workflow.

Why these two together?

Editor AI excels at code-aware work (AST/context, diffs, tests) while terminal AI shines for command discovery, explanations, and guardrails around risky shell operations. Together, they speed up the path from idea -> code -> verified result.

Cursor: Editor-as-pair programmer

Start small / incremental tasks

I feed Cursor minimal, tightly scoped prompts (often just one function or small module). This matches with my rule.

Keep tasks small

Keep context tight and relevant and once a task is done, either reset the context or start a new chat. This helps contain “hallucination drift” and makes verification easier.

Ideation in my head first

Before asking Cursor for code, I imagine the solution: data shapes, algorithm approach, API design. Then I send a prompt describing what I intend. If Cursor’s suggestion aligns, I accept; otherwise, I tweak the prompt.

For example, I once knew I needed an IN query in SQL to fetch a set of IDs efficiently and avoid expensive joins across tables. Cursor first gave me a join-based solution. I rejected it and refined the prompt:

Use IN on table A’s id list; avoid additional joins; show just the values.

I rarely switch to a browser or full docs. If I need to look up a library function (say go Chan or RxJS combineLatest), I open Cursor’s sidebar and prompt it

Refactor

Cursor supports rewriting or refactoring multiple lines of code at once via natural language transformations. For instance:

I avoid giant “rewrite this file” prompts and instead give very specific goals. Cursor’s inline Chat and “Show Diff” flow make this safe and reversible.

Prompt I used:

Refactor UsersController#create to:
- move business logic to a service object Users::Creator
- return explicit success/failure objects
- keep controller thin and deterministic
- do NOT change the public API (params, response shape, status codes)
Also propose minimal RSpec changes.

Before (controller):

class UsersController < ApplicationController
  def create
    user = User.new(user_params)
    if params[:invite_code] && Invite.valid?(params[:invite_code])
      user.role = :invited
      Analytics.track("invited_user_created", user_id: user.id)
    end

    if user.save
      Mailer.welcome(user.id).deliver_later
      render json: { id: user.id, email: user.email }, status: :created
    else
      render json: { errors: user.errors.full_messages }, status: :unprocessable_entity
    end
  end

  private
  def user_params
    params.require(:user).permit(:email, :password)
  end
end

After (service + slimmer controller):

# app/services/users/creator.rb
module Users
  class Creator
    Result = Struct.new(:ok?, :user, :errors, keyword_init: true)

    def initialize(params:, invite_code: nil)
      @params = params
      @invite_code = invite_code
    end

    def call
      user = User.new(@params)
      apply_invite!(user)
      return Result.new(ok?: false, errors: user.errors.full_messages) unless user.save

      Mailer.welcome(user.id).deliver_later
      Result.new(ok?: true, user: user)
    end

    private
    def apply_invite!(user)
      return unless @invite_code
      return unless Invite.valid?(@invite_code)
      user.role = :invited
      Analytics.track("invited_user_created", user_id: user.id)
    end
  end
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def create
    result = Users::Creator.new(
      params: user_params,
      invite_code: params[:invite_code]
    ).call

    if result.ok?
      render json: { id: result.user.id, email: result.user.email }, status: :created
    else
      render json: { errors: result.errors }, status: :unprocessable_entity
    end
  end

  private
  def user_params
    params.require(:user).permit(:email, :password)
  end
end

Testing & boilerplate generation

Writing test scaffolding used to be tedious. Now, I often prompt Cursor:

Prompt:

Write RSpec request specs for UsersController#create covering:
- valid user without invite
- valid user with invite (role: invited, analytics event fired)
- invalid params path (collect error messages)
Prefer fast, deterministic examples; stub external calls.

Resulting spec (edited by me):

# spec/requests/users_spec.rb
require "rails_helper"

RSpec.describe "Users", type: :request do
  describe "POST /users" do
    let(:params) { { user: { email: "a@b.com", password: "secret123" } } }

    it "creates without invite" do
      expect(Mailer).to receive_message_chain(:welcome, :deliver_later)
      post "/users", params: params
      expect(response).to have_http_status(:created)
      body = JSON.parse(response.body)
      expect(body["email"]).to eq("a@b.com")
    end

    it "creates with invite" do
      allow(Invite).to receive(:valid?).and_return(true)
      expect(Analytics).to receive(:track).with("invited_user_created", hash_including(:user_id))
      expect(Mailer).to receive_message_chain(:welcome, :deliver_later)

      post "/users", params: params.merge(invite_code: "XYZ")
      expect(response).to have_http_status(:created)
    end

    it "fails with invalid params" do
      post "/users", params: { user: { email: "" } }
      expect(response).to have_http_status(:unprocessable_entity)
      body = JSON.parse(response.body)
      expect(body["errors"]).to be_an(Array)
    end
  end
end

Cursor Rules

One of the most powerful features I use is Cursor rules. These rules help you set boundaries and guide the AI, so it aligns with your coding style and workflow habits.

We can create Team Specific, User Specific, Project Specific and local nested rules. Project rules live in root .cursor/rules . Each rule is a file and version-controlled. For example, this is how my rails project rule files look like

  You are an expert in Ruby on Rails, PostgreSQL

  Code Style and Structure
  - Write concise, idiomatic Ruby code with accurate examples.
  - Follow Rails conventions and best practices.
  - Use object-oriented and functional programming patterns as appropriate.
  - Prefer iteration and modularization over code duplication.
  - Use descriptive variable and method names (e.g., user_signed_in?, calculate_total).
  - Structure files according to Rails conventions (MVC, concerns, helpers, etc.).
  
  Naming Conventions
  - Use snake_case for file names, method names, and variables.
  - Use CamelCase for class and module names.
  - Follow Rails naming conventions for models, controllers, and views.

  Syntax and Formatting
  - Follow the Ruby Style Guide (https://rubystyle.guide/)
  - Use Ruby's expressive syntax (e.g., unless, ||=, &.)
  - Prefer single quotes for strings unless interpolation is needed.
  
  Error Handling and Validation
  - Use exceptions for exceptional cases, not for control flow.
  - Implement proper error logging and user-friendly messages.
  - Use ActiveModel validations in models.
  - Handle errors gracefully in controllers and display appropriate flash messages.
  
  Testing
  - Write comprehensive tests using RSpec or Minitest.
  - Follow TDD/BDD practices.
  - Use factories (FactoryBot) for test data generation.
  
  Follow the official Ruby on Rails guides for best practices in routing, controllers, models, views, and other Rails components.

Similarly, you can organize nested rules by placing them in .cursor/rules directories throughout your project. Nested rules automatically attach when files in their directory are referenced.

alt

You can also download the rules from Community-contributed rules directory cursor.directory where you can find the pre-defined rules templates for all the languages.

Keep rules short(<500 lines), specific, and reusable, with clear examples references.

Cursor Diffs

When cursor generates code changes, they’re presented in a review interface that shows additions and deletions with color-coded lines. This allows you to examine and control which changes are applied to your codebase. After generation completes, you’ll see a prompt to review all changes before proceeding. This gives you an overview of what will be modified.

alt

Warp: AI in the terminal

While Cursor handles the “code realm”, Warp handles the “command line / shell / deployment”, but in a more intelligent, interactive way. Warp is a modern terminal emulator with integrated AI features into the CLI. It helps me move faster while staying safe, especially around commands that could be destructive.

Agent Mode / Natural Language Prompts

Warp lets you type in plain English instead of remembering complex command syntax. It automatically detects whether you’re giving a command or describing a task. You can ask it to do things like “delete merged branches”, or “download the docker image” and Warp will interpret your intent, suggest the right commands, or even run them for you making your terminal experience faster, smarter, and more natural.

Prompt (in Warp):

alt

Preview safely:

alt

Auto Switch from Shell to Agent mode

Makes prompts more powerful (you can attach related files, see Git status, etc.). It Helps avoid ambiguity. The auto-detect feature saves you from switching modes manually. Better context improves AI suggestions and reduces errors.

alt

Blocks: Grouping Input + Output

In Warp, every command and its output are grouped into neat “blocks.” You can scroll through them, collapse or expand, copy, or even share specific blocks with your team. It makes reading and organizing terminal output so much easier no more endless scrolling. You can quickly revisit past commands, isolate errors, and share only what matters. It’s a small change that makes a big difference in how you work.

alt

Warp comes with a powerful command palette, just like modern IDEs. You can quickly search for commands, workflows, settings, or anything from your command history and Warp Drive. It saves tons of time since you don’t need to remember exact commands, just search and run.

alt

When procrastination meets Warp

So, this blog I built with Gatsby around 5 years ago. It was using Gatsby 4 with React 17. Every time I thought about updating it, I’d open the codebase, stare at the mess, and quietly close it again. Classic procrastination 😁 .

Finally gave in and decided to let warp.dev handle it, and honestly, I wasn’t expecting much. But to my surprise, it upgraded everything to the latest Gatsby and React versions in like 20 minutes. Fixing errors one by one, Clean, smooth, no hassle. Kinda impressed, honestly, productivity is at its peak.

alt

Guardrails and habits

  • Never paste secrets into prompts. Treat the editor/terminal as you would your code: secrets belong in env vars or secret managers.
  • Verify everything. AI outputs are drafts, not facts.
  • Keep changes small and reviewable. Prefer diffs over bulk rewrites.
  • Pin versions and codify results (scripts, tests, docs) so you aren’t re-asking the same things.

My Cursor subscription is covered by the organization, while I personally pay for Warp at USD 18 per month. This setup lets me balance organizational tools with my own preferred workflow, ensuring I can stay efficient and customize my environment the way I like.

Cursor and Warp feel like a calm, one sits in the code and one sits in the terminal. They won’t replace ownership or judgment, but they will shorten the distance between intent and impact. Using these tools has transformed how I think, code, and debug. They compress my mental loop, reduce friction, and let me focus on architecture, design, and problem solving rather than syntactic plumbing.