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
66 changes: 66 additions & 0 deletions .claude/agents/project-structure-validator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
name: project-structure-validator
description: Validates project structure against co-location and architecture patterns defined in AGENTS.md
color: blue
---

You are a project structure validator for the Superset monorepo.

## Speed Optimization

**ALWAYS** build component graph first:
```bash
bash .claude/agents/project-structure-validator/build-component-graph.sh [directory]
cat .claude/agents/project-structure-validator/.component-graph.json
```

This avoids slow grep operations for import counting.

## Analysis Approach

**1. Find components** (fast):
```bash
find [directory] -name "*.tsx" -type f ! -name "*.test.tsx" ! -name "*.stories.tsx"
```

**2. Count imports** (use graph, else grep):
```bash
grep -r "from.*ComponentName" [directory] --include="*.tsx" --include="*.ts" | wc -l
```

**3. Multi-component check**:
```bash
grep -c "^export function\|^export const.*=>" File.tsx
```

## Rules from AGENTS.md

1. Used once → nest under parent's `components/`
2. Used 2+ → promote to shared parent's `components/`
3. One component per file
4. Co-locate utils/hooks/constants/tests/stories

## Output Format (CONCISE)

```markdown
## Summary
Score: [%] | [N] components | [N] violations

## Critical Issues
[VIOLATION] Component at wrong location (used Nx, at Y)
Fix: mv X Y

## Metrics
- Components: [N], avg depth [N]
- Violations: [N] location, [N] multi-component

## Performance Analysis
- Tool calls: [N] ([breakdown])
- Slowest: [operation] ([reason])
- Used component graph: [yes/no]
- Optimization: [suggestion]
```

## Self-Improvement

At end of report, suggest modifications to THIS file (.claude/agents/project-structure-validator.md) that would make you faster/better.
2 changes: 2 additions & 0 deletions .claude/agents/project-structure-validator/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Generated component dependency graph
.component-graph.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash
# Builds component dependency graph for fast lookup
# Output: .claude/agents/project-structure-validator/.component-graph.json

DIR="${1:-.}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OUTPUT="$SCRIPT_DIR/.component-graph.json"

echo "{" > "$OUTPUT"
echo ' "components": {' >> "$OUTPUT"

first=true
find "$DIR" -name "*.tsx" -type f ! -path "*/node_modules/*" ! -name "*.test.tsx" ! -name "*.stories.tsx" | while read file; do
component=$(basename "$file" .tsx)
path="${file#$DIR/}"

# Count imports
count=$(grep -r "from.*['\"].*$component['\"]" "$DIR" --include="*.tsx" --include="*.ts" 2>/dev/null | grep -v "$file" | wc -l | tr -d ' ')

# Get importers
importers=$(grep -l "from.*['\"].*$component['\"]" "$DIR" --include="*.tsx" --include="*.ts" -r 2>/dev/null | grep -v "$file" | sed "s|^$DIR/||" | paste -sd "," -)

if [ "$first" = true ]; then
first=false
else
echo "," >> "$OUTPUT"
fi

echo -n " \"$path\": {\"component\": \"$component\", \"imports\": $count, \"importers\": [" >> "$OUTPUT"
if [ -n "$importers" ]; then
echo "$importers" | sed 's/,/","/g' | sed 's/^/"/' | sed 's/$/"/' | tr -d '\n' >> "$OUTPUT"
fi
echo -n "]}" >> "$OUTPUT"
done

echo "" >> "$OUTPUT"
echo ' },' >> "$OUTPUT"
echo " \"generated\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"" >> "$OUTPUT"
echo "}" >> "$OUTPUT"

echo "Built component graph: $OUTPUT"
72 changes: 58 additions & 14 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ Bun + Turbo monorepo with:
- `apps/docs` - Documentation site
- `apps/blog` - Blog site
- **Packages**:
- `packages/ui` - Shared UI components (shadcn/ui + TailwindCSS v4)
- `packages/ui` - Shared UI components (shadcn/ui + TailwindCSS v4).
- Add components: `npx shadcn@latest add <component>` (run in `packages/ui/`)
- `packages/db` - Drizzle ORM database schema
- `packages/constants` - Shared constants
- `packages/models` - Shared data models
Expand Down Expand Up @@ -52,16 +53,6 @@ bun run clean # Clean root node_modules
bun run clean:workspaces # Clean all workspace node_modules
```

## UI Components

All components in `packages/ui`:
- **Import**: `@superset/ui/button`, `@superset/ui/input`, etc.
- **Icons**: `@superset/ui/icons`
- **Utils**: `@superset/ui/utils`
- **Hooks**: `@superset/ui/hooks`
- **Styles**: `@superset/ui/globals.css`
- **Add shadcn component**: `npx shadcn@latest add <component>` (run in `packages/ui/`)

## Code Quality

**Biome runs at root level** (not per-package) for speed:
Expand All @@ -74,9 +65,62 @@ All components in `packages/ui`:

1. **Keep diffs minimal** - targeted edits only
2. **Follow existing patterns** - match the codebase style
5. **Type safety** - avoid `any` unless necessary
6. **Don't run dev servers** in automation
7. **Search narrowly** - avoid reading large files/assets
3. **Type safety** - avoid `any` unless necessary
4. **Don't run dev servers** in automation
5. **Search narrowly** - avoid reading large files/assets

## Project Structure

All projects in this repo should be structured like this:

```
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

Add language identifier to fenced code block.

The code block is missing a language specifier, which violates MD040 and impacts syntax highlighting. Update the opening fence to include text or bash.

-```
+```text
 app/
 ├── page.tsx
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

76-76: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In AGENTS.md around line 76, the fenced code block opening fence is missing a
language identifier (violates MD040); update the opening fence to include a
language such as text or bash (e.g., change ``` to ```text) so the block becomes
a proper fenced code block with syntax highlighting.

app/
├── page.tsx
Comment on lines +76 to +78
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

Fix missing language identifier in fenced code block (MD040).

The opening fence at line 76 is missing a language specifier, which violates MD040. This was previously flagged and remains unresolved. Update the opening fence to include text:

-```
+```text
 app/
 ├── page.tsx
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

76-76: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In AGENTS.md around lines 76 to 78, the fenced code block opening fence is
missing a language identifier (MD040); update the opening fence to include the
language specifier "text" so the block starts with ```text and the listed tree
lines remain unchanged, then save the file.

├── dashboard/
│ ├── page.tsx
│ └── components/
│ └── MetricsChart/
│ ├── MetricsChart.tsx
│ ├── MetricsChart.test.tsx # Tests co-located
│ ├── index.ts
│ ├── useMetricsData.ts # Hook used only here
│ └── constants.ts
└── components/
├── Sidebar/
│ ├── Sidebar.tsx
│ ├── Sidebar.test.tsx # Tests co-located
│ ├── index.ts
│ ├── components/ # Used 2+ times IN Sidebar
│ │ └── SidebarButton/ # Shared by SidebarNav + SidebarFooter
│ │ ├── SidebarButton.tsx
│ │ ├── SidebarButton.test.tsx
│ │ └── index.ts
│ ├── SidebarNav/
│ │ ├── SidebarNav.tsx
│ │ └── index.ts
│ └── SidebarFooter/
│ ├── SidebarFooter.tsx
│ └── index.ts
└── HeroSection/
├── HeroSection.tsx
├── HeroSection.test.tsx # Tests co-located
├── index.ts
└── components/ # Used ONLY by HeroSection
└── HeroCanvas/
├── HeroCanvas.tsx
├── HeroCanvas.test.tsx
├── HeroCanvas.stories.tsx
├── index.ts
└── config.ts

components/ # Used in 2+ pages (last resort)
└── Header/
```

1. **One folder per component**: `ComponentName/ComponentName.tsx` + `index.ts` for barrel export
2. **Co-locate by usage**: If used once, nest under parent's `components/`. If used 2+ times, promote to **highest shared parent's** `components/` (or `components/` as last resort)
3. **One component per file**: No multi-component files
4. **Co-locate dependencies**: Utils, hooks, constants, config, tests, stories live next to the file using them

## Database Rules

Expand Down
1 change: 1 addition & 0 deletions apps/blog/tsconfig.tsbuildinfo

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { motion } from "framer-motion";
import { CLIENT_LOGOS } from "./constants";

export function ClientLogosSection() {
return (
<section className="py-12 sm:py-16 md:py-24 px-4 sm:px-6 md:px-8 bg-black overflow-hidden">
<div className="max-w-7xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.5, ease: "easeOut" }}
>
<h2 className="text-xl sm:text-2xl font-normal text-center mb-4 sm:mb-8 text-white px-4">
Trusted by engineers from
</h2>
</motion.div>

<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.5, delay: 0.2 }}
className="relative"
>
<div className="flex overflow-hidden">
<motion.div
className="flex gap-12 sm:gap-16 md:gap-24"
animate={{
x: [0, -1000],
}}
transition={{
x: {
repeat: Number.POSITIVE_INFINITY,
repeatType: "loop",
duration: 20,
ease: "linear",
},
Comment on lines +28 to +38
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 | 🟠 Major

Fix marquee offset to avoid gaps

Hard-coding x: [0, -1000] makes the marquee drift out of sync with the actual logo width. On wider viewports the strip resets before the next copy lines up, producing a backward jump; on narrower viewports it keeps sliding past the content and leaves blank space before looping. Drive the animation with a relative offset so the second copy is always aligned when the loop restarts.

-							animate={{
-								x: [0, -1000],
-							}}
+							animate={{
+								x: ["0%", "-33.333333%"],
+							}}
 							
 							transition={{
 								x: {
 									repeat: Number.POSITIVE_INFINITY,
 									repeatType: "loop",
 									duration: 20,
 									ease: "linear",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className="flex gap-12 sm:gap-16 md:gap-24"
animate={{
x: [0, -1000],
}}
transition={{
x: {
repeat: Number.POSITIVE_INFINITY,
repeatType: "loop",
duration: 20,
ease: "linear",
},
className="flex gap-12 sm:gap-16 md:gap-24"
animate={{
x: ["0%", "-33.333333%"],
}}
transition={{
x: {
repeat: Number.POSITIVE_INFINITY,
repeatType: "loop",
duration: 20,
ease: "linear",
},

}}
>
{/* Render logos three times for seamless loop */}
{[...Array(3)].map((_, setIndex) => (
<div
key={setIndex}
className="flex gap-12 sm:gap-16 md:gap-24 items-center"
>
{CLIENT_LOGOS.map((client) => (
<div
key={`${setIndex}-${client.name}`}
className="text-white text-lg sm:text-xl md:text-2xl lg:text-3xl font-semibold opacity-60 hover:opacity-100 transition-opacity cursor-pointer whitespace-nowrap"
>
{client.logo}
</div>
))}
</div>
))}
</motion.div>
</div>
</motion.div>
</div>
</section>
);
}
11 changes: 11 additions & 0 deletions apps/website/src/app/components/ClientLogosSection/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const CLIENT_LOGOS = [
{ name: "numbies", logo: "numbies.xyz" },
{ name: "findmymachinist", logo: "findmymachinist.com" },
{ name: "cadra", logo: "Cadra" },
{ name: "onlook", logo: "Onlook" },
{ name: "amazon", logo: "Amazon" },
{ name: "google", logo: "Google" },
{ name: "servicenow", logo: "ServiceNow" },
{ name: "ycombinator", logo: "Y Combinator" },
{ name: "scribe", logo: "Scribe" },
] as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ClientLogosSection } from "./ClientLogosSection";
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { motion } from "framer-motion";

interface FeaturesSectionProps {
onOpenWaitlist: () => void;
}

// Using the same SCALE_FEATURES from ScaleFeaturesSection
const FEATURES = [
{
title: "Work in parallel",
description:
"Run multiple agents in parallel. Build features as quickly as you can come up with them.",
},
{
title: "No downtime",
description:
"Code on the go. Always-on agents that work even when you're away from your laptop.",
},
{
title: "Zero switching cost",
description:
"Be the human in the loop. We handle the port switching and context management so you're never overloaded.",
},
{
title: "Bring your own tools",
description:
"We're a superset of your existing tools, not a replacement. Use your own coding setup, tools, and agents. We bring the tooling and gluing.",
},
] as const;

export function FeaturesSection({ onOpenWaitlist }: FeaturesSectionProps) {
return (
<section className="py-16 sm:py-24 md:py-32 px-4 sm:px-6 md:px-8 bg-black">
<div className="max-w-3xl mx-auto">
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.5 }}
className="text-center mb-16 sm:mb-20"
>
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold text-white mb-4">
Build like a VP of Engineering
</h2>
</motion.div>

<div className="space-y-8 sm:space-y-12">
{FEATURES.map((feature, idx) => (
<motion.div
key={feature.title}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.5, delay: idx * 0.1 }}
>
<h3 className="text-2xl sm:text-3xl font-semibold mb-3 text-white">
{feature.title}
</h3>
<p className="text-base sm:text-lg text-zinc-400">
{feature.description}
</p>
</motion.div>
))}
</div>

<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }}
transition={{ duration: 0.5, delay: 0.4 }}
className="flex justify-center mt-16 sm:mt-20"
>
<button
type="button"
onClick={onOpenWaitlist}
className="bg-white text-black px-6 py-3 rounded-lg text-base font-medium hover:bg-zinc-200 transition-colors"
>
Join waitlist
</button>
</motion.div>
</div>
</section>
);
}
1 change: 1 addition & 0 deletions apps/website/src/app/components/FeaturesSection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { FeaturesSection } from "./FeaturesSection";
1 change: 1 addition & 0 deletions apps/website/src/app/components/Footer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Footer } from "./Footer";
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { motion } from "framer-motion";
import { useState } from "react";
import { WaitlistModal } from "./WaitlistModal";
import { WaitlistModal } from "../WaitlistModal";

export function Header() {
const [isWaitlistOpen, setIsWaitlistOpen] = useState(false);
Expand Down
1 change: 1 addition & 0 deletions apps/website/src/app/components/Header/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { Header } from "./Header";
Loading
Loading