Iterate mode for agents

Feature request for product/service

Cursor IDE

Describe the request

I provide a simple task to the AI agent to do in a large workspace, it does 5% of the work, I want an iterate mode that does it once, creates branch or commit, does it again, create branch or commit, does it again, and so on

the use case I’m requesting this feature for is this command


description: Reusability Rules
globs: “**/*.{ts,tsx,js,jsx}”
alwaysApply: true

Reusability Rules

While converting a component to a reusable component, props must not include dynamic code, like classNames, components, functions, icons, etc… in such case, create variants or a structured typescript interface that builds what’s the code was intended to do.

Data-Driven Rendering

Never duplicate components that are based on a list or array of data. Always use array methods (.map(), .filter(), etc.) to render components from data structures instead of manually duplicating JSX elements.

Bad Example:

<div>
  <Card>Item 1</Card>
  <Card>Item 2</Card>
  <Card>Item 3</Card>
</div>

Good Example:

const items = [
  { id: 1, name: "Item 1" },
  { id: 2, name: "Item 2" },
  { id: 3, name: "Item 3" },
];

<div>
  {items.map((item) => (
    <Card key={item.id}>{item.name}</Card>
  ))}
</div>;

This approach ensures:

  • Single source of truth for data
  • Easier maintenance and updates
  • Consistent rendering patterns
  • Better performance through proper key usage
  • Reduced code duplication

Data Storage Location

Always store arrays, configuration data, and static data structures in the components/config directory (or components/config/nodes/ for domain-specific configs). Never define data arrays directly in component files.

Bad Example:

// app/pages/list/page.tsx
export default function ListPage() {
  const items = [
    { link: "/accounts", name: "Accounts", description: "..." },
    { link: "/companies", name: "Companies", description: "..." },
  ];

  return <div>{items.map(...)}</div>;
}

Good Example:

// components/config/list-items.config.ts
export interface ListItem {
  link: string;
  name: string;
  description: string;
}

export const LIST_ITEMS: ListItem[] = [
  { link: "/accounts", name: "Accounts", description: "..." },
  { link: "/companies", name: "Companies", description: "..." },
];

// app/pages/list/page.tsx
import { LIST_ITEMS } from "@/components/config/list-items.config";

export default function ListPage() {
  return <div>{LIST_ITEMS.map(...)}</div>;
}

Benefits:

  • Centralized data management
  • Reusable across multiple components
  • Easier to maintain and update
  • Clear separation of concerns
  • Better testability

Code Refactoring Guidelines

When refactoring code, follow these principles to improve maintainability, reduce duplication, and enhance reusability:

1. Delete Unused Code

Identify and remove:

  • Unused imports, variables, functions, and components
  • Commented-out code blocks
  • Dead code paths that are never executed
  • Unused type definitions and interfaces
  • Unused configuration values

Using the Unused Exports Script:

Before refactoring or cleaning up code, use the automated script to identify unused exports:

pnpm run find-unused-exports

This script:

  • Analyzes your TypeScript project using ts-unused-exports
  • Generates a report file unused-exports.txt in the project root
  • Lists all unused exports with their file paths and line numbers
  • Excludes paths configured in scripts/unused-exports.ts (e.g., components/ui)

Report Format:

  • File paths with unused exports
  • Export names and their locations (line:character)
  • Total count of unused exports
  • Completely unused files (if found)

Before removing code, verify:

  • Code is not referenced in other files (use search/grep or check the unused-exports report)
  • Code is not used dynamically or via string references
  • Code is not part of a public API that external code might use
  • Review the unused-exports.txt report to identify safe-to-remove exports

2. Convert Duplicate or Similar Code to Reusable Components/Interfaces

When you find duplicate or similar code:

  • Extract common patterns into reusable components
  • Create interfaces/types for shared data structures
  • Use composition to build variations from base components
  • Implement variant patterns (using libraries like class-variance-authority or cva) instead of duplicating similar components

Process:

  1. Identify the commonalities between duplicated code
  2. Extract the shared logic/structure into a reusable component/interface
  3. Parameterize the differences as props/interface properties
  4. Replace all duplicates with the new reusable version
  5. Verify functionality remains unchanged

Example - Duplicate Components:

// Bad: Duplicate components
function UserCardA({ name, email }) {
  return (
    <Card>
      <h3>{name}</h3>
      <p>{email}</p>
    </Card>
  );
}

function UserCardB({ name, email }) {
  return (
    <Card>
      <h3>{name}</h3>
      <p>{email}</p>
    </Card>
  );
}

// Good: Single reusable component
interface UserCardProps {
  name: string;
  email: string;
}

function UserCard({ name, email }: UserCardProps) {
  return (
    <Card>
      <h3>{name}</h3>
      <p>{email}</p>
    </Card>
  );
}

3. Split Complex Components/Interfaces into Multiple Reusable Components/Interfaces

When to split:

  • Component has multiple responsibilities (violates Single Responsibility Principle)
  • Component is difficult to understand or test
  • Component has grown too large (>300 lines is a common indicator)
  • Parts of the component could be reused independently
  • Interface has too many optional properties or nested structures

How to split:

  • Extract sub-components that represent distinct UI sections
  • Create smaller, focused interfaces for specific use cases
  • Use composition to combine smaller components
  • Extract custom hooks for complex state/logic
  • Split large interfaces into base interfaces with extensions

Example - Splitting Complex Component:

// Bad: Monolithic component
function UserProfile({ user, posts, settings, notifications }) {
  // 200+ lines of mixed concerns
  return <div>{/* User info, posts, settings, notifications all mixed */}</div>;
}

// Good: Split into focused components
function UserProfile({ user }) {
  return (
    <div>
      <UserHeader user={user} />
      <UserStats user={user} />
      <UserPosts userId={user.id} />
    </div>
  );
}

function UserHeader({ user }: { user: User }) {
  return <div>{/* User header content */}</div>;
}

function UserStats({ user }: { user: User }) {
  return <div>{/* Stats content */}</div>;
}

function UserPosts({ userId }: { userId: string }) {
  return <div>{/* Posts content */}</div>;
}

Example - Splitting Complex Interface:

// Bad: Complex interface with many concerns
interface User {
  id: string;
  name: string;
  email: string;
  profilePicture?: string;
  bio?: string;
  role: string;
  permissions: string[];
  address?: {
    street: string;
    city: string;
    country: string;
  };
  preferences?: {
    theme: string;
    notifications: boolean;
  };
  metadata?: Record<string, unknown>;
}

// Good: Split into focused interfaces
interface BaseUser {
  id: string;
  name: string;
  email: string;
}

interface UserProfile extends BaseUser {
  profilePicture?: string;
  bio?: string;
}

interface UserAddress {
  street: string;
  city: string;
  country: string;
}

interface UserPreferences {
  theme: string;
  notifications: boolean;
}

interface User extends UserProfile {
  role: string;
  permissions: string[];
  address?: UserAddress;
  preferences?: UserPreferences;
  metadata?: Record<string, unknown>;
}

4. Combine Similar Reusable Components/Interfaces into One Reusable Component/Interface

When to combine:

  • Components share most of their implementation (>70% overlap)
  • Components only differ by a few props or variant styles
  • Maintaining separate components creates maintenance burden
  • Interfaces have significant overlap and could share a base

How to combine:

  • Use variant props or discriminated unions for differences
  • Make optional props for features that only some variants need
  • Use composition patterns where appropriate
  • Create base interfaces and extend them for specific cases
  • Use conditional rendering based on props rather than separate components

Example - Combining Similar Components:

// Bad: Separate components with minor differences
function PrimaryButton({ children, onClick }) {
  return (
    <button className="bg-blue-500" onClick={onClick}>
      {children}
    </button>
  );
}

function SecondaryButton({ children, onClick }) {
  return (
    <button className="bg-gray-500" onClick={onClick}>
      {children}
    </button>
  );
}

function DangerButton({ children, onClick }) {
  return (
    <button className="bg-red-500" onClick={onClick}>
      {children}
    </button>
  );
}

// Good: Single component with variants
interface ButtonProps {
  variant?: "primary" | "secondary" | "danger";
  children: React.ReactNode;
  onClick: () => void;
}

const buttonVariants = {
  primary: "bg-blue-500",
  secondary: "bg-gray-500",
  danger: "bg-red-500",
};

function Button({ variant = "primary", children, onClick }: ButtonProps) {
  return (
    <button className={buttonVariants[variant]} onClick={onClick}>
      {children}
    </button>
  );
}

Example - Combining Similar Interfaces:

// Bad: Separate interfaces with overlap
interface CreateUserRequest {
  name: string;
  email: string;
  password: string;
}

interface UpdateUserRequest {
  id: string;
  name?: string;
  email?: string;
  password?: string;
}

// Good: Combined with discriminated union or base interface
interface BaseUserRequest {
  name?: string;
  email?: string;
  password?: string;
}

interface CreateUserRequest extends Omit<BaseUserRequest, "name" | "email"> {
  name: string;
  email: string;
  password: string;
}

interface UpdateUserRequest extends BaseUserRequest {
  id: string;
}

// Or using discriminated union:
type UserRequest =
  | ({ type: "create" } & CreateUserRequest)
  | ({ type: "update" } & UpdateUserRequest);

Refactoring Checklist

Before refactoring:

  • Identify all usages of code to be refactored
  • Understand the full context and dependencies
  • Ensure tests exist or write tests before refactoring
  • Plan the refactoring approach

During refactoring:

  • Make one change at a time
  • Keep the code functional at each step
  • Update all references to the refactored code
  • Maintain backward compatibility if it’s a public API
  • Update types/interfaces consistently

After refactoring:

  • Verify all functionality works as before
  • Run tests to ensure nothing broke
  • Check for any new linting errors
  • Remove unused code that resulted from refactoring
  • Run pnpm run find-unused-exports to verify dead code was removed
  • Update documentation if needed