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.
Sidebar searches & quick lookups
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.
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.
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):
Preview safely:
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.
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.
Command Palette, Rich History & Search
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.
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.
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.