Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions apps/marketing/content/blog/history-of-git-worktrees.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
title: The History of Git Worktrees
description: How git worktrees evolved from a niche feature to the foundation of modern parallel development workflows.
author: Avi Peltz
date: 2025-01-18
category: Research
---

Git worktrees have become an essential tool for developers managing multiple tasks simultaneously. But where did they come from, and why are they suddenly so relevant?

![Git worktrees visualization](/blog/history-of-git-worktrees/worktrees-visualization.png)

## The Early Days

Git worktrees were introduced in Git 2.5 (July 2015) as a way to have multiple working directories attached to a single repository. Before worktrees, developers had two options for working on multiple branches simultaneously:

1. **Stash and switch** - Constantly stashing changes and switching branches
2. **Multiple clones** - Maintaining separate clones of the same repository

Both approaches had significant drawbacks. Stashing was error-prone and disruptive. Multiple clones wasted disk space and made it difficult to share local commits between workspaces.

## How Worktrees Work

A git worktree creates a new working directory that shares the same `.git` directory as your main repository. This means:

- All branches and commits are instantly available
- No need to push/pull between directories
- Minimal disk space overhead
- Each worktree can have its own branch checked out

```bash
# Create a new worktree
git worktree add ../feature-branch feature/new-feature

# List all worktrees
git worktree list

# Remove a worktree
git worktree remove ../feature-branch
```

## The AI Agent Revolution

With the rise of AI coding agents, worktrees have found a new purpose. When you're running multiple agents in parallel, each one needs:

- Its own working directory
- Isolation from other agents' changes
- Access to the full repository history

Worktrees provide all of this out of the box. That's why Superset uses them as the foundation for parallel agent workspaces.

## Best Practices

When using worktrees for parallel development:

- **Use descriptive paths** - Name your worktree directories after the feature or task
- **Clean up regularly** - Remove worktrees when you're done with them
- **Avoid long-lived worktrees** - Merge or rebase frequently to avoid drift

## Conclusion

What started as a niche feature for advanced Git users has become essential infrastructure for the age of AI-assisted development. As coding agents become more capable, the ability to run them in parallel—powered by git worktrees—will only become more important.
46 changes: 46 additions & 0 deletions apps/marketing/content/blog/introducing-superset.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
title: Introducing Superset
description: Run 10+ parallel coding agents on your machine. A new way to work with AI coding assistants.
author: Avi Peltz
date: 2025-01-15
category: Product
---

We're excited to introduce Superset - a new way to work with AI coding agents.

## The Problem

Modern AI coding assistants are powerful, but they have a fundamental limitation: you can only run one task at a time. When your agent is working on a complex feature, you're stuck waiting. Context switching between different projects means losing your train of thought.

## The Solution

Superset lets you run **10+ parallel coding agents** on your machine. Here's what that means for your workflow:

- **Spin up new tasks** while waiting for your current agent to finish
- **Quickly switch between tasks** as they need your attention
- **Keep your context** across multiple projects and features

## How It Works

Superset uses isolated workspaces powered by git worktrees. Each task runs in its own environment, with its own terminal, its own files, and its own agent context.

![Superset workspace interface](/blog/introducing-superset/workspace-interface.png)

```bash
# Create a new workspace for a feature
superset create --branch feature/auth

# Your agent works in isolation
# Meanwhile, start another task...
superset create --branch feature/dashboard
```

## What's Next

We're just getting started. In the coming weeks, we'll be sharing more about:

- Deep integrations with popular AI coding tools
- Advanced workspace management features
- Team collaboration capabilities

Stay tuned for updates.
144 changes: 144 additions & 0 deletions apps/marketing/content/blog/parallel-agents-guide.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
---
title: A Guide to Parallel Coding Agents
description: Learn how to maximize your productivity by running multiple AI coding agents simultaneously.
author: Avi Peltz
date: 2025-01-20
category: Research
---

Running multiple AI coding agents in parallel can dramatically boost your productivity. Here's how to make the most of it.

## Why Parallel Agents?

Traditional AI coding workflows are sequential. You ask your agent to do something, wait for it to finish, review the results, and move on. But what if you could:

1. Start a refactoring task
2. While that's running, begin a new feature
3. Review completed tasks as they finish
4. Context switch without losing progress

## Setting Up Your Workflow

### Creating Workspaces

Each parallel agent runs in its own isolated workspace. Here's how to create one:

```bash
# Create a new workspace for a feature
superset create --branch feature/auth

# List all active workspaces
superset list

# Switch to a workspace
superset switch feature/auth
```

<Video src="/blog/parallel-agents-guide/parallel-agents-demo.mov" title="Parallel agents in action" />

### Organize by Task Type

Group your parallel workspaces by task type:

- **Feature development** - New functionality
- **Bug fixes** - Quick patches
- **Refactoring** - Code improvements
- **Documentation** - README updates, comments

### Example: React Component

Here's an example of a component you might build in parallel:

```tsx
import { useState, useEffect } from 'react';

interface User {
id: string;
name: string;
email: string;
}

export function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
async function fetchUser() {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data);
setLoading(false);
}
fetchUser();
}, [userId]);

if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;

return (
<div className="p-4 rounded-lg border">
<h2 className="text-xl font-bold">{user.name}</h2>
<p className="text-gray-600">{user.email}</p>
</div>
);
}
```

### API Routes

While one agent works on the frontend, another can build the API:

```typescript
import { db } from '@/lib/db';
import { users } from '@/lib/schema';
import { eq } from 'drizzle-orm';

export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const user = await db
.select()
.from(users)
.where(eq(users.id, params.id))
.limit(1);

if (!user.length) {
return Response.json(
{ error: 'User not found' },
{ status: 404 }
);
}

return Response.json(user[0]);
}
```

## Best Practices

- **Start small** - Begin with 2-3 parallel agents and scale up
- **Keep tasks focused** - Smaller, well-defined tasks work better
- **Use isolation** - Each workspace should be independent
- **Commit frequently** - Save progress in each workspace

### Configuration Example

You can configure Superset with a simple config file:

```json
{
"workspaces": {
"maxParallel": 10,
"defaultBranch": "main",
"autoCommit": true
},
"agents": {
"model": "claude-sonnet-4-20250514",
"maxTokens": 8192
}
}
```

## Conclusion

Parallel coding agents transform how you work with AI. Instead of waiting, you're always making progress on something. Give it a try and see how it changes your workflow.
4 changes: 4 additions & 0 deletions apps/marketing/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,19 @@
"@t3-oss/env-nextjs": "^0.13.8",
"framer-motion": "^12.23.26",
"geist": "^1.5.1",
"gray-matter": "^4.0.3",
"import-in-the-middle": "2.0.1",
"lucide-react": "^0.560.0",
"next": "^16.0.10",
"next-mdx-remote": "^5.0.0",
"next-themes": "^0.4.6",
"posthog-js": "1.310.1",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-fast-marquee": "^1.6.5",
"react-icons": "^5.5.0",
"require-in-the-middle": "8.0.1",
"shiki": "^3.21.0",
"simplex-noise": "^4.0.3",
"stripe-gradient": "^1.0.1",
"three": "^0.181.2",
Expand All @@ -38,6 +41,7 @@
"devDependencies": {
"@superset/typescript": "workspace:*",
"@tailwindcss/postcss": "^4.0.9",
"@tailwindcss/typography": "^0.5.16",
"@types/mdx": "^2.0.13",
"@types/node": "^24.9.1",
"@types/react": "~19.1.0",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
"use client";

import { ArrowLeft } from "lucide-react";
import Link from "next/link";
import type { ReactNode } from "react";
import { AuthorAvatar } from "@/app/blog/components/AuthorAvatar";
import { GridCross } from "@/app/blog/components/GridCross";
import { type BlogPost, formatBlogDate, type TocItem } from "@/lib/blog-utils";

interface BlogPostLayoutProps {
post: BlogPost;
toc: TocItem[];
children: ReactNode;
}

export function BlogPostLayout({ post, children }: BlogPostLayoutProps) {
const formattedDate = formatBlogDate(post.date);

return (
<article className="relative min-h-screen">
{/* Grid background with dashed lines */}
<div
className="absolute inset-0 pointer-events-none"
style={{
backgroundImage: `
linear-gradient(to right, transparent 0%, transparent calc(50% - 384px), rgba(255,255,255,0.06) calc(50% - 384px), rgba(255,255,255,0.06) calc(50% - 383px), transparent calc(50% - 383px), transparent calc(50% + 383px), rgba(255,255,255,0.06) calc(50% + 383px), rgba(255,255,255,0.06) calc(50% + 384px), transparent calc(50% + 384px))
`,
}}
/>

{/* Hero header */}
<header className="relative border-b border-border">
<div className="max-w-3xl mx-auto px-6 pt-16 pb-10 md:pt-20 md:pb-12">
{/* Grid crosses */}
<GridCross className="top-0 left-0" />
<GridCross className="top-0 right-0" />

<div className="text-center">
<span className="text-sm font-mono text-muted-foreground uppercase tracking-wider">
{post.category}
</span>

<h1 className="text-3xl md:text-4xl lg:text-5xl font-medium tracking-tight text-foreground mt-4 mb-4">
{post.title}
</h1>

{post.description && (
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto mb-6">
{post.description}
</p>
)}

<div className="flex items-center justify-center gap-3 text-sm text-muted-foreground">
<AuthorAvatar
name={post.author}
title="Cofounder, Superset"
twitterHandle="avimakesrobots"
/>
Comment on lines +54 to +58
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded author metadata will be incorrect for different authors.

The title and twitterHandle props are hardcoded to "Cofounder, Superset" and "avimakesrobots" respectively, but name uses post.author. If blog posts have different authors, this will display incorrect metadata.

Consider either:

  1. Adding authorTitle and authorTwitter fields to the BlogPost interface and frontmatter
  2. Creating an author lookup map if there's a fixed set of authors
  3. Removing the author details if they can't be reliably sourced
🤖 Prompt for AI Agents
In
`@apps/marketing/src/app/blog/`[slug]/components/BlogPostLayout/BlogPostLayout.tsx
around lines 54 - 58, The AuthorAvatar is receiving hardcoded title and
twitterHandle while name uses post.author, causing mismatched metadata; update
the data model and JSX to pass real author metadata: add authorTitle and
authorTwitter fields to the BlogPost frontmatter/interface (and to whatever
parser builds post), populate them in existing posts or provide sensible
fallbacks, then replace the hardcoded strings in the BlogPostLayout component by
passing post.authorTitle and post.authorTwitter into AuthorAvatar (or, if you
prefer a fixed set, implement an author lookup map keyed by post.author and use
that to supply title and twitterHandle; alternatively remove those props if no
reliable source exists).

<span className="text-foreground/70">{post.author}</span>
<span className="text-muted-foreground/50">·</span>
<time dateTime={post.date}>{formattedDate}</time>
</div>
</div>
</div>

{/* Bottom crosses */}
<div className="max-w-3xl mx-auto px-6 relative">
<GridCross className="bottom-0 left-0" />
<GridCross className="bottom-0 right-0" />
</div>
</header>

{/* Back link section */}
<div className="relative border-b border-border">
<div className="max-w-3xl mx-auto px-6 py-4">
<Link
href="/blog"
className="inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
>
<ArrowLeft className="h-4 w-4" />
Back to Blog
</Link>
</div>
</div>

{/* Content */}
<div className="relative max-w-3xl mx-auto px-6 py-12">
<div className="prose max-w-none">{children}</div>
</div>

{/* Footer */}
<footer className="relative border-t border-border">
<div className="max-w-3xl mx-auto px-6 relative">
<GridCross className="top-0 left-0" />
<GridCross className="top-0 right-0" />
</div>
<div className="max-w-3xl mx-auto px-6 py-10">
<Link
href="/blog"
className="inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
>
<ArrowLeft className="h-4 w-4" />
All posts
</Link>
</div>
</footer>
</article>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { BlogPostLayout } from "./BlogPostLayout";
Loading