Quant Agent — multi-agent quant terminal on Hyperliquid
All checks were successful
deploy / deploy (push) Successful in 7s
All checks were successful
deploy / deploy (push) Successful in 7s
Live HL data → in-browser TA → 4-agent committee → LONG/SHORT/SKIP verdict. Terminal grid command bar + interactive agent-workflow graph. Paper-trading worker reuses the browser brain (dry-run + Telegram). Forgejo static deploy. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
f3b533b032
110 changed files with 15045 additions and 0 deletions
97
.agents/skills/add-x-tweet/SKILL.md
Normal file
97
.agents/skills/add-x-tweet/SKILL.md
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
---
|
||||
name: add-x-tweet
|
||||
description: Add X (Twitter) testimonials to the bklit homepage. Fetches username, avatar, and tweet text from a status URL and appends an entry to apps/web/lib/testimonials.ts. Use when the user shares an x.com/twitter.com tweet URL to add or replace a testimonial.
|
||||
---
|
||||
|
||||
# Add X Tweet Testimonial
|
||||
|
||||
Add social proof tweets to the homepage testimonial grid.
|
||||
|
||||
## When to use
|
||||
|
||||
- User provides one or more `https://x.com/.../status/...` URLs
|
||||
- User asks to replace, prioritize, or reorder testimonials
|
||||
- User says "add tweet", "new testimonial", or similar
|
||||
|
||||
## Workflow
|
||||
|
||||
### 1. Fetch tweet metadata
|
||||
|
||||
Run the fetch script from the repo root (requires network):
|
||||
|
||||
```bash
|
||||
node .agents/skills/add-x-tweet/scripts/fetch-tweet.mjs "https://x.com/user/status/123..."
|
||||
```
|
||||
|
||||
For multiple URLs, pass each URL or run once per URL.
|
||||
|
||||
The script prints JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "123...",
|
||||
"url": "https://x.com/user/status/123...",
|
||||
"author": { "name": "...", "handle": "@user", "avatar": "https://pbs.twimg.com/...", "verified": true },
|
||||
"content": "tweet text without @uixmat prefix"
|
||||
}
|
||||
```
|
||||
|
||||
**Fallback:** If the script fails, use Twitter oEmbed:
|
||||
|
||||
```bash
|
||||
curl -sL "https://publish.twitter.com/oembed?url=TWEET_URL&omit_script=1"
|
||||
```
|
||||
|
||||
Parse `author_name` and HTML `<p>` for text. Avatar still needs fxtwitter or manual profile image URL.
|
||||
|
||||
### 2. Edit `apps/web/lib/testimonials.ts`
|
||||
|
||||
- Use **status id** as `id` (numeric string from URL)
|
||||
- Set `url` to the canonical tweet URL (no query params)
|
||||
- Run avatar through `twitterAvatarUrl()` (upgrades `_200x200` → `_400x400`)
|
||||
- Strip leading `@uixmat` from content; trim whitespace
|
||||
- Shorten very long tweets if they include trailing promo links (keep the praise, drop link-only tails)
|
||||
|
||||
### 3. Ordering rules
|
||||
|
||||
Masonry uses CSS columns (top-to-bottom per column). On `lg`, **indices 0–2** = column 1 top 3, **3–5** = column 2 top 3, **6–8** = column 3 top 3.
|
||||
|
||||
- **Prioritize** when asked: place in the next open slot among indices 0–8 without duplicating ids
|
||||
- **Replace** when asked: remove the old entry (match by handle or old id) before adding the new one
|
||||
- Set `author.verified` from script output (`author.verification.verified` via FxTwitter)
|
||||
|
||||
### 4. Collapsed grid
|
||||
|
||||
The homepage shows `testimonialCollapsedCount` (6) cards before **See more**. No change needed when adding tweets unless the user asks to change visible rows.
|
||||
|
||||
### 5. Verify
|
||||
|
||||
```bash
|
||||
cd apps/web && npx tsc --noEmit -p tsconfig.json
|
||||
```
|
||||
|
||||
Open homepage — grid is 3 columns, bottom fade + **See more** expands max-height.
|
||||
|
||||
## Data source
|
||||
|
||||
Primary: [FxTwitter API](https://github.com/FixTweet/FxTwitter) `https://api.fxtwitter.com/{user}/status/{id}` — returns author name, screen_name, avatar_url, and text.
|
||||
|
||||
Do not commit API keys; the public endpoint is used by the script only at add-tweet time.
|
||||
|
||||
## Example entry
|
||||
|
||||
```ts
|
||||
{
|
||||
id: "2056717453816729751",
|
||||
url: "https://x.com/jordienr/status/2056717453816729751",
|
||||
author: {
|
||||
name: "jordi",
|
||||
handle: "@jordienr",
|
||||
avatar: twitterAvatarUrl(
|
||||
"https://pbs.twimg.com/profile_images/2053541405121769475/TUDez6zL_200x200.jpg"
|
||||
),
|
||||
verified: true,
|
||||
},
|
||||
content: "bklit saved my marriage",
|
||||
},
|
||||
```
|
||||
71
.agents/skills/add-x-tweet/scripts/fetch-tweet.mjs
Normal file
71
.agents/skills/add-x-tweet/scripts/fetch-tweet.mjs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Fetch X tweet metadata for homepage testimonials.
|
||||
* Usage: node fetch-tweet.mjs "https://x.com/user/status/123"
|
||||
*/
|
||||
|
||||
const urls = process.argv.slice(2);
|
||||
|
||||
if (urls.length === 0) {
|
||||
console.error("Usage: node fetch-tweet.mjs <tweet-url> [tweet-url...]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function parseTweetUrl(url) {
|
||||
const match = url.match(/(?:x|twitter)\.com\/([^/]+)\/status\/(\d+)/i);
|
||||
if (!match) {
|
||||
throw new Error(`Invalid tweet URL: ${url}`);
|
||||
}
|
||||
return { screenName: match[1], statusId: match[2] };
|
||||
}
|
||||
|
||||
function stripReplyPrefix(text) {
|
||||
return text.replace(/^@uixmat\s*/i, "").trim();
|
||||
}
|
||||
|
||||
function upgradeAvatar(url) {
|
||||
return url.replace(/_200x200\.(jpg|png|webp)$/i, "_400x400.$1");
|
||||
}
|
||||
|
||||
async function fetchTweet(url) {
|
||||
const { screenName, statusId } = parseTweetUrl(url);
|
||||
const apiUrl = `https://api.fxtwitter.com/${screenName}/status/${statusId}`;
|
||||
const res = await fetch(apiUrl, {
|
||||
headers: { "User-Agent": "bklit-ui-add-x-tweet/1.0" },
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`FxTwitter ${res.status} for ${url}`);
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
const tweet = data.tweet ?? data;
|
||||
const author = tweet.author ?? {};
|
||||
|
||||
const canonicalUrl = `https://x.com/${screenName}/status/${statusId}`;
|
||||
|
||||
const verified = author.verification?.verified === true;
|
||||
|
||||
return {
|
||||
id: statusId,
|
||||
url: canonicalUrl,
|
||||
author: {
|
||||
name: author.name ?? screenName,
|
||||
handle: `@${author.screen_name ?? screenName}`,
|
||||
avatar: upgradeAvatar(author.avatar_url ?? author.avatar ?? ""),
|
||||
verified,
|
||||
},
|
||||
content: stripReplyPrefix(tweet.text ?? ""),
|
||||
};
|
||||
}
|
||||
|
||||
for (const url of urls) {
|
||||
try {
|
||||
const entry = await fetchTweet(url);
|
||||
console.log(JSON.stringify(entry, null, 2));
|
||||
} catch (err) {
|
||||
console.error(`Failed: ${url}`);
|
||||
console.error(err.message);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
}
|
||||
30
.agents/skills/bklit-playground/SKILL.md
Normal file
30
.agents/skills/bklit-playground/SKILL.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
name: bklit-playground
|
||||
description: >
|
||||
DEPRECATED — use bklit-studio instead. Local /playground is no longer the
|
||||
chart development workflow for bklit-ui contributors.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Deprecated: bklit-playground
|
||||
|
||||
**This skill is retired.** Chart prototyping and editing now happen in **Studio** only.
|
||||
|
||||
## Use instead
|
||||
|
||||
Read and follow **`.agents/skills/bklit-studio/SKILL.md`**.
|
||||
|
||||
- Dev URL: **http://localhost:3000/studio** (`pnpm dev` from repo root)
|
||||
- Optional: `?chart=line-chart` (or any slug from `packages/studio/src/chart-slugs.ts`)
|
||||
|
||||
## Why
|
||||
|
||||
Studio already includes the editor shell, component tree, properties, motion controls, codegen, and registry-backed previews. The gitignored `/playground` route duplicated wiring and drifted from production Studio.
|
||||
|
||||
## `/playground` route
|
||||
|
||||
Redirects to `/studio`. Do not scaffold `apps/web/app/playground/page.tsx` or `apps/web/components/playground/` for new chart work.
|
||||
|
||||
## Shipping
|
||||
|
||||
Unchanged: **`.agents/skills/bklit-ship/SKILL.md`** — prototypes should live in `packages/ui` and `packages/studio`, not under `apps/web/components/playground/`.
|
||||
7
.agents/skills/bklit-playground/templates/page.tsx
Normal file
7
.agents/skills/bklit-playground/templates/page.tsx
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* DEPRECATED — do not copy to apps/web/app/playground/page.tsx.
|
||||
*
|
||||
* Chart development uses Studio: http://localhost:3000/studio
|
||||
* See .agents/skills/bklit-studio/SKILL.md
|
||||
*/
|
||||
export {};
|
||||
115
.agents/skills/bklit-ship/SKILL.md
Normal file
115
.agents/skills/bklit-ship/SKILL.md
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
---
|
||||
name: bklit-ship
|
||||
description: bklit-ui monorepo contributors only — ship a chart or component from Studio prototype to production in packages/ui with docs and registry.
|
||||
disable-model-invocation: true
|
||||
---
|
||||
|
||||
# Bklit Ship Skill
|
||||
|
||||
This skill is for **bklit-ui monorepo contributors** taking a chart or component validated in **Studio** into **production**: published in the UI package, documented, and ready for users.
|
||||
|
||||
## When to use this skill
|
||||
|
||||
- You cloned `bklit/bklit-ui` and have a working prototype validated in **Studio** (`/studio`) and want to ship it.
|
||||
- You are ready to move from "scaffolding" to "permanent": the API and key props/variants are decided from the prototyping phase.
|
||||
|
||||
---
|
||||
|
||||
## Plan: Prototype → Production
|
||||
|
||||
Follow these steps in order. Treat this as a checklist; each step has concrete locations in the repo.
|
||||
|
||||
### 1. Move into the UI package and export
|
||||
|
||||
- **Move** or finalize chart/component source in `packages/ui/` (e.g. `packages/ui/src/charts/` for charts). Prototypes should already live here if you followed **bklit-studio**; remove any leftover copies under `apps/web/components/playground/` if present.
|
||||
- **Export** the new chart/component from the package’s public API:
|
||||
- For charts: add exports in `packages/ui/src/charts/index.ts`.
|
||||
- If the package uses `exports` in `package.json` for specific entry points, add or update the relevant entry so the new component is importable as `@bklitui/ui/charts` (or the appropriate path).
|
||||
- **Do not** add app-only chart copies under `apps/web/components/playground/` (deprecated). Studio previews belong in `packages/studio`.
|
||||
|
||||
### 2. Documentation and examples (apps/web)
|
||||
|
||||
Documentation lives under `apps/web/`. Do all of the following.
|
||||
|
||||
- **Update existing component docs** when shipping extends an existing chart (new props, subcomponents, or behavior):
|
||||
- Add new props to the relevant tables in the **parent** doc (e.g. `line-chart.mdx` for `Grid highlightRowValues`, `ChartTooltip indicatorColor`).
|
||||
- Update related utility docs if needed (e.g. `content/docs/utility/grid.mdx`, `tooltip.mdx`).
|
||||
- Keep the **primary docs preview unchanged** — do not swap the standard preview on an existing page for the new variant.
|
||||
- Add a short section linking to a dedicated doc page when the feature warrants one (e.g. Profit/Loss → `profit-loss-line.mdx`).
|
||||
|
||||
- **Chart examples (live demos on `/charts/[slug]`)**
|
||||
- Add examples to the **corresponding gallery route** (e.g. profit/loss variants under `/charts/line-chart`, not only a new docs preview).
|
||||
- If the feature is a new chart kind, add its slug to `apps/web/components/charts/chart-slugs.ts` and register examples in `apps/web/components/charts/chart-examples.tsx` (`CHART_NAV_ITEMS` / factory registry as appropriate).
|
||||
- Reuse or mirror the prop variants you validated in the scaffolding phase.
|
||||
|
||||
- **Dedicated doc page** (when shipping a new composable or chart kind)
|
||||
- Add `apps/web/content/docs/components/<name>.mdx` with frontmatter, `<ComponentPreview>`, installation, usage, and props — consistent with existing component docs.
|
||||
- Add the slug to `apps/web/content/docs/components/meta.json` (desktop sidebar).
|
||||
- Add an entry to `apps/web/components/docs/site-header.tsx` (mobile nav).
|
||||
|
||||
### 3. Studio
|
||||
|
||||
- **Update the existing studio chart** when the feature is a variant of an existing type, **or add a new studio chart** when it is a distinct kind.
|
||||
- Wire **all tunable props** into studio (if not already done while prototyping):
|
||||
- `packages/studio/src/lib/studio-parsers.ts` — URL state keys and defaults
|
||||
- `packages/studio/src/lib/registry-control-groups.ts` — control groups
|
||||
- `packages/studio/src/lib/registry.tsx` — render preview + `generateCode`
|
||||
- `packages/studio/src/lib/studio-components.ts` — layer tree
|
||||
- Chart-type defaults in `packages/studio/src/components/studio-state-provider.tsx` when switching chart type
|
||||
|
||||
### 4. Rebuild the shadcn registry
|
||||
|
||||
- From the **repo root**: run `pnpm registry:build`.
|
||||
- This updates `apps/web/public/r/` from `packages/ui`. Ensure new components are listed in `packages/ui/registry.json` when they should be installable via shadcn.
|
||||
|
||||
### 5. Lint, format, test, and build
|
||||
|
||||
Run from repo root until clean:
|
||||
|
||||
```bash
|
||||
pnpm lint
|
||||
pnpm format
|
||||
pnpm --filter @bklitui/ui test # when package logic changed
|
||||
pnpm build
|
||||
pnpm registry:build # if not already run in step 4
|
||||
```
|
||||
|
||||
Fix all errors; repeat until hooks pass on commit.
|
||||
|
||||
### 6. Commit, push, and open a PR
|
||||
|
||||
- **Commit** with a short, clear message (e.g. `feat(charts): add ProfitLossLine component`).
|
||||
- **Push** the branch.
|
||||
- **Open a PR** with the ship checklist filled in (see below).
|
||||
|
||||
---
|
||||
|
||||
## PR checklist
|
||||
|
||||
- [ ] Chart/component moved to `packages/ui` and exported
|
||||
- [ ] **Existing docs updated** with new props/features (standard preview unchanged)
|
||||
- [ ] Gallery examples on the correct `/charts/**` route
|
||||
- [ ] Dedicated doc page + sidebar + mobile nav (if new composable/chart kind)
|
||||
- [ ] **Studio** chart updated or added with all props in control groups
|
||||
- [ ] Registry rebuilt (`pnpm registry:build`)
|
||||
- [ ] `pnpm lint`, tests (if applicable), and `pnpm build` pass
|
||||
|
||||
---
|
||||
|
||||
## File reference (quick lookup)
|
||||
|
||||
| Step | Location |
|
||||
|------|----------|
|
||||
| Chart exports | `packages/ui/src/charts/index.ts` |
|
||||
| Chart slugs | `apps/web/components/charts/chart-slugs.ts` |
|
||||
| Chart examples (nav + registry) | `apps/web/components/charts/chart-examples.tsx` |
|
||||
| Component docs | `apps/web/content/docs/components/<name>.mdx` |
|
||||
| Utility docs (Grid, Tooltip, …) | `apps/web/content/docs/utility/*.mdx` |
|
||||
| Sidebar (desktop) | `apps/web/content/docs/components/meta.json` → `pages` |
|
||||
| Mobile nav | `apps/web/components/docs/site-header.tsx` → `components` array |
|
||||
| Studio registry | `packages/studio/src/lib/registry.tsx` |
|
||||
| Studio components tree | `packages/studio/src/lib/studio-components.ts` |
|
||||
| Studio controls | `packages/studio/src/lib/registry-control-groups.ts` |
|
||||
| Studio URL state | `packages/studio/src/lib/studio-parsers.ts` |
|
||||
| Registry (source) | `packages/ui/registry.json`; build output: `apps/web/public/r/` |
|
||||
| Registry build | From root: `pnpm registry:build` |
|
||||
134
.agents/skills/bklit-studio-chart-performance/SKILL.md
Normal file
134
.agents/skills/bklit-studio-chart-performance/SKILL.md
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
---
|
||||
name: bklit-studio-chart-performance
|
||||
description: >
|
||||
Reusable Studio chart performance audit and fix workflow. Use when a chart
|
||||
feels sluggish in /studio (pan, slider ticks, legend hover) but siblings
|
||||
like pie-chart feel fine.
|
||||
---
|
||||
|
||||
# Studio chart performance
|
||||
|
||||
Use when a chart feels sluggish in Studio but similar charts (e.g. pie-chart) are fine.
|
||||
|
||||
## One-line rule
|
||||
|
||||
Keep enter animation on paths if you need it, then **drop Motion path subscriptions** and **isolate hover** so Studio slider and legend updates don't replay expensive arc/path math across every series every frame.
|
||||
|
||||
---
|
||||
|
||||
## 1. Find what re-renders on every interaction
|
||||
|
||||
Studio updates `displayState` on every slider tick and on legend/slice hover. Trace:
|
||||
|
||||
- Does hover live in the **same context** as data, scales, and animation config?
|
||||
- Does the preview **recreate children** (`data.map`, pattern defs, motion props) every render?
|
||||
- Does the chart **remount unnecessarily** (`key` tied to motion signature vs manual replay)?
|
||||
|
||||
**Pattern:** Split context like cartesian / pie charts — **stable slice** (data, geometry, animation config) vs **hover slice** (`hoveredIndex`, tooltip). Consumers that don't need hover use only the stable hook (`usePieStable`, `useRingStable`, `useChartStable`, …).
|
||||
|
||||
**Studio pan:** Wrap chart render in `StudioChartRender` (`packages/studio/src/components/studio-chart-render.tsx`) so camera pan / FPS counter parent updates skip the chart tree when render props are unchanged.
|
||||
|
||||
---
|
||||
|
||||
## 2. Treat SVG path `d` animation as expensive
|
||||
|
||||
Animating `d` with Motion / `useTransform` + d3 arc (or similar) runs layout + paint every frame, per series.
|
||||
|
||||
| Prefer | Avoid |
|
||||
|--------|--------|
|
||||
| `transform` / `opacity` for hover (compositor-friendly) | Continuous `d` morphing after enter is done |
|
||||
| Static `d` once enter finishes | Keeping Motion subscriptions on `d` for the chart's lifetime |
|
||||
| Enter animation only, then static paths | Re-running enter path math on unrelated prop changes |
|
||||
|
||||
**Pattern:** `useMountProgress` for enter → when progress ≥ 1 (`useEnterComplete`), render **static paths** and only animate hover with `x`/`y`/`opacity`/`scale` on a **`motion.g`** wrapper (not per-path `scale` on `motion.path`).
|
||||
|
||||
Shared hook: `packages/ui/src/charts/use-enter-complete.ts`
|
||||
|
||||
---
|
||||
|
||||
## 3. Memoize chart shell context
|
||||
|
||||
Unmemoized provider values force all children to reconcile on every parent render.
|
||||
|
||||
- Memoize the **stable context object** with explicit deps (data, arcs/radii, dimensions, callbacks).
|
||||
- Memoize **hover context** on `hoveredIndex` + stable `setHoveredIndex` (`useCallback` in chart shell).
|
||||
- Match **`isLoaded`** to ring/cartesian: `useEffect` + timeout, not a lazy `useState` initializer.
|
||||
|
||||
Reference: `pie-context.tsx`, `ring-context.tsx`, `chart-context.tsx`, `PieChartCore` / `RingChartCore` `useMemo` on provider value.
|
||||
|
||||
---
|
||||
|
||||
## 4. Studio preview–specific wins
|
||||
|
||||
Chart-agnostic; apply in `packages/studio/src/components/charts/*-studio*.tsx`:
|
||||
|
||||
| Win | How |
|
||||
|-----|-----|
|
||||
| Conditional defs | Only pass `patternDefs` / gradients when a series uses patterns |
|
||||
| Memo derived data | Colored/mapped data arrays; slice/series lists (`useMemo`, deps: `dataSeed` + design fields that affect color) |
|
||||
| Memo motion enter | Don't call `getStudioMotionEnterProps` inline; `useMemo` with **motion-only** deps (not full `state`) |
|
||||
| Memo legend hover | `{ hoveredIndex, setHoveredIndex }` in `useMemo` — already in `studio-legend-hover.tsx` |
|
||||
| Memo chart body | `memo()` wrapper; pass **primitives** (`chartKey`, `chartSize`, `data`) not whole `ctx` so pan/shell re-renders skip rebuild |
|
||||
| Disable glow in Studio | `showGlow={false}` on series components |
|
||||
|
||||
Reference: `pie-studio-preview.tsx`, `ring-studio-preview.tsx`
|
||||
|
||||
---
|
||||
|
||||
## 5. Compare against a “fast” sibling in Studio
|
||||
|
||||
Diff the slow chart against one that feels smooth in the same editor (usually **pie-chart**):
|
||||
|
||||
| Check | Slow chart often has | Fast chart often has |
|
||||
|-------|----------------------|----------------------|
|
||||
| Shell | Inline render, extra defs | `StudioChartShell` + conditional patterns |
|
||||
| Series count | Many animated paths | Fewer paths or simpler geometry |
|
||||
| Hover | Context + full tree re-render | Stable subscribers; hover on `motion.g` / translate |
|
||||
| Enter | Path `d` wipe per series | Static `d` after enter; transform-only hover |
|
||||
| Pan | Chart tree rebuilds every frame | `StudioChartRender` memo boundary |
|
||||
|
||||
---
|
||||
|
||||
## 6. Validation bar
|
||||
|
||||
Before opening a PR:
|
||||
|
||||
```bash
|
||||
pnpm lint
|
||||
pnpm --filter @bklitui/ui check-types
|
||||
pnpm --filter @bklitui/studio check-types
|
||||
# scoped production build when touching studio/web
|
||||
```
|
||||
|
||||
Manual `/studio?chart=<slug>`:
|
||||
|
||||
- [ ] Enter animation
|
||||
- [ ] Hover / legend sync
|
||||
- [ ] Drag geometry sliders (no unnecessary remount)
|
||||
- [ ] Canvas pan (space + drag) after enter — FPS near pie-chart baseline
|
||||
- [ ] Pattern/gradient mode if supported
|
||||
|
||||
---
|
||||
|
||||
## Chart status (bklit-ui)
|
||||
|
||||
| Chart | Slug | Status |
|
||||
|-------|------|--------|
|
||||
| Pie | `pie-chart` | ✅ Reference (#120) |
|
||||
| Ring | `ring-chart` | ✅ Aligned to checklist (context split, static paths, `StudioChartRender`, preview memo) |
|
||||
| Radar / Funnel / Choropleth | various | Partial — run checklist |
|
||||
| Cartesian / scatter / live-line | various | ✅ #91 decimation + hover batching |
|
||||
| Sankey | `sankey-chart` | `useTransform` on link stroke — candidate for `useEnterComplete` |
|
||||
| Gauge | `gauge-chart` | Low priority (single arc) |
|
||||
|
||||
---
|
||||
|
||||
## Key files
|
||||
|
||||
| Area | Path |
|
||||
|------|------|
|
||||
| Enter-complete hook | `packages/ui/src/charts/use-enter-complete.ts` |
|
||||
| Pie reference | `packages/ui/src/charts/pie-slice.tsx`, `pie-context.tsx`, `pie-studio-preview.tsx` |
|
||||
| Ring | `packages/ui/src/charts/ring.tsx`, `ring-chart.tsx`, `ring-studio-preview.tsx` |
|
||||
| Pan isolation | `packages/studio/src/components/studio-chart-render.tsx` |
|
||||
| Registry | `packages/studio/src/lib/registry.tsx` |
|
||||
107
.agents/skills/bklit-studio/SKILL.md
Normal file
107
.agents/skills/bklit-studio/SKILL.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
name: bklit-studio
|
||||
description: >
|
||||
bklit-ui monorepo contributors only. Use automatically when building or editing
|
||||
charts, tuning props/animation, or prototyping in Studio (/studio). Replaces
|
||||
the deprecated local /playground route.
|
||||
---
|
||||
|
||||
# Bklit Studio Skill (chart development)
|
||||
|
||||
**Monorepo contributors only.** Use this skill **automatically** whenever you build, edit, or prototype a chart — before shipping via **bklit-ship**.
|
||||
|
||||
Studio at **`/studio`** is the single development surface: full editor shell, component tree, properties, motion, codegen, and registry previews. Do **not** scaffold `apps/web/app/playground/`.
|
||||
|
||||
## When to use (auto-trigger)
|
||||
|
||||
- Build, prototype, or edit a chart (new or existing)
|
||||
- Tune props, layers, data, styling, or animation
|
||||
- Mentions Studio, `/studio`, registry preview, or chart controls
|
||||
- Work continues from a prior playground task → use Studio instead
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
pnpm dev # repo root
|
||||
```
|
||||
|
||||
Open **http://localhost:3000/studio** (optional query: `?chart=line-chart`).
|
||||
|
||||
| Task | URL |
|
||||
|------|-----|
|
||||
| Edit existing chart | `/studio?chart=<slug>` — slugs in `packages/studio/src/chart-slugs.ts` |
|
||||
| Profit/loss line mode | `/studio?chart=line-chart&lineChartMode=profitLoss` |
|
||||
|
||||
## Editor layout
|
||||
|
||||
| Region | Purpose |
|
||||
|--------|---------|
|
||||
| **Left** | Chart type (in full Studio), layer list, data controls, animation |
|
||||
| **Center** | Canvas, rulers, resizable frame, replay/scramble |
|
||||
| **Right** | Properties for the **selected layer** |
|
||||
|
||||
**Pane rules:** animation → left (`motionPanel` on chart config); per-layer props → right; visibility → eye icon on layer (uses `hiddenComponents` in URL state).
|
||||
|
||||
## Where to implement (existing charts)
|
||||
|
||||
Edit the chart in **`packages/studio`** and **`packages/ui`** — not `apps/web/components/playground/`.
|
||||
|
||||
| Concern | Path |
|
||||
|---------|------|
|
||||
| Chart preview + codegen | `packages/studio/src/lib/registry.tsx` (`render`, `generateCode`) |
|
||||
| Layer tree (components panel) | `packages/studio/src/lib/studio-components.ts` (`resolve*Components`) |
|
||||
| Control groups (properties) | `packages/studio/src/lib/registry-control-groups.ts` |
|
||||
| URL / default state | `packages/studio/src/lib/studio-parsers.ts` |
|
||||
| Chart UI | `packages/ui/src/charts/` |
|
||||
| Slugs | `packages/studio/src/chart-slugs.ts` + `studioRegistry` in `registry.tsx` |
|
||||
| Studio-only previews | `packages/studio/src/components/charts/*-studio-preview.tsx` |
|
||||
|
||||
### Patterns in `registry.tsx` render
|
||||
|
||||
Reuse committed helpers (do not reimplement):
|
||||
|
||||
- `StudioChartShell` + `studioCartesianLegendItems` — legend grid + `showLegend`
|
||||
- `StudioVisibleLayer` + `componentId` — ties render to layer visibility (`line.grid`, `line.series.0`, …)
|
||||
- `getStudioCssRevealPropsForPreview` — motion / reveal
|
||||
- `isStudioComponentVisible(state, componentId)` — conditional children
|
||||
- `seriesStrokePropsFromState`, `fadeEdgesPropValue`, `chartTooltipPropsFromState`
|
||||
|
||||
Reference implementation: `lineConfig.render` in `registry.tsx`.
|
||||
|
||||
## New chart (not in registry yet)
|
||||
|
||||
1. Implement component(s) in `packages/ui/src/charts/` (minimal API first).
|
||||
2. Add slug to `packages/studio/src/chart-slugs.ts`.
|
||||
3. Add `StudioChartConfig` to `studioRegistry` in `registry.tsx`:
|
||||
- `render(state, ctx)` — preview inside `EditorChartFrame` via `StudioShell`
|
||||
- `resolveComponents` in `studio-components.ts`
|
||||
- `controlGroups` / `resolveControlGroups`
|
||||
- `generateCode` for the code sheet
|
||||
4. Open `/studio?chart=<new-slug>` and iterate.
|
||||
5. When stable, follow **bklit-ship** for docs, gallery, shadcn registry.
|
||||
|
||||
## Adding a prop or layer
|
||||
|
||||
1. Add key to `StudioUrlState` / `defaultStudioState` in `studio-parsers.ts` if missing.
|
||||
2. Add control(s) on the right layer in `registry-control-groups.ts` or per-component `controlGroups` in `studio-components.ts`.
|
||||
3. Wire prop in `registry.tsx` `render` (and in `packages/ui` chart code).
|
||||
4. If the prop affects animation → ensure `motionPanel: true` on chart config.
|
||||
5. Verify in Studio: layer list, visibility toggle, properties, replay.
|
||||
|
||||
## Shipping
|
||||
|
||||
When the API is stable, read `.agents/skills/bklit-ship/SKILL.md` — prototype code should already live in `packages/ui` + `packages/studio`; no playground route to migrate.
|
||||
|
||||
## Deprecated: `/playground`
|
||||
|
||||
The local **`/playground`** route and **`bklit-playground`** skill are deprecated. `/playground` redirects to `/studio`. Do not copy `playground-editor-shell` or `apps/web/components/playground/` for new work.
|
||||
|
||||
## File reference
|
||||
|
||||
| Item | Path |
|
||||
|------|------|
|
||||
| Studio app route | `apps/web/app/studio/page.tsx` (`StudioShell`) |
|
||||
| Studio package | `packages/studio/` |
|
||||
| Studio skill | `.agents/skills/bklit-studio/SKILL.md` |
|
||||
| Ship checklist | `.agents/skills/bklit-ship/SKILL.md` |
|
||||
| Performance audit | `.agents/skills/bklit-studio-chart-performance/SKILL.md` |
|
||||
136
.agents/skills/bklit-ui/SKILL.md
Normal file
136
.agents/skills/bklit-ui/SKILL.md
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
---
|
||||
name: bklit-ui
|
||||
description: >
|
||||
Bklit UI charts and data visualization for any project using the @bklit
|
||||
shadcn registry. Install, compose, theme, and animate charts correctly.
|
||||
Triggers when working with @bklitui/ui/charts, @bklit components, data
|
||||
visualization, dashboards, or chart theming. Also invoke manually for
|
||||
chart tasks.
|
||||
allowed-tools: Bash(npx shadcn@latest *), Bash(pnpm dlx shadcn@latest *), Bash(bunx --bun shadcn@latest *)
|
||||
---
|
||||
|
||||
# Bklit UI
|
||||
|
||||
Composable chart components for React, distributed via the `@bklit` shadcn registry. Charts are installed as source into the user's project.
|
||||
|
||||
> **IMPORTANT:** Run shadcn CLI commands with the project's package runner: `npx shadcn@latest`, `pnpm dlx shadcn@latest`, or `bunx --bun shadcn@latest`.
|
||||
|
||||
## Current Project Context
|
||||
|
||||
```json
|
||||
!`npx shadcn@latest info --json`
|
||||
```
|
||||
|
||||
Use the JSON above for framework, aliases, Tailwind version, installed components, and resolved paths. Confirm the `@bklit` registry is configured before adding charts.
|
||||
|
||||
## Principles
|
||||
|
||||
1. **Install before inventing.** Use `npx shadcn@latest add @bklit/<chart>` — charts are registry components, not hand-rolled SVG.
|
||||
2. **Compose, don't flatten.** Root chart → `Grid` → series → axes → `ChartTooltip`. See [composition.md](./rules/composition.md).
|
||||
3. **Theme with tokens.** Use `chartCssVars` and `--chart-*` variables — never hardcode one-off colors. See [theming.md](./rules/theming.md).
|
||||
4. **Read the doc page first.** Each chart has props, data shape, and examples at `https://ui.bklit.com/docs/components/<slug>`.
|
||||
5. **Browse variants.** Gallery: `https://ui.bklit.com/charts/<slug>` — Studio: `https://ui.bklit.com/studio?chart=<slug>`.
|
||||
|
||||
## Critical Rules
|
||||
|
||||
These rules are **always enforced**. Each links to Incorrect/Correct examples.
|
||||
|
||||
### Composition → [composition.md](./rules/composition.md)
|
||||
|
||||
- **Series and axes live inside the root chart** — `LineChart`, `BarChart`, `AreaChart`, etc.
|
||||
- **One root per chart** — use `ComposedChart` for mixed series types.
|
||||
- **Grid before series** so lines/bars render above grid lines.
|
||||
- **`ChartTooltip` as a chart child** — required for crosshair and hover context.
|
||||
|
||||
### Theming → [theming.md](./rules/theming.md)
|
||||
|
||||
- **Use `chartCssVars`** from `@bklitui/ui/charts` instead of raw `"var(--chart-…)"` strings.
|
||||
- **Series palette:** `--chart-1` … `--chart-5` for multi-series charts.
|
||||
- **Tooltip surfaces:** `bg-popover text-popover-foreground` — avoids white-on-white in light mode.
|
||||
|
||||
### Animation → [animation.md](./rules/animation.md)
|
||||
|
||||
- **Default duration ~1100ms** for cartesian enter animations unless the doc specifies otherwise.
|
||||
- **Replay:** change `revealSignature` or remount with a new `key`.
|
||||
- **Live charts:** use `paused` on `LiveLineChart` to debug without stopping the rAF loop manually.
|
||||
|
||||
### Tooltips → [tooltips.md](./rules/tooltips.md)
|
||||
|
||||
- **Custom content via `ChartTooltip` `content` prop** or children patterns from docs.
|
||||
- **`indicatorColor` function** for candlestick / dynamic crosshair colors.
|
||||
- **Custom indicators:** use `useChart()` — do not track mouse globally outside chart context.
|
||||
|
||||
### Installation → [installation.md](./rules/installation.md)
|
||||
|
||||
- **Require `@bklit` registry** in `components.json`.
|
||||
- **Install:** `npx shadcn@latest add @bklit/<slug>`.
|
||||
- **Let the CLI install peer dependencies** — do not pin `@visx/*` / `motion` manually unless resolving a conflict.
|
||||
|
||||
## Chart Catalog
|
||||
|
||||
| Slug | Use when | Install | Docs | Gallery |
|
||||
|------|----------|---------|------|---------|
|
||||
| `area-chart` | Trends with filled regions under lines | `@bklit/area-chart` | [/docs/components/area-chart](https://ui.bklit.com/docs/components/area-chart) | [/charts/area-chart](https://ui.bklit.com/charts/area-chart) |
|
||||
| `bar-chart` | Category comparisons, stacked or grouped bars | `@bklit/bar-chart` | [/docs/components/bar-chart](https://ui.bklit.com/docs/components/bar-chart) | [/charts/bar-chart](https://ui.bklit.com/charts/bar-chart) |
|
||||
| `line-chart` | Time series, multi-line trends, markers | `@bklit/line-chart` | [/docs/components/line-chart](https://ui.bklit.com/docs/components/line-chart) | [/charts/line-chart](https://ui.bklit.com/charts/line-chart) |
|
||||
| `live-line-chart` | Streaming / real-time data | `@bklit/live-line-chart` | [/docs/components/live-line-chart](https://ui.bklit.com/docs/components/live-line-chart) | [/charts/live-line-chart](https://ui.bklit.com/charts/live-line-chart) |
|
||||
| `composed-chart` | Mixed bar + line (or similar) on one axis | `@bklit/composed-chart` | [/docs/components/composed-chart](https://ui.bklit.com/docs/components/composed-chart) | [/charts/composed-chart](https://ui.bklit.com/charts/composed-chart) |
|
||||
| `scatter-chart` | Correlation, distribution, bubble sizing | `@bklit/scatter-chart` | [/docs/components/scatter-chart](https://ui.bklit.com/docs/components/scatter-chart) | [/charts/scatter-chart](https://ui.bklit.com/charts/scatter-chart) |
|
||||
| `candlestick-chart` | OHLC financial data, brushes | `@bklit/candlestick-chart` | [/docs/components/candlestick-chart](https://ui.bklit.com/docs/components/candlestick-chart) | [/charts/candlestick-chart](https://ui.bklit.com/charts/candlestick-chart) |
|
||||
| `pie-chart` | Part-to-whole slices | `@bklit/pie-chart` | [/docs/components/pie-chart](https://ui.bklit.com/docs/components/pie-chart) | [/charts/pie-chart](https://ui.bklit.com/charts/pie-chart) |
|
||||
| `ring-chart` | Donut / ring KPIs | `@bklit/ring-chart` | [/docs/components/ring-chart](https://ui.bklit.com/docs/components/ring-chart) | [/charts/ring-chart](https://ui.bklit.com/charts/ring-chart) |
|
||||
| `radar-chart` | Multi-axis comparison | `@bklit/radar-chart` | [/docs/components/radar-chart](https://ui.bklit.com/docs/components/radar-chart) | [/charts/radar-chart](https://ui.bklit.com/charts/radar-chart) |
|
||||
| `gauge-chart` | Single-value KPI dial | `@bklit/gauge-chart` | [/docs/components/gauge-chart](https://ui.bklit.com/docs/components/gauge-chart) | [/charts/gauge-chart](https://ui.bklit.com/charts/gauge-chart) |
|
||||
| `funnel-chart` | Stage conversion funnels | `@bklit/funnel-chart` | [/docs/components/funnel-chart](https://ui.bklit.com/docs/components/funnel-chart) | [/charts/funnel-chart](https://ui.bklit.com/charts/funnel-chart) |
|
||||
| `sankey-chart` | Flow between nodes | `@bklit/sankey-chart` | [/docs/components/sankey-chart](https://ui.bklit.com/docs/components/sankey-chart) | [/charts/sankey-chart](https://ui.bklit.com/charts/sankey-chart) |
|
||||
| `choropleth-chart` | Geo regions colored by value | `@bklit/choropleth-chart` | [/docs/components/choropleth-chart](https://ui.bklit.com/docs/components/choropleth-chart) | [/charts/choropleth-chart](https://ui.bklit.com/charts/choropleth-chart) |
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Run `npx shadcn@latest info --json` — verify `@bklit` registry and aliases.
|
||||
2. Pick a chart from the catalog (or ask the user what story the data tells).
|
||||
3. Open the doc URL for data shape and props.
|
||||
4. If not installed: `npx shadcn@latest add @bklit/<slug>`.
|
||||
5. Compose with grid, series, axes, tooltip — apply theming tokens.
|
||||
6. Point the user to the gallery or Studio URL for variant inspiration.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Project info
|
||||
npx shadcn@latest info --json
|
||||
|
||||
# Add a chart
|
||||
npx shadcn@latest add @bklit/line-chart
|
||||
|
||||
# Search registries (if configured)
|
||||
npx shadcn@latest search @bklit
|
||||
```
|
||||
|
||||
```tsx
|
||||
import { LineChart, Line, Grid, XAxis, ChartTooltip, chartCssVars } from "@bklitui/ui/charts";
|
||||
|
||||
<LineChart data={data} xDataKey="date">
|
||||
<Grid horizontal />
|
||||
<Line dataKey="users" stroke={chartCssVars.linePrimary} />
|
||||
<XAxis />
|
||||
<ChartTooltip />
|
||||
</LineChart>
|
||||
```
|
||||
|
||||
## Utility docs
|
||||
|
||||
- Theming: https://ui.bklit.com/docs/theming
|
||||
- Grid: https://ui.bklit.com/docs/utility/grid
|
||||
- Legend: https://ui.bklit.com/docs/utility/legend
|
||||
- Tooltip: https://ui.bklit.com/docs/utility/tooltip
|
||||
- Custom indicator: https://ui.bklit.com/docs/utility/custom-indicator
|
||||
- useChart: https://ui.bklit.com/docs/utility/use-chart
|
||||
|
||||
## Detailed References
|
||||
|
||||
- [composition.md](./rules/composition.md)
|
||||
- [theming.md](./rules/theming.md)
|
||||
- [animation.md](./rules/animation.md)
|
||||
- [tooltips.md](./rules/tooltips.md)
|
||||
- [installation.md](./rules/installation.md)
|
||||
42
.agents/skills/bklit-ui/rules/animation.md
Normal file
42
.agents/skills/bklit-ui/rules/animation.md
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Animation
|
||||
|
||||
## Defaults
|
||||
|
||||
Most cartesian charts default to ~1100ms enter animation. Bar charts often use staggered reveals (~1.2s total) with easing `cubic-bezier(0.85, 0, 0.15, 1)`.
|
||||
|
||||
## Replay enter animations
|
||||
|
||||
Pass a changing `revealSignature` (or remount the chart with a new `key`) to replay mount animations after prop changes.
|
||||
|
||||
### Correct
|
||||
|
||||
```tsx
|
||||
const [replayKey, setReplayKey] = useState(0);
|
||||
|
||||
<LineChart key={replayKey} data={data} revealSignature={String(replayKey)}>
|
||||
{/* ... */}
|
||||
</LineChart>
|
||||
|
||||
<button type="button" onClick={() => setReplayKey((k) => k + 1)}>
|
||||
Replay
|
||||
</button>
|
||||
```
|
||||
|
||||
## Live charts
|
||||
|
||||
- `LiveLineChart` runs its own animation loop — use `paused` to freeze updates for debugging.
|
||||
- Avoid nesting heavy state updates inside the rAF path; pass stable props where possible.
|
||||
|
||||
## Reduced motion
|
||||
|
||||
Respect `prefers-reduced-motion` when adding custom animation wrappers around charts. Bklit charts handle reduced motion internally for enter transitions.
|
||||
|
||||
## Performance
|
||||
|
||||
- Prefer CSS/SVG-friendly props over re-creating large data arrays every frame.
|
||||
- For streaming data, append points and trim the window instead of replacing the full array when possible.
|
||||
|
||||
## Inspiration
|
||||
|
||||
Browse animated variants: https://ui.bklit.com/charts/<chart-slug>
|
||||
Tune motion interactively: https://ui.bklit.com/studio?chart=<chart-slug>
|
||||
57
.agents/skills/bklit-ui/rules/composition.md
Normal file
57
.agents/skills/bklit-ui/rules/composition.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# Chart Composition
|
||||
|
||||
## Root + children
|
||||
|
||||
Charts use a composable API. Always wrap series, axes, and overlays inside the root chart component.
|
||||
|
||||
### Incorrect
|
||||
|
||||
```tsx
|
||||
<div className="h-80">
|
||||
<Line dataKey="users" />
|
||||
<XAxis />
|
||||
</div>
|
||||
```
|
||||
|
||||
### Correct
|
||||
|
||||
```tsx
|
||||
<LineChart data={data}>
|
||||
<Grid horizontal />
|
||||
<Line dataKey="users" />
|
||||
<XAxis />
|
||||
<ChartTooltip />
|
||||
</LineChart>
|
||||
```
|
||||
|
||||
## Axes and grid
|
||||
|
||||
- Put `Grid` before series so lines render on top.
|
||||
- Use `XAxis` / `YAxis` as siblings inside the root chart — not outside the chart context.
|
||||
- For live streaming charts, use `LiveXAxis` and `LiveYAxis` inside `LiveLineChart`.
|
||||
|
||||
## Tooltips and markers
|
||||
|
||||
- `ChartTooltip` must be a child of the root chart so it can read hover state.
|
||||
- Custom tooltip content receives chart context — prefer `useChart()` when building custom indicators.
|
||||
- `ChartMarkers` and marker content render inside `ChartTooltip` when showing event callouts.
|
||||
|
||||
## Multi-series charts
|
||||
|
||||
- One `Line` / `Bar` / `Area` child per `dataKey`.
|
||||
- Use `--chart-1` … `--chart-5` or explicit stroke/fill props for series differentiation.
|
||||
- For combined line + bar, use `ComposedChart` instead of nesting unrelated roots.
|
||||
|
||||
## Data shape
|
||||
|
||||
- Cartesian charts: array of objects; set `xDataKey` on the root (default `"date"`).
|
||||
- OHLC: use `CandlestickChart` with `{ date, open, high, low, close }`.
|
||||
- Live line: `{ time, value }` points with a separate `value` prop on the root.
|
||||
- Sankey / funnel / pie: follow each chart’s doc for node/link or slice shape.
|
||||
|
||||
## Docs
|
||||
|
||||
- Composition patterns: https://ui.bklit.com/docs/components/line-chart
|
||||
- `useChart` hook: https://ui.bklit.com/docs/utility/use-chart
|
||||
- Grid: https://ui.bklit.com/docs/utility/grid
|
||||
- Legend: https://ui.bklit.com/docs/utility/legend
|
||||
74
.agents/skills/bklit-ui/rules/installation.md
Normal file
74
.agents/skills/bklit-ui/rules/installation.md
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# Installation
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Bklit UI is a shadcn registry. Initialize shadcn in the project first:
|
||||
|
||||
```bash
|
||||
npx shadcn@latest init
|
||||
```
|
||||
|
||||
## Registry namespace
|
||||
|
||||
Add the `@bklit` namespace to `components.json` if it is not already present:
|
||||
|
||||
```json
|
||||
{
|
||||
"registries": {
|
||||
"@bklit": "https://ui.bklit.com/r/{name}.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
New projects scaffolded with Bklit often include this automatically.
|
||||
|
||||
## Install a chart
|
||||
|
||||
```bash
|
||||
npx shadcn@latest add @bklit/line-chart
|
||||
```
|
||||
|
||||
Replace `line-chart` with any chart slug from the catalog. The CLI copies source into your components directory and installs peer dependencies.
|
||||
|
||||
## Verify project context
|
||||
|
||||
```bash
|
||||
npx shadcn@latest info --json
|
||||
```
|
||||
|
||||
Use the JSON to confirm framework, aliases, Tailwind version, and which `@bklit` components are already installed.
|
||||
|
||||
## Import path
|
||||
|
||||
After install, import from the charts entry (path may vary by alias — check `info --json`):
|
||||
|
||||
```tsx
|
||||
import { LineChart, Line, Grid, XAxis, ChartTooltip } from "@/components/ui/line-chart";
|
||||
// or from package re-exports when using the npm package directly:
|
||||
import { LineChart, Line, Grid, XAxis, ChartTooltip } from "@bklitui/ui/charts";
|
||||
```
|
||||
|
||||
Prefer the import path your project’s shadcn install actually generated.
|
||||
|
||||
## Common peer dependencies
|
||||
|
||||
| Chart | Typical dependencies |
|
||||
|-------|---------------------|
|
||||
| line-chart, area-chart | `@visx/curve`, `@visx/shape`, `motion` |
|
||||
| bar-chart | `@visx/gradient`, `@visx/pattern`, `@visx/shape`, `motion` |
|
||||
| candlestick-chart | `@visx/scale`, `@visx/shape`, `@visx/responsive`, `d3-array`, `motion` |
|
||||
| live-line-chart | `@visx/curve`, `@visx/scale`, `@visx/shape`, `@visx/responsive`, `@visx/event`, `d3-array`, `motion` |
|
||||
| choropleth-chart | `@visx/geo`, `@visx/responsive`, `@visx/zoom`, `d3-geo`, `topojson-client`, `motion` |
|
||||
| sankey-chart | `@visx/gradient`, `@visx/pattern`, `@visx/responsive`, `@visx/sankey`, `motion` |
|
||||
| scatter-chart | `d3-scale`, `d3-array`, `motion`, `react-use-measure` |
|
||||
| pie-chart, ring-chart | `@visx/responsive`, `@visx/shape`, `motion` |
|
||||
| radar-chart | `@visx/responsive`, `d3-shape`, `motion` |
|
||||
| funnel-chart | `motion` |
|
||||
| gauge-chart | `@visx/responsive`, `motion` |
|
||||
|
||||
The CLI installs these when adding a chart — do not guess versions; let `shadcn add` resolve them.
|
||||
|
||||
## Docs
|
||||
|
||||
- Installation: https://ui.bklit.com/docs/installation
|
||||
- Per-chart install tabs: https://ui.bklit.com/docs/components/<slug>
|
||||
55
.agents/skills/bklit-ui/rules/theming.md
Normal file
55
.agents/skills/bklit-ui/rules/theming.md
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# Theming
|
||||
|
||||
## Use chartCssVars
|
||||
|
||||
Prefer the typed `chartCssVars` export over raw CSS variable strings.
|
||||
|
||||
### Incorrect
|
||||
|
||||
```tsx
|
||||
<line stroke="var(--chart-crosshair)" />
|
||||
<rect fill="var(--chart-indicator-color)" />
|
||||
```
|
||||
|
||||
### Correct
|
||||
|
||||
```tsx
|
||||
import { chartCssVars } from "@bklitui/ui/charts";
|
||||
|
||||
<line stroke={chartCssVars.crosshair} />
|
||||
<rect fill={chartCssVars.indicatorColor} />
|
||||
```
|
||||
|
||||
## Series colors
|
||||
|
||||
- Multi-series: `var(--chart-1)` through `var(--chart-5)` or theme tokens.
|
||||
- Line charts: `var(--chart-line-primary)` / `var(--chart-line-secondary)` for default strokes.
|
||||
- Do not hardcode hex colors unless the design explicitly requires brand colors outside the theme.
|
||||
|
||||
## Tooltip and badge surfaces
|
||||
|
||||
Tooltip boxes and live value badges should use shadcn popover tokens so text stays readable in light and dark mode:
|
||||
|
||||
```tsx
|
||||
// Tooltip content / badges
|
||||
className="bg-popover text-popover-foreground"
|
||||
```
|
||||
|
||||
## Dark mode
|
||||
|
||||
Override chart variables in `:root` and `.dark` — do not sprinkle `dark:` on individual chart SVG elements.
|
||||
|
||||
```css
|
||||
:root {
|
||||
--chart-background: oklch(1 0 0);
|
||||
--chart-grid: oklch(0.9 0 0);
|
||||
}
|
||||
.dark {
|
||||
--chart-background: oklch(0.145 0.004 285);
|
||||
--chart-grid: oklch(0.25 0 0);
|
||||
}
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
- Full theming guide: https://ui.bklit.com/docs/theming
|
||||
71
.agents/skills/bklit-ui/rules/tooltips.md
Normal file
71
.agents/skills/bklit-ui/rules/tooltips.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Tooltips
|
||||
|
||||
## Default tooltip
|
||||
|
||||
Use `ChartTooltip` as a child of the root chart. Defaults include crosshair, dots, and date pill where applicable.
|
||||
|
||||
```tsx
|
||||
<LineChart data={data}>
|
||||
<Line dataKey="users" />
|
||||
<XAxis />
|
||||
<ChartTooltip />
|
||||
</LineChart>
|
||||
```
|
||||
|
||||
## Custom content
|
||||
|
||||
```tsx
|
||||
<ChartTooltip
|
||||
content={({ point, formattedDate }) => (
|
||||
<div className="rounded-md bg-popover px-3 py-2 text-popover-foreground text-sm">
|
||||
<div className="font-medium">{formattedDate}</div>
|
||||
<div>{point.users as number} users</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
```
|
||||
|
||||
## indicatorColor (candlestick and crosshair)
|
||||
|
||||
Match the crosshair to the hovered datum — e.g. green/red candles:
|
||||
|
||||
```tsx
|
||||
<ChartTooltip
|
||||
indicatorColor={(point) =>
|
||||
(point.close as number) >= (point.open as number)
|
||||
? "var(--chart-1)"
|
||||
: "var(--chart-3)"
|
||||
}
|
||||
showDots={false}
|
||||
/>
|
||||
```
|
||||
|
||||
## Custom indicators
|
||||
|
||||
For advanced crosshair/markers, use `useChart()` and the patterns in the custom indicator docs.
|
||||
|
||||
### Incorrect
|
||||
|
||||
```tsx
|
||||
// Reading mouse position manually outside chart context
|
||||
const [x, setX] = useState(0);
|
||||
useEffect(() => {
|
||||
window.addEventListener("mousemove", ...);
|
||||
}, []);
|
||||
```
|
||||
|
||||
### Correct
|
||||
|
||||
```tsx
|
||||
import { useChart } from "@bklitui/ui/charts";
|
||||
|
||||
function CustomIndicator() {
|
||||
const { tooltipData } = useChart();
|
||||
// render from tooltipData
|
||||
}
|
||||
```
|
||||
|
||||
## Docs
|
||||
|
||||
- ChartTooltip: https://ui.bklit.com/docs/utility/tooltip
|
||||
- Custom indicators: https://ui.bklit.com/docs/utility/custom-indicator
|
||||
299
.agents/skills/pr-open/SKILL.md
Normal file
299
.agents/skills/pr-open/SKILL.md
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
---
|
||||
name: pr-open
|
||||
description: |
|
||||
Open a pull request the bklit-ui way: stage and commit with pre-commit hooks,
|
||||
run ultracite from the repo root, rebuild the shadcn registry, run a production test
|
||||
build, fix failures, push, and create a PR with a structured summary. Use when the
|
||||
user asks to commit, push,
|
||||
open a PR, "ship it", or run the full pre-PR checklist.
|
||||
---
|
||||
|
||||
# PR Open Skill
|
||||
|
||||
End-to-end workflow for committing work and opening a merge-ready PR in **bklit-ui**.
|
||||
|
||||
Read this skill when the user wants to add, commit, validate, push, and open a PR — or references `@pr-open`.
|
||||
|
||||
---
|
||||
|
||||
## Before you start
|
||||
|
||||
1. **Confirm branch context**
|
||||
- If the user merged an earlier PR on this branch and there are new changes, branch from latest `main` instead of stacking on a stale feature branch:
|
||||
```bash
|
||||
git fetch origin main
|
||||
git checkout -b <new-branch-name> origin/main
|
||||
```
|
||||
- Re-apply or cherry-pick only the intended changes; do not commit unrelated `registry:build` noise (e.g. reformatted `packages/ui/registry/examples/*` unless those edits are intentional).
|
||||
|
||||
2. **Never commit secrets** — skip `.env`, credentials, API keys, etc. Warn the user if they ask to commit them.
|
||||
|
||||
3. **Git safety** (required)
|
||||
- Do not update git config.
|
||||
- Do not run destructive commands (`push --force`, `reset --hard`) unless the user explicitly asks.
|
||||
- Do not skip hooks (`--no-verify`) unless the user explicitly asks.
|
||||
- Do not `git commit --amend` unless all amend rules in the user's git rules are satisfied (HEAD commit is yours, not pushed, or user requested amend).
|
||||
- If a commit **fails** due to a hook, fix the issue and create a **new** commit — do not amend a failed commit.
|
||||
- Use HEREDOC for commit messages (see below).
|
||||
- Do not push unless the user asked to push or open a PR.
|
||||
|
||||
---
|
||||
|
||||
## Step 1 — Inspect changes
|
||||
|
||||
Run in parallel from the repo root:
|
||||
|
||||
```bash
|
||||
git status
|
||||
git diff
|
||||
git diff --staged
|
||||
git log -5 --oneline
|
||||
```
|
||||
|
||||
If opening a PR, also check divergence from base:
|
||||
|
||||
```bash
|
||||
git fetch origin main
|
||||
git log origin/main..HEAD --oneline
|
||||
git diff origin/main...HEAD --stat
|
||||
```
|
||||
|
||||
Draft a commit message that explains **why**, not just what. Match recent repo style (e.g. `feat(charts): …`, `fix(web): …`).
|
||||
|
||||
---
|
||||
|
||||
## Step 2 — Stage and commit (pre-commit loop)
|
||||
|
||||
### Stage
|
||||
|
||||
```bash
|
||||
git add <paths> # prefer explicit paths over blind git add -A
|
||||
```
|
||||
|
||||
### Commit
|
||||
|
||||
```bash
|
||||
git commit -m "$(cat <<'EOF'
|
||||
<subject line>
|
||||
|
||||
<optional body — 1–2 sentences on why>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
Husky **pre-commit** runs `npx ultracite fix` (see `.husky/pre-commit`).
|
||||
|
||||
### If pre-commit fails or leaves unstaged fixes
|
||||
|
||||
1. Read the hook output and fix every reported issue (lint correctness, nested ternaries, `biome-ignore` only when justified, etc.).
|
||||
2. Re-stage affected files: `git add <paths>`
|
||||
3. Commit again with a **new** commit (or amend only if amend rules allow and the previous commit succeeded but the hook auto-modified files).
|
||||
|
||||
Repeat until `git commit` succeeds **and** `git status` is clean (no leftover hook formatting).
|
||||
|
||||
---
|
||||
|
||||
## Step 3 — Ultracite from repo root
|
||||
|
||||
After commits are clean, run the root pnpm scripts (not `npx ultracite` directly unless fixing a one-off):
|
||||
|
||||
```bash
|
||||
pnpm lint # ultracite check
|
||||
```
|
||||
|
||||
If check fails:
|
||||
|
||||
```bash
|
||||
pnpm lint:fix # ultracite fix (same as pnpm format)
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
1. Review `git diff` for unexpected changes.
|
||||
2. If files changed: `git add <paths>` → `git commit -m "$(cat <<'EOF'
|
||||
Fix lint issues from ultracite
|
||||
EOF
|
||||
)"`
|
||||
3. Re-run `pnpm lint` until it passes with no fixes pending.
|
||||
|
||||
Do not open a PR while `pnpm lint` fails or while lint fixes are unstaged.
|
||||
|
||||
---
|
||||
|
||||
## Step 4 — Rebuild shadcn registry (required)
|
||||
|
||||
**Always** run this before the production build on every PR — not only when `packages/ui` changed. Registry artifacts under `apps/web/public/r/` must stay in sync with the committed UI package.
|
||||
|
||||
From the repo root:
|
||||
|
||||
```bash
|
||||
pnpm registry:build
|
||||
```
|
||||
|
||||
This updates `apps/web/public/r/` from `packages/ui` (see `packages/ui/package.json` → `registry:build`).
|
||||
|
||||
1. Review `git diff` after the build. **Include** intentional changes under `apps/web/public/r/`.
|
||||
2. **Do not** commit incidental reformats on `packages/ui/registry/examples/*` unless those edits are intentional.
|
||||
3. If `apps/web/public/r/` (or `packages/ui/registry.json` when you edited it) changed:
|
||||
|
||||
```bash
|
||||
git add apps/web/public/r packages/ui/registry.json
|
||||
git commit -m "$(cat <<'EOF'
|
||||
chore(registry): rebuild shadcn registry for PR
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
4. Re-run **Step 3** (`pnpm lint`) if any source files changed outside `public/r/`.
|
||||
|
||||
Do not open a PR while registry outputs are stale (uncommitted `apps/web/public/r/` diffs after `registry:build`).
|
||||
|
||||
---
|
||||
|
||||
## Step 5 — Test build
|
||||
|
||||
Run a production build to catch type and compile errors before the PR.
|
||||
|
||||
**Default (whole monorepo):**
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
**Web app only** (faster when changes are confined to `apps/web`):
|
||||
|
||||
```bash
|
||||
cd apps/web && pnpm build
|
||||
```
|
||||
|
||||
If the web build OOMs in dev/CI-like environments, retry with more heap:
|
||||
|
||||
```bash
|
||||
cd apps/web && NODE_OPTIONS='--max-old-space-size=8192' pnpm build
|
||||
```
|
||||
|
||||
**Optional but recommended** when touching TypeScript:
|
||||
|
||||
```bash
|
||||
pnpm check-types
|
||||
```
|
||||
|
||||
### If build fails
|
||||
|
||||
1. Fix the root cause (types, imports, missing registry files, etc.).
|
||||
2. Re-run the failing command until it passes.
|
||||
3. Commit fixes with a clear message, then re-run **Step 3** (`pnpm lint`) and **Step 4** (`pnpm registry:build`) if source files under `packages/ui` changed.
|
||||
|
||||
---
|
||||
|
||||
## Step 6 — Push
|
||||
|
||||
Only when the user asked to push or open a PR:
|
||||
|
||||
```bash
|
||||
git push -u origin HEAD
|
||||
```
|
||||
|
||||
If the branch was already pushed and you added commits, `git push` is enough.
|
||||
|
||||
---
|
||||
|
||||
## Step 7 — Open the PR
|
||||
|
||||
Use GitHub CLI from the repo root:
|
||||
|
||||
```bash
|
||||
gh pr create --title "<PR title>" --body "$(cat <<'EOF'
|
||||
## Summary
|
||||
|
||||
- <bullet: what changed and why>
|
||||
- <bullet: user-facing impact>
|
||||
- <bullet: follow-ups or deploy notes if any>
|
||||
|
||||
## Test plan
|
||||
|
||||
- [ ] `pnpm lint` passes at repo root
|
||||
- [ ] `pnpm registry:build` run; `apps/web/public/r/` committed if updated
|
||||
- [ ] `pnpm build` (or `apps/web` build for web-only changes)
|
||||
- [ ] <manual verification step 1>
|
||||
- [ ] <manual verification step 2>
|
||||
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
Return the PR URL to the user.
|
||||
|
||||
### PR title guidelines
|
||||
|
||||
- Short, imperative, scoped (e.g. `Add @bklit/chart-animation registry item`)
|
||||
- Match the primary commit subject when possible
|
||||
|
||||
### PR body template (copy structure every time)
|
||||
|
||||
```markdown
|
||||
## Summary
|
||||
|
||||
- <1–3 bullets: what and why>
|
||||
|
||||
## Test plan
|
||||
|
||||
- [ ] `pnpm lint` passes at repo root
|
||||
- [ ] `pnpm registry:build` run; registry outputs committed if changed
|
||||
- [ ] Production build succeeds (`pnpm build` or scoped web build)
|
||||
- [ ] <feature-specific check>
|
||||
- [ ] <regression check if applicable>
|
||||
```
|
||||
|
||||
Add extra sections only when useful:
|
||||
|
||||
- **Context** — link to a merged PR or issue (e.g. "Follow-up to #70")
|
||||
- **Deploy notes** — e.g. registry JSON must be live on `ui.bklit.com` for Open in v0
|
||||
|
||||
### Repo-specific PR notes
|
||||
|
||||
- **Registry**: Step 4 always runs `pnpm registry:build`; commit `apps/web/public/r/` when the build updates it.
|
||||
- **Do not** commit incidental reformats from `registry:build` on `packages/ui/registry/examples/*` unless intentional.
|
||||
- Chart/docs work: mention manual checks for docs pages, Studio, or Open in v0 when relevant.
|
||||
|
||||
---
|
||||
|
||||
## Full checklist (quick reference)
|
||||
|
||||
| Step | Command / action | Must pass before next step |
|
||||
|------|------------------|----------------------------|
|
||||
| 1 | `git status` / `git diff` / `git log` | Understand scope |
|
||||
| 2 | `git add` → `git commit` | Pre-commit hook clean; working tree clean |
|
||||
| 3 | `pnpm lint` → `pnpm lint:fix` if needed → re-commit | `pnpm lint` exits 0 |
|
||||
| 4 | `pnpm registry:build` → commit `apps/web/public/r/` if changed | No stale registry diff |
|
||||
| 5 | `pnpm build` (and `pnpm check-types` if TS changed) | Build exits 0 |
|
||||
| 6 | `git push -u origin HEAD` | Only if user requested push/PR |
|
||||
| 7 | `gh pr create` with Summary + Test plan | PR URL returned |
|
||||
|
||||
---
|
||||
|
||||
## Common failures
|
||||
|
||||
| Failure | Fix |
|
||||
|---------|-----|
|
||||
| Pre-commit biome errors | Fix code; add `biome-ignore` only with a one-line justification |
|
||||
| Hook fixed files but commit already succeeded | `git add` + new commit (or amend if allowed) |
|
||||
| `pnpm lint` fails after commit | `pnpm lint:fix`, review diff, commit, re-run `pnpm lint` |
|
||||
| `Can't resolve './animation'` in registry consumers | Add missing `@bklit/*` registry deps; run `pnpm registry:build` |
|
||||
| Web build OOM | `NODE_OPTIONS='--max-old-space-size=8192'` for `apps/web` build |
|
||||
| PR branch already merged | New branch from `origin/main`, re-apply only new changes |
|
||||
|
||||
---
|
||||
|
||||
## Example flow
|
||||
|
||||
User: "commit this and open a PR"
|
||||
|
||||
1. `git status` + `git diff` + `git log -3`
|
||||
2. `git add apps/web packages/ui` → `git commit` (fix pre-commit until clean)
|
||||
3. `pnpm lint` → `pnpm lint:fix` if needed → commit → `pnpm lint`
|
||||
4. `pnpm registry:build` → commit `apps/web/public/r/` if changed
|
||||
5. `pnpm build` (fix until green) → commit if needed
|
||||
6. `git push -u origin HEAD`
|
||||
7. `gh pr create` with Summary + Test plan template
|
||||
8. Reply with PR link and one-line summary of what was validated
|
||||
914
.agents/skills/turborepo/SKILL.md
Normal file
914
.agents/skills/turborepo/SKILL.md
Normal file
|
|
@ -0,0 +1,914 @@
|
|||
---
|
||||
name: turborepo
|
||||
description: |
|
||||
Turborepo monorepo build system guidance. Triggers on: turbo.json, task pipelines,
|
||||
dependsOn, caching, remote cache, the "turbo" CLI, --filter, --affected, CI optimization, environment
|
||||
variables, internal packages, monorepo structure/best practices, and boundaries.
|
||||
|
||||
Use when user: configures tasks/workflows/pipelines, creates packages, sets up
|
||||
monorepo, shares code between apps, runs changed/affected packages, debugs cache,
|
||||
or has apps/packages directories.
|
||||
metadata:
|
||||
version: 2.7.6-canary.3
|
||||
---
|
||||
|
||||
# Turborepo Skill
|
||||
|
||||
Build system for JavaScript/TypeScript monorepos. Turborepo caches task outputs and runs tasks in parallel based on dependency graph.
|
||||
|
||||
## IMPORTANT: Package Tasks, Not Root Tasks
|
||||
|
||||
**DO NOT create Root Tasks. ALWAYS create package tasks.**
|
||||
|
||||
When creating tasks/scripts/pipelines, you MUST:
|
||||
|
||||
1. Add the script to each relevant package's `package.json`
|
||||
2. Register the task in root `turbo.json`
|
||||
3. Root `package.json` only delegates via `turbo run <task>`
|
||||
|
||||
**DO NOT** put task logic in root `package.json`. This defeats Turborepo's parallelization.
|
||||
|
||||
```json
|
||||
// DO THIS: Scripts in each package
|
||||
// apps/web/package.json
|
||||
{ "scripts": { "build": "next build", "lint": "eslint .", "test": "vitest" } }
|
||||
|
||||
// apps/api/package.json
|
||||
{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }
|
||||
|
||||
// packages/ui/package.json
|
||||
{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }
|
||||
```
|
||||
|
||||
```json
|
||||
// turbo.json - register tasks
|
||||
{
|
||||
"tasks": {
|
||||
"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
|
||||
"lint": {},
|
||||
"test": { "dependsOn": ["build"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// Root package.json - ONLY delegates, no task logic
|
||||
{
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"lint": "turbo run lint",
|
||||
"test": "turbo run test"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// DO NOT DO THIS - defeats parallelization
|
||||
// Root package.json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "cd apps/web && next build && cd ../api && tsc",
|
||||
"lint": "eslint apps/ packages/",
|
||||
"test": "vitest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Root Tasks (`//#taskname`) are ONLY for tasks that truly cannot exist in packages (rare).
|
||||
|
||||
## Secondary Rule: `turbo run` vs `turbo`
|
||||
|
||||
**Always use `turbo run` when the command is written into code:**
|
||||
|
||||
```json
|
||||
// package.json - ALWAYS "turbo run"
|
||||
{
|
||||
"scripts": {
|
||||
"build": "turbo run build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
# CI workflows - ALWAYS "turbo run"
|
||||
- run: turbo run build --affected
|
||||
```
|
||||
|
||||
**The shorthand `turbo <tasks>` is ONLY for one-off terminal commands** typed directly by humans or agents. Never write `turbo build` into package.json, CI, or scripts.
|
||||
|
||||
## Quick Decision Trees
|
||||
|
||||
### "I need to configure a task"
|
||||
|
||||
```
|
||||
Configure a task?
|
||||
├─ Define task dependencies → references/configuration/tasks.md
|
||||
├─ Lint/check-types (parallel + caching) → Use Transit Nodes pattern (see below)
|
||||
├─ Specify build outputs → references/configuration/tasks.md#outputs
|
||||
├─ Handle environment variables → references/environment/README.md
|
||||
├─ Set up dev/watch tasks → references/configuration/tasks.md#persistent
|
||||
├─ Package-specific config → references/configuration/README.md#package-configurations
|
||||
└─ Global settings (cacheDir, daemon) → references/configuration/global-options.md
|
||||
```
|
||||
|
||||
### "My cache isn't working"
|
||||
|
||||
```
|
||||
Cache problems?
|
||||
├─ Tasks run but outputs not restored → Missing `outputs` key
|
||||
├─ Cache misses unexpectedly → references/caching/gotchas.md
|
||||
├─ Need to debug hash inputs → Use --summarize or --dry
|
||||
├─ Want to skip cache entirely → Use --force or cache: false
|
||||
├─ Remote cache not working → references/caching/remote-cache.md
|
||||
└─ Environment causing misses → references/environment/gotchas.md
|
||||
```
|
||||
|
||||
### "I want to run only changed packages"
|
||||
|
||||
```
|
||||
Run only what changed?
|
||||
├─ Changed packages + dependents (RECOMMENDED) → turbo run build --affected
|
||||
├─ Custom base branch → --affected --affected-base=origin/develop
|
||||
├─ Manual git comparison → --filter=...[origin/main]
|
||||
└─ See all filter options → references/filtering/README.md
|
||||
```
|
||||
|
||||
**`--affected` is the primary way to run only changed packages.** It automatically compares against the default branch and includes dependents.
|
||||
|
||||
### "I want to filter packages"
|
||||
|
||||
```
|
||||
Filter packages?
|
||||
├─ Only changed packages → --affected (see above)
|
||||
├─ By package name → --filter=web
|
||||
├─ By directory → --filter=./apps/*
|
||||
├─ Package + dependencies → --filter=web...
|
||||
├─ Package + dependents → --filter=...web
|
||||
└─ Complex combinations → references/filtering/patterns.md
|
||||
```
|
||||
|
||||
### "Environment variables aren't working"
|
||||
|
||||
```
|
||||
Environment issues?
|
||||
├─ Vars not available at runtime → Strict mode filtering (default)
|
||||
├─ Cache hits with wrong env → Var not in `env` key
|
||||
├─ .env changes not causing rebuilds → .env not in `inputs`
|
||||
├─ CI variables missing → references/environment/gotchas.md
|
||||
└─ Framework vars (NEXT_PUBLIC_*) → Auto-included via inference
|
||||
```
|
||||
|
||||
### "I need to set up CI"
|
||||
|
||||
```
|
||||
CI setup?
|
||||
├─ GitHub Actions → references/ci/github-actions.md
|
||||
├─ Vercel deployment → references/ci/vercel.md
|
||||
├─ Remote cache in CI → references/caching/remote-cache.md
|
||||
├─ Only build changed packages → --affected flag
|
||||
├─ Skip unnecessary builds → turbo-ignore (references/cli/commands.md)
|
||||
└─ Skip container setup when no changes → turbo-ignore
|
||||
```
|
||||
|
||||
### "I want to watch for changes during development"
|
||||
|
||||
```
|
||||
Watch mode?
|
||||
├─ Re-run tasks on change → turbo watch (references/watch/README.md)
|
||||
├─ Dev servers with dependencies → Use `with` key (references/configuration/tasks.md#with)
|
||||
├─ Restart dev server on dep change → Use `interruptible: true`
|
||||
└─ Persistent dev tasks → Use `persistent: true`
|
||||
```
|
||||
|
||||
### "I need to create/structure a package"
|
||||
|
||||
```
|
||||
Package creation/structure?
|
||||
├─ Create an internal package → references/best-practices/packages.md
|
||||
├─ Repository structure → references/best-practices/structure.md
|
||||
├─ Dependency management → references/best-practices/dependencies.md
|
||||
├─ Best practices overview → references/best-practices/README.md
|
||||
├─ JIT vs Compiled packages → references/best-practices/packages.md#compilation-strategies
|
||||
└─ Sharing code between apps → references/best-practices/README.md#package-types
|
||||
```
|
||||
|
||||
### "How should I structure my monorepo?"
|
||||
|
||||
```
|
||||
Monorepo structure?
|
||||
├─ Standard layout (apps/, packages/) → references/best-practices/README.md
|
||||
├─ Package types (apps vs libraries) → references/best-practices/README.md#package-types
|
||||
├─ Creating internal packages → references/best-practices/packages.md
|
||||
├─ TypeScript configuration → references/best-practices/structure.md#typescript-configuration
|
||||
├─ ESLint configuration → references/best-practices/structure.md#eslint-configuration
|
||||
├─ Dependency management → references/best-practices/dependencies.md
|
||||
└─ Enforce package boundaries → references/boundaries/README.md
|
||||
```
|
||||
|
||||
### "I want to enforce architectural boundaries"
|
||||
|
||||
```
|
||||
Enforce boundaries?
|
||||
├─ Check for violations → turbo boundaries
|
||||
├─ Tag packages → references/boundaries/README.md#tags
|
||||
├─ Restrict which packages can import others → references/boundaries/README.md#rule-types
|
||||
└─ Prevent cross-package file imports → references/boundaries/README.md
|
||||
```
|
||||
|
||||
## Critical Anti-Patterns
|
||||
|
||||
### Using `turbo` Shorthand in Code
|
||||
|
||||
**`turbo run` is recommended in package.json scripts and CI pipelines.** The shorthand `turbo <task>` is intended for interactive terminal use.
|
||||
|
||||
```json
|
||||
// WRONG - using shorthand in package.json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "turbo build",
|
||||
"dev": "turbo dev"
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT
|
||||
{
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"dev": "turbo run dev"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```yaml
|
||||
# WRONG - using shorthand in CI
|
||||
- run: turbo build --affected
|
||||
|
||||
# CORRECT
|
||||
- run: turbo run build --affected
|
||||
```
|
||||
|
||||
### Root Scripts Bypassing Turbo
|
||||
|
||||
Root `package.json` scripts MUST delegate to `turbo run`, not run tasks directly.
|
||||
|
||||
```json
|
||||
// WRONG - bypasses turbo entirely
|
||||
{
|
||||
"scripts": {
|
||||
"build": "bun build",
|
||||
"dev": "bun dev"
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT - delegates to turbo
|
||||
{
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"dev": "turbo run dev"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using `&&` to Chain Turbo Tasks
|
||||
|
||||
Don't chain turbo tasks with `&&`. Let turbo orchestrate.
|
||||
|
||||
```json
|
||||
// WRONG - turbo task not using turbo run
|
||||
{
|
||||
"scripts": {
|
||||
"changeset:publish": "bun build && changeset publish"
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT
|
||||
{
|
||||
"scripts": {
|
||||
"changeset:publish": "turbo run build && changeset publish"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `prebuild` Scripts That Manually Build Dependencies
|
||||
|
||||
Scripts like `prebuild` that manually build other packages bypass Turborepo's dependency graph.
|
||||
|
||||
```json
|
||||
// WRONG - manually building dependencies
|
||||
{
|
||||
"scripts": {
|
||||
"prebuild": "cd ../../packages/types && bun run build && cd ../utils && bun run build",
|
||||
"build": "next build"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**However, the fix depends on whether workspace dependencies are declared:**
|
||||
|
||||
1. **If dependencies ARE declared** (e.g., `"@repo/types": "workspace:*"` in package.json), remove the `prebuild` script. Turbo's `dependsOn: ["^build"]` handles this automatically.
|
||||
|
||||
2. **If dependencies are NOT declared**, the `prebuild` exists because `^build` won't trigger without a dependency relationship. The fix is to:
|
||||
- Add the dependency to package.json: `"@repo/types": "workspace:*"`
|
||||
- Then remove the `prebuild` script
|
||||
|
||||
```json
|
||||
// CORRECT - declare dependency, let turbo handle build order
|
||||
// package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"@repo/types": "workspace:*",
|
||||
"@repo/utils": "workspace:*"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "next build"
|
||||
}
|
||||
}
|
||||
|
||||
// turbo.json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key insight:** `^build` only runs build in packages listed as dependencies. No dependency declaration = no automatic build ordering.
|
||||
|
||||
### Overly Broad `globalDependencies`
|
||||
|
||||
`globalDependencies` affects ALL tasks in ALL packages. Be specific.
|
||||
|
||||
```json
|
||||
// WRONG - heavy hammer, affects all hashes
|
||||
{
|
||||
"globalDependencies": ["**/.env.*local"]
|
||||
}
|
||||
|
||||
// BETTER - move to task-level inputs
|
||||
{
|
||||
"globalDependencies": [".env"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
||||
"outputs": ["dist/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Repetitive Task Configuration
|
||||
|
||||
Look for repeated configuration across tasks that can be collapsed. Turborepo supports shared configuration patterns.
|
||||
|
||||
```json
|
||||
// WRONG - repetitive env and inputs across tasks
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": ["API_URL", "DATABASE_URL"],
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"]
|
||||
},
|
||||
"test": {
|
||||
"env": ["API_URL", "DATABASE_URL"],
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"]
|
||||
},
|
||||
"dev": {
|
||||
"env": ["API_URL", "DATABASE_URL"],
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BETTER - use globalEnv and globalDependencies for shared config
|
||||
{
|
||||
"globalEnv": ["API_URL", "DATABASE_URL"],
|
||||
"globalDependencies": [".env*"],
|
||||
"tasks": {
|
||||
"build": {},
|
||||
"test": {},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**When to use global vs task-level:**
|
||||
|
||||
- `globalEnv` / `globalDependencies` - affects ALL tasks, use for truly shared config
|
||||
- Task-level `env` / `inputs` - use when only specific tasks need it
|
||||
|
||||
### NOT an Anti-Pattern: Large `env` Arrays
|
||||
|
||||
A large `env` array (even 50+ variables) is **not** a problem. It usually means the user was thorough about declaring their build's environment dependencies. Do not flag this as an issue.
|
||||
|
||||
### Using `--parallel` Flag
|
||||
|
||||
The `--parallel` flag bypasses Turborepo's dependency graph. If tasks need parallel execution, configure `dependsOn` correctly instead.
|
||||
|
||||
```bash
|
||||
# WRONG - bypasses dependency graph
|
||||
turbo run lint --parallel
|
||||
|
||||
# CORRECT - configure tasks to allow parallel execution
|
||||
# In turbo.json, set dependsOn appropriately (or use transit nodes)
|
||||
turbo run lint
|
||||
```
|
||||
|
||||
### Package-Specific Task Overrides in Root turbo.json
|
||||
|
||||
When multiple packages need different task configurations, use **Package Configurations** (`turbo.json` in each package) instead of cluttering root `turbo.json` with `package#task` overrides.
|
||||
|
||||
```json
|
||||
// WRONG - root turbo.json with many package-specific overrides
|
||||
{
|
||||
"tasks": {
|
||||
"test": { "dependsOn": ["build"] },
|
||||
"@repo/web#test": { "outputs": ["coverage/**"] },
|
||||
"@repo/api#test": { "outputs": ["coverage/**"] },
|
||||
"@repo/utils#test": { "outputs": [] },
|
||||
"@repo/cli#test": { "outputs": [] },
|
||||
"@repo/core#test": { "outputs": [] }
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT - use Package Configurations
|
||||
// Root turbo.json - base config only
|
||||
{
|
||||
"tasks": {
|
||||
"test": { "dependsOn": ["build"] }
|
||||
}
|
||||
}
|
||||
|
||||
// packages/web/turbo.json - package-specific override
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"test": { "outputs": ["coverage/**"] }
|
||||
}
|
||||
}
|
||||
|
||||
// packages/api/turbo.json
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"test": { "outputs": ["coverage/**"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits of Package Configurations:**
|
||||
|
||||
- Keeps configuration close to the code it affects
|
||||
- Root turbo.json stays clean and focused on base patterns
|
||||
- Easier to understand what's special about each package
|
||||
- Works with `$TURBO_EXTENDS$` to inherit + extend arrays
|
||||
|
||||
**When to use `package#task` in root:**
|
||||
|
||||
- Single package needs a unique dependency (e.g., `"deploy": { "dependsOn": ["web#build"] }`)
|
||||
- Temporary override while migrating
|
||||
|
||||
See `references/configuration/README.md#package-configurations` for full details.
|
||||
|
||||
### Using `../` to Traverse Out of Package in `inputs`
|
||||
|
||||
Don't use relative paths like `../` to reference files outside the package. Use `$TURBO_ROOT$` instead.
|
||||
|
||||
```json
|
||||
// WRONG - traversing out of package
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": ["$TURBO_DEFAULT$", "../shared-config.json"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT - use $TURBO_ROOT$ for repo root
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Missing `outputs` for File-Producing Tasks
|
||||
|
||||
**Before flagging missing `outputs`, check what the task actually produces:**
|
||||
|
||||
1. Read the package's script (e.g., `"build": "tsc"`, `"test": "vitest"`)
|
||||
2. Determine if it writes files to disk or only outputs to stdout
|
||||
3. Only flag if the task produces files that should be cached
|
||||
|
||||
```json
|
||||
// WRONG: build produces files but they're not cached
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT: build outputs are cached
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Common outputs by framework:
|
||||
|
||||
- Next.js: `[".next/**", "!.next/cache/**"]`
|
||||
- Vite/Rollup: `["dist/**"]`
|
||||
- tsc: `["dist/**"]` or custom `outDir`
|
||||
|
||||
**TypeScript `--noEmit` can still produce cache files:**
|
||||
|
||||
When `incremental: true` in tsconfig.json, `tsc --noEmit` writes `.tsbuildinfo` files even without emitting JS. Check the tsconfig before assuming no outputs:
|
||||
|
||||
```json
|
||||
// If tsconfig has incremental: true, tsc --noEmit produces cache files
|
||||
{
|
||||
"tasks": {
|
||||
"typecheck": {
|
||||
"outputs": ["node_modules/.cache/tsbuildinfo.json"] // or wherever tsBuildInfoFile points
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To determine correct outputs for TypeScript tasks:
|
||||
|
||||
1. Check if `incremental` or `composite` is enabled in tsconfig
|
||||
2. Check `tsBuildInfoFile` for custom cache location (default: alongside `outDir` or in project root)
|
||||
3. If no incremental mode, `tsc --noEmit` produces no files
|
||||
|
||||
### `^build` vs `build` Confusion
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
// ^build = run build in DEPENDENCIES first (other packages this one imports)
|
||||
"build": {
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
// build (no ^) = run build in SAME PACKAGE first
|
||||
"test": {
|
||||
"dependsOn": ["build"]
|
||||
},
|
||||
// pkg#task = specific package's task
|
||||
"deploy": {
|
||||
"dependsOn": ["web#build"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Variables Not Hashed
|
||||
|
||||
```json
|
||||
// WRONG: API_URL changes won't cause rebuilds
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"outputs": ["dist/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT: API_URL changes invalidate cache
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"outputs": ["dist/**"],
|
||||
"env": ["API_URL", "API_KEY"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `.env` Files Not in Inputs
|
||||
|
||||
Turbo does NOT load `.env` files - your framework does. But Turbo needs to know about changes:
|
||||
|
||||
```json
|
||||
// WRONG: .env changes don't invalidate cache
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": ["API_URL"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT: .env file changes invalidate cache
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": ["API_URL"],
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Root `.env` File in Monorepo
|
||||
|
||||
A `.env` file at the repo root is an anti-pattern — even for small monorepos or starter templates. It creates implicit coupling between packages and makes it unclear which packages depend on which variables.
|
||||
|
||||
```
|
||||
// WRONG - root .env affects all packages implicitly
|
||||
my-monorepo/
|
||||
├── .env # Which packages use this?
|
||||
├── apps/
|
||||
│ ├── web/
|
||||
│ └── api/
|
||||
└── packages/
|
||||
|
||||
// CORRECT - .env files in packages that need them
|
||||
my-monorepo/
|
||||
├── apps/
|
||||
│ ├── web/
|
||||
│ │ └── .env # Clear: web needs DATABASE_URL
|
||||
│ └── api/
|
||||
│ └── .env # Clear: api needs API_KEY
|
||||
└── packages/
|
||||
```
|
||||
|
||||
**Problems with root `.env`:**
|
||||
|
||||
- Unclear which packages consume which variables
|
||||
- All packages get all variables (even ones they don't need)
|
||||
- Cache invalidation is coarse-grained (root .env change invalidates everything)
|
||||
- Security risk: packages may accidentally access sensitive vars meant for others
|
||||
- Bad habits start small — starter templates should model correct patterns
|
||||
|
||||
**If you must share variables**, use `globalEnv` to be explicit about what's shared, and document why.
|
||||
|
||||
### Strict Mode Filtering CI Variables
|
||||
|
||||
By default, Turborepo filters environment variables to only those in `env`/`globalEnv`. CI variables may be missing:
|
||||
|
||||
```json
|
||||
// If CI scripts need GITHUB_TOKEN but it's not in env:
|
||||
{
|
||||
"globalPassThroughEnv": ["GITHUB_TOKEN", "CI"],
|
||||
"tasks": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
Or use `--env-mode=loose` (not recommended for production).
|
||||
|
||||
### Shared Code in Apps (Should Be a Package)
|
||||
|
||||
```
|
||||
// WRONG: Shared code inside an app
|
||||
apps/
|
||||
web/
|
||||
shared/ # This breaks monorepo principles!
|
||||
utils.ts
|
||||
|
||||
// CORRECT: Extract to a package
|
||||
packages/
|
||||
utils/
|
||||
src/utils.ts
|
||||
```
|
||||
|
||||
### Accessing Files Across Package Boundaries
|
||||
|
||||
```typescript
|
||||
// WRONG: Reaching into another package's internals
|
||||
import { Button } from "../../packages/ui/src/button";
|
||||
|
||||
// CORRECT: Install and import properly
|
||||
import { Button } from "@repo/ui/button";
|
||||
```
|
||||
|
||||
### Too Many Root Dependencies
|
||||
|
||||
```json
|
||||
// WRONG: App dependencies in root
|
||||
{
|
||||
"dependencies": {
|
||||
"react": "^18",
|
||||
"next": "^14"
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT: Only repo tools in root
|
||||
{
|
||||
"devDependencies": {
|
||||
"turbo": "latest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Task Configurations
|
||||
|
||||
### Standard Build Pipeline
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://turborepo.dev/schema.v2.json",
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
|
||||
},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Add a `transit` task if you have tasks that need parallel execution with cache invalidation (see below).
|
||||
|
||||
### Dev Task with `^dev` Pattern (for `turbo watch`)
|
||||
|
||||
A `dev` task with `dependsOn: ["^dev"]` and `persistent: false` in root turbo.json may look unusual but is **correct for `turbo watch` workflows**:
|
||||
|
||||
```json
|
||||
// Root turbo.json
|
||||
{
|
||||
"tasks": {
|
||||
"dev": {
|
||||
"dependsOn": ["^dev"],
|
||||
"cache": false,
|
||||
"persistent": false // Packages have one-shot dev scripts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Package turbo.json (apps/web/turbo.json)
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"dev": {
|
||||
"persistent": true // Apps run long-running dev servers
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why this works:**
|
||||
|
||||
- **Packages** (e.g., `@acme/db`, `@acme/validators`) have `"dev": "tsc"` — one-shot type generation that completes quickly
|
||||
- **Apps** override with `persistent: true` for actual dev servers (Next.js, etc.)
|
||||
- **`turbo watch`** re-runs the one-shot package `dev` scripts when source files change, keeping types in sync
|
||||
|
||||
**Intended usage:** Run `turbo watch dev` (not `turbo run dev`). Watch mode re-executes one-shot tasks on file changes while keeping persistent tasks running.
|
||||
|
||||
**Alternative pattern:** Use a separate task name like `prepare` or `generate` for one-shot dependency builds to make the intent clearer:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"prepare": {
|
||||
"dependsOn": ["^prepare"],
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"dev": {
|
||||
"dependsOn": ["prepare"],
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Transit Nodes for Parallel Tasks with Cache Invalidation
|
||||
|
||||
Some tasks can run in parallel (don't need built output from dependencies) but must invalidate cache when dependency source code changes.
|
||||
|
||||
**The problem with `dependsOn: ["^taskname"]`:**
|
||||
|
||||
- Forces sequential execution (slow)
|
||||
|
||||
**The problem with `dependsOn: []` (no dependencies):**
|
||||
|
||||
- Allows parallel execution (fast)
|
||||
- But cache is INCORRECT - changing dependency source won't invalidate cache
|
||||
|
||||
**Transit Nodes solve both:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"transit": { "dependsOn": ["^transit"] },
|
||||
"my-task": { "dependsOn": ["transit"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `transit` task creates dependency relationships without matching any actual script, so tasks run in parallel with correct cache invalidation.
|
||||
|
||||
**How to identify tasks that need this pattern:** Look for tasks that read source files from dependencies but don't need their build outputs.
|
||||
|
||||
### With Environment Variables
|
||||
|
||||
```json
|
||||
{
|
||||
"globalEnv": ["NODE_ENV"],
|
||||
"globalDependencies": [".env"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"],
|
||||
"env": ["API_URL", "DATABASE_URL"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Reference Index
|
||||
|
||||
### Configuration
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------------------------------------------------------------------- | -------------------------------------------------------- |
|
||||
| [configuration/README.md](./references/configuration/README.md) | turbo.json overview, Package Configurations |
|
||||
| [configuration/tasks.md](./references/configuration/tasks.md) | dependsOn, outputs, inputs, env, cache, persistent |
|
||||
| [configuration/global-options.md](./references/configuration/global-options.md) | globalEnv, globalDependencies, cacheDir, daemon, envMode |
|
||||
| [configuration/gotchas.md](./references/configuration/gotchas.md) | Common configuration mistakes |
|
||||
|
||||
### Caching
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------------------------------------------------- | -------------------------------------------- |
|
||||
| [caching/README.md](./references/caching/README.md) | How caching works, hash inputs |
|
||||
| [caching/remote-cache.md](./references/caching/remote-cache.md) | Vercel Remote Cache, self-hosted, login/link |
|
||||
| [caching/gotchas.md](./references/caching/gotchas.md) | Debugging cache misses, --summarize, --dry |
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------------------------------------------------- | ----------------------------------------- |
|
||||
| [environment/README.md](./references/environment/README.md) | env, globalEnv, passThroughEnv |
|
||||
| [environment/modes.md](./references/environment/modes.md) | Strict vs Loose mode, framework inference |
|
||||
| [environment/gotchas.md](./references/environment/gotchas.md) | .env files, CI issues |
|
||||
|
||||
### Filtering
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------------------- | ------------------------ |
|
||||
| [filtering/README.md](./references/filtering/README.md) | --filter syntax overview |
|
||||
| [filtering/patterns.md](./references/filtering/patterns.md) | Common filter patterns |
|
||||
|
||||
### CI/CD
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------------------------------------------- | ------------------------------- |
|
||||
| [ci/README.md](./references/ci/README.md) | General CI principles |
|
||||
| [ci/github-actions.md](./references/ci/github-actions.md) | Complete GitHub Actions setup |
|
||||
| [ci/vercel.md](./references/ci/vercel.md) | Vercel deployment, turbo-ignore |
|
||||
| [ci/patterns.md](./references/ci/patterns.md) | --affected, caching strategies |
|
||||
|
||||
### CLI
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------- | --------------------------------------------- |
|
||||
| [cli/README.md](./references/cli/README.md) | turbo run basics |
|
||||
| [cli/commands.md](./references/cli/commands.md) | turbo run flags, turbo-ignore, other commands |
|
||||
|
||||
### Best Practices
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||
| [best-practices/README.md](./references/best-practices/README.md) | Monorepo best practices overview |
|
||||
| [best-practices/structure.md](./references/best-practices/structure.md) | Repository structure, workspace config, TypeScript/ESLint setup |
|
||||
| [best-practices/packages.md](./references/best-practices/packages.md) | Creating internal packages, JIT vs Compiled, exports |
|
||||
| [best-practices/dependencies.md](./references/best-practices/dependencies.md) | Dependency management, installing, version sync |
|
||||
|
||||
### Watch Mode
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------------------------------- | ----------------------------------------------- |
|
||||
| [watch/README.md](./references/watch/README.md) | turbo watch, interruptible tasks, dev workflows |
|
||||
|
||||
### Boundaries (Experimental)
|
||||
|
||||
| File | Purpose |
|
||||
| --------------------------------------------------------- | ----------------------------------------------------- |
|
||||
| [boundaries/README.md](./references/boundaries/README.md) | Enforce package isolation, tag-based dependency rules |
|
||||
|
||||
## Source Documentation
|
||||
|
||||
This skill is based on the official Turborepo documentation at:
|
||||
|
||||
- Source: `docs/site/content/docs/` in the Turborepo repository
|
||||
- Live: https://turborepo.dev/docs
|
||||
70
.agents/skills/turborepo/command/turborepo.md
Normal file
70
.agents/skills/turborepo/command/turborepo.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
description: Load Turborepo skill for creating workflows, tasks, and pipelines in monorepos. Use when users ask to "create a workflow", "make a task", "generate a pipeline", or set up build orchestration.
|
||||
---
|
||||
|
||||
Load the Turborepo skill and help with monorepo task orchestration: creating workflows, configuring tasks, setting up pipelines, and optimizing builds.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Step 1: Load turborepo skill
|
||||
|
||||
```
|
||||
skill({ name: 'turborepo' })
|
||||
```
|
||||
|
||||
### Step 2: Identify task type from user request
|
||||
|
||||
Analyze $ARGUMENTS to determine:
|
||||
|
||||
- **Topic**: configuration, caching, filtering, environment, CI, or CLI
|
||||
- **Task type**: new setup, debugging, optimization, or implementation
|
||||
|
||||
Use decision trees in SKILL.md to select the relevant reference files.
|
||||
|
||||
### Step 3: Read relevant reference files
|
||||
|
||||
Based on task type, read from `references/<topic>/`:
|
||||
|
||||
| Task | Files to Read |
|
||||
| -------------------- | --------------------------------------------------------- |
|
||||
| Configure turbo.json | `configuration/README.md` + `configuration/tasks.md` |
|
||||
| Debug cache issues | `caching/gotchas.md` |
|
||||
| Set up remote cache | `caching/remote-cache.md` |
|
||||
| Filter packages | `filtering/README.md` + `filtering/patterns.md` |
|
||||
| Environment problems | `environment/gotchas.md` + `environment/modes.md` |
|
||||
| Set up CI | `ci/README.md` + `ci/github-actions.md` or `ci/vercel.md` |
|
||||
| CLI usage | `cli/commands.md` |
|
||||
|
||||
### Step 4: Execute task
|
||||
|
||||
Apply Turborepo-specific patterns from references to complete the user's request.
|
||||
|
||||
**CRITICAL - When creating tasks/scripts/pipelines:**
|
||||
|
||||
1. **DO NOT create Root Tasks** - Always create package tasks
|
||||
2. Add scripts to each relevant package's `package.json` (e.g., `apps/web/package.json`, `packages/ui/package.json`)
|
||||
3. Register the task in root `turbo.json`
|
||||
4. Root `package.json` only contains `turbo run <task>` - never actual task logic
|
||||
|
||||
**Other things to verify:**
|
||||
|
||||
- `outputs` defined for cacheable tasks
|
||||
- `dependsOn` uses correct syntax (`^task` vs `task`)
|
||||
- Environment variables in `env` key
|
||||
- `.env` files in `inputs` if used
|
||||
- Use `turbo run` (not `turbo`) in package.json and CI
|
||||
|
||||
### Step 5: Summarize
|
||||
|
||||
```
|
||||
=== Turborepo Task Complete ===
|
||||
|
||||
Topic: <configuration|caching|filtering|environment|ci|cli>
|
||||
Files referenced: <reference files consulted>
|
||||
|
||||
<brief summary of what was done>
|
||||
```
|
||||
|
||||
<user-request>
|
||||
$ARGUMENTS
|
||||
</user-request>
|
||||
|
|
@ -0,0 +1,246 @@
|
|||
# Dependency Management
|
||||
|
||||
Best practices for managing dependencies in a Turborepo monorepo.
|
||||
|
||||
## Core Principle: Install Where Used
|
||||
|
||||
Dependencies belong in the package that uses them, not the root.
|
||||
|
||||
```bash
|
||||
# Good: Install in specific package
|
||||
pnpm add react --filter=@repo/ui
|
||||
pnpm add next --filter=web
|
||||
|
||||
# Avoid: Installing in root
|
||||
pnpm add react -w # Only for repo-level tools!
|
||||
```
|
||||
|
||||
## Benefits of Local Installation
|
||||
|
||||
### 1. Clarity
|
||||
|
||||
Each package's `package.json` lists exactly what it needs:
|
||||
|
||||
```json
|
||||
// packages/ui/package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"react": "^18.0.0",
|
||||
"class-variance-authority": "^0.7.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Flexibility
|
||||
|
||||
Different packages can use different versions when needed:
|
||||
|
||||
```json
|
||||
// packages/legacy-ui/package.json
|
||||
{ "dependencies": { "react": "^17.0.0" } }
|
||||
|
||||
// packages/ui/package.json
|
||||
{ "dependencies": { "react": "^18.0.0" } }
|
||||
```
|
||||
|
||||
### 3. Better Caching
|
||||
|
||||
Installing in root changes workspace lockfile, invalidating all caches.
|
||||
|
||||
### 4. Pruning Support
|
||||
|
||||
`turbo prune` can remove unused dependencies for Docker images.
|
||||
|
||||
## What Belongs in Root
|
||||
|
||||
Only repository-level tools:
|
||||
|
||||
```json
|
||||
// Root package.json
|
||||
{
|
||||
"devDependencies": {
|
||||
"turbo": "latest",
|
||||
"husky": "^8.0.0",
|
||||
"lint-staged": "^15.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**NOT** application dependencies:
|
||||
|
||||
- react, next, express
|
||||
- lodash, axios, zod
|
||||
- Testing libraries (unless truly repo-wide)
|
||||
|
||||
## Installing Dependencies
|
||||
|
||||
### Single Package
|
||||
|
||||
```bash
|
||||
# pnpm
|
||||
pnpm add lodash --filter=@repo/utils
|
||||
|
||||
# npm
|
||||
npm install lodash --workspace=@repo/utils
|
||||
|
||||
# yarn
|
||||
yarn workspace @repo/utils add lodash
|
||||
|
||||
# bun
|
||||
cd packages/utils && bun add lodash
|
||||
```
|
||||
|
||||
### Multiple Packages
|
||||
|
||||
```bash
|
||||
# pnpm
|
||||
pnpm add jest --save-dev --filter=web --filter=@repo/ui
|
||||
|
||||
# npm
|
||||
npm install jest --save-dev --workspace=web --workspace=@repo/ui
|
||||
|
||||
# yarn (v2+)
|
||||
yarn workspaces foreach -R --from '{web,@repo/ui}' add jest --dev
|
||||
```
|
||||
|
||||
### Internal Packages
|
||||
|
||||
```bash
|
||||
# pnpm
|
||||
pnpm add @repo/ui --filter=web
|
||||
|
||||
# This updates package.json:
|
||||
{
|
||||
"dependencies": {
|
||||
"@repo/ui": "workspace:*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Keeping Versions in Sync
|
||||
|
||||
### Option 1: Tooling
|
||||
|
||||
```bash
|
||||
# syncpack - Check and fix version mismatches
|
||||
npx syncpack list-mismatches
|
||||
npx syncpack fix-mismatches
|
||||
|
||||
# manypkg - Similar functionality
|
||||
npx @manypkg/cli check
|
||||
npx @manypkg/cli fix
|
||||
|
||||
# sherif - Rust-based, very fast
|
||||
npx sherif
|
||||
```
|
||||
|
||||
### Option 2: Package Manager Commands
|
||||
|
||||
```bash
|
||||
# pnpm - Update everywhere
|
||||
pnpm up --recursive typescript@latest
|
||||
|
||||
# npm - Update in all workspaces
|
||||
npm install typescript@latest --workspaces
|
||||
```
|
||||
|
||||
### Option 3: pnpm Catalogs (pnpm 9.5+)
|
||||
|
||||
```yaml
|
||||
# pnpm-workspace.yaml
|
||||
packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
|
||||
catalog:
|
||||
react: ^18.2.0
|
||||
typescript: ^5.3.0
|
||||
```
|
||||
|
||||
```json
|
||||
// Any package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"react": "catalog:" // Uses version from catalog
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Internal vs External Dependencies
|
||||
|
||||
### Internal (Workspace)
|
||||
|
||||
```json
|
||||
// pnpm/bun
|
||||
{ "@repo/ui": "workspace:*" }
|
||||
|
||||
// npm/yarn
|
||||
{ "@repo/ui": "*" }
|
||||
```
|
||||
|
||||
Turborepo understands these relationships and orders builds accordingly.
|
||||
|
||||
### External (npm Registry)
|
||||
|
||||
```json
|
||||
{ "lodash": "^4.17.21" }
|
||||
```
|
||||
|
||||
Standard semver versioning from npm.
|
||||
|
||||
## Peer Dependencies
|
||||
|
||||
For library packages that expect the consumer to provide dependencies:
|
||||
|
||||
```json
|
||||
// packages/ui/package.json
|
||||
{
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"react": "^18.0.0", // For development/testing
|
||||
"react-dom": "^18.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Issues
|
||||
|
||||
### "Module not found"
|
||||
|
||||
1. Check the dependency is installed in the right package
|
||||
2. Run `pnpm install` / `npm install` to update lockfile
|
||||
3. Check exports are defined in the package
|
||||
|
||||
### Version Conflicts
|
||||
|
||||
Packages can use different versions - this is a feature, not a bug. But if you need consistency:
|
||||
|
||||
1. Use tooling (syncpack, manypkg)
|
||||
2. Use pnpm catalogs
|
||||
3. Create a lint rule
|
||||
|
||||
### Hoisting Issues
|
||||
|
||||
Some tools expect dependencies in specific locations. Use package manager config:
|
||||
|
||||
```yaml
|
||||
# .npmrc (pnpm)
|
||||
public-hoist-pattern[]=*eslint*
|
||||
public-hoist-pattern[]=*prettier*
|
||||
```
|
||||
|
||||
## Lockfile
|
||||
|
||||
**Required** for:
|
||||
|
||||
- Reproducible builds
|
||||
- Turborepo dependency analysis
|
||||
- Cache correctness
|
||||
|
||||
```bash
|
||||
# Commit your lockfile!
|
||||
git add pnpm-lock.yaml # or package-lock.json, yarn.lock
|
||||
```
|
||||
335
.agents/skills/turborepo/references/best-practices/packages.md
Normal file
335
.agents/skills/turborepo/references/best-practices/packages.md
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
# Creating Internal Packages
|
||||
|
||||
How to create and structure internal packages in your monorepo.
|
||||
|
||||
## Package Creation Checklist
|
||||
|
||||
1. Create directory in `packages/`
|
||||
2. Add `package.json` with name and exports
|
||||
3. Add source code in `src/`
|
||||
4. Add `tsconfig.json` if using TypeScript
|
||||
5. Install as dependency in consuming packages
|
||||
6. Run package manager install to update lockfile
|
||||
|
||||
## Package Compilation Strategies
|
||||
|
||||
### Just-in-Time (JIT)
|
||||
|
||||
Export TypeScript directly. The consuming app's bundler compiles it.
|
||||
|
||||
```json
|
||||
// packages/ui/package.json
|
||||
{
|
||||
"name": "@repo/ui",
|
||||
"exports": {
|
||||
"./button": "./src/button.tsx",
|
||||
"./card": "./src/card.tsx"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"check-types": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
|
||||
- Apps use modern bundlers (Turbopack, webpack, Vite)
|
||||
- You want minimal configuration
|
||||
- Build times are acceptable without caching
|
||||
|
||||
**Limitations:**
|
||||
|
||||
- No Turborepo cache for the package itself
|
||||
- Consumer must support TypeScript compilation
|
||||
- Can't use TypeScript `paths` (use Node.js subpath imports instead)
|
||||
|
||||
### Compiled
|
||||
|
||||
Package handles its own compilation.
|
||||
|
||||
```json
|
||||
// packages/ui/package.json
|
||||
{
|
||||
"name": "@repo/ui",
|
||||
"exports": {
|
||||
"./button": {
|
||||
"types": "./src/button.tsx",
|
||||
"default": "./dist/button.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"dev": "tsc --watch"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
// packages/ui/tsconfig.json
|
||||
{
|
||||
"extends": "@repo/typescript-config/library.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
|
||||
- You want Turborepo to cache builds
|
||||
- Package will be used by non-bundler tools
|
||||
- You need maximum compatibility
|
||||
|
||||
**Remember:** Add `dist/**` to turbo.json outputs!
|
||||
|
||||
## Defining Exports
|
||||
|
||||
### Multiple Entrypoints
|
||||
|
||||
```json
|
||||
{
|
||||
"exports": {
|
||||
".": "./src/index.ts", // @repo/ui
|
||||
"./button": "./src/button.tsx", // @repo/ui/button
|
||||
"./card": "./src/card.tsx", // @repo/ui/card
|
||||
"./hooks": "./src/hooks/index.ts" // @repo/ui/hooks
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Conditional Exports (Compiled)
|
||||
|
||||
```json
|
||||
{
|
||||
"exports": {
|
||||
"./button": {
|
||||
"types": "./src/button.tsx",
|
||||
"import": "./dist/button.mjs",
|
||||
"require": "./dist/button.cjs",
|
||||
"default": "./dist/button.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Installing Internal Packages
|
||||
|
||||
### Add to Consuming Package
|
||||
|
||||
```json
|
||||
// apps/web/package.json
|
||||
{
|
||||
"dependencies": {
|
||||
"@repo/ui": "workspace:*" // pnpm/bun
|
||||
// "@repo/ui": "*" // npm/yarn
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Run Install
|
||||
|
||||
```bash
|
||||
pnpm install # Updates lockfile with new dependency
|
||||
```
|
||||
|
||||
### Import and Use
|
||||
|
||||
```typescript
|
||||
// apps/web/src/page.tsx
|
||||
import { Button } from '@repo/ui/button';
|
||||
|
||||
export default function Page() {
|
||||
return <Button>Click me</Button>;
|
||||
}
|
||||
```
|
||||
|
||||
## One Purpose Per Package
|
||||
|
||||
### Good Examples
|
||||
|
||||
```
|
||||
packages/
|
||||
├── ui/ # Shared UI components
|
||||
├── utils/ # General utilities
|
||||
├── auth/ # Authentication logic
|
||||
├── database/ # Database client/schemas
|
||||
├── eslint-config/ # ESLint configuration
|
||||
├── typescript-config/ # TypeScript configuration
|
||||
└── api-client/ # Generated API client
|
||||
```
|
||||
|
||||
### Avoid Mega-Packages
|
||||
|
||||
```
|
||||
// BAD: One package for everything
|
||||
packages/
|
||||
└── shared/
|
||||
├── components/
|
||||
├── utils/
|
||||
├── hooks/
|
||||
├── types/
|
||||
└── api/
|
||||
|
||||
// GOOD: Separate by purpose
|
||||
packages/
|
||||
├── ui/ # Components
|
||||
├── utils/ # Utilities
|
||||
├── hooks/ # React hooks
|
||||
├── types/ # Shared TypeScript types
|
||||
└── api-client/ # API utilities
|
||||
```
|
||||
|
||||
## Config Packages
|
||||
|
||||
### TypeScript Config
|
||||
|
||||
```json
|
||||
// packages/typescript-config/package.json
|
||||
{
|
||||
"name": "@repo/typescript-config",
|
||||
"exports": {
|
||||
"./base.json": "./base.json",
|
||||
"./nextjs.json": "./nextjs.json",
|
||||
"./library.json": "./library.json"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ESLint Config
|
||||
|
||||
```json
|
||||
// packages/eslint-config/package.json
|
||||
{
|
||||
"name": "@repo/eslint-config",
|
||||
"exports": {
|
||||
"./base": "./base.js",
|
||||
"./next": "./next.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"eslint": "^8.0.0",
|
||||
"eslint-config-next": "latest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
### Forgetting to Export
|
||||
|
||||
```json
|
||||
// BAD: No exports defined
|
||||
{
|
||||
"name": "@repo/ui"
|
||||
}
|
||||
|
||||
// GOOD: Clear exports
|
||||
{
|
||||
"name": "@repo/ui",
|
||||
"exports": {
|
||||
"./button": "./src/button.tsx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Wrong Workspace Syntax
|
||||
|
||||
```json
|
||||
// pnpm/bun
|
||||
{ "@repo/ui": "workspace:*" } // Correct
|
||||
|
||||
// npm/yarn
|
||||
{ "@repo/ui": "*" } // Correct
|
||||
{ "@repo/ui": "workspace:*" } // Wrong for npm/yarn!
|
||||
```
|
||||
|
||||
### Missing from turbo.json Outputs
|
||||
|
||||
```json
|
||||
// Package builds to dist/, but turbo.json doesn't know
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"outputs": [".next/**"] // Missing dist/**!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Correct
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"outputs": [".next/**", "dist/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## TypeScript Best Practices
|
||||
|
||||
### Use Node.js Subpath Imports (Not `paths`)
|
||||
|
||||
TypeScript `compilerOptions.paths` breaks with JIT packages. Use Node.js subpath imports instead (TypeScript 5.4+).
|
||||
|
||||
**JIT Package:**
|
||||
|
||||
```json
|
||||
// packages/ui/package.json
|
||||
{
|
||||
"imports": {
|
||||
"#*": "./src/*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// packages/ui/button.tsx
|
||||
import { MY_STRING } from "#utils.ts"; // Uses .ts extension
|
||||
```
|
||||
|
||||
**Compiled Package:**
|
||||
|
||||
```json
|
||||
// packages/ui/package.json
|
||||
{
|
||||
"imports": {
|
||||
"#*": "./dist/*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// packages/ui/button.tsx
|
||||
import { MY_STRING } from "#utils.js"; // Uses .js extension
|
||||
```
|
||||
|
||||
### Use `tsc` for Internal Packages
|
||||
|
||||
For internal packages, prefer `tsc` over bundlers. Bundlers can mangle code before it reaches your app's bundler, causing hard-to-debug issues.
|
||||
|
||||
### Enable Go-to-Definition
|
||||
|
||||
For Compiled Packages, enable declaration maps:
|
||||
|
||||
```json
|
||||
// tsconfig.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationMap": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This creates `.d.ts` and `.d.ts.map` files for IDE navigation.
|
||||
|
||||
### No Root tsconfig.json Needed
|
||||
|
||||
Each package should have its own `tsconfig.json`. A root one causes all tasks to miss cache when changed. Only use root `tsconfig.json` for non-package scripts.
|
||||
|
||||
### Avoid TypeScript Project References
|
||||
|
||||
They add complexity and another caching layer. Turborepo handles dependencies better.
|
||||
269
.agents/skills/turborepo/references/best-practices/structure.md
Normal file
269
.agents/skills/turborepo/references/best-practices/structure.md
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
# Repository Structure
|
||||
|
||||
Detailed guidance on structuring a Turborepo monorepo.
|
||||
|
||||
## Workspace Configuration
|
||||
|
||||
### pnpm (Recommended)
|
||||
|
||||
```yaml
|
||||
# pnpm-workspace.yaml
|
||||
packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
```
|
||||
|
||||
### npm/yarn/bun
|
||||
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
"workspaces": ["apps/*", "packages/*"]
|
||||
}
|
||||
```
|
||||
|
||||
## Root package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-monorepo",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@9.0.0",
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"dev": "turbo run dev",
|
||||
"lint": "turbo run lint",
|
||||
"test": "turbo run test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"turbo": "latest"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Key points:
|
||||
|
||||
- `private: true` - Prevents accidental publishing
|
||||
- `packageManager` - Enforces consistent package manager version
|
||||
- **Scripts only delegate to `turbo run`** - No actual build logic here!
|
||||
- Minimal devDependencies (just turbo and repo tools)
|
||||
|
||||
## Always Prefer Package Tasks
|
||||
|
||||
**Always use package tasks. Only use Root Tasks if you cannot succeed with package tasks.**
|
||||
|
||||
```json
|
||||
// packages/web/package.json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"lint": "eslint .",
|
||||
"test": "vitest",
|
||||
"typecheck": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
// packages/api/package.json
|
||||
{
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"lint": "eslint .",
|
||||
"test": "vitest",
|
||||
"typecheck": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Package tasks enable Turborepo to:
|
||||
|
||||
1. **Parallelize** - Run `web#lint` and `api#lint` simultaneously
|
||||
2. **Cache individually** - Each package's task output is cached separately
|
||||
3. **Filter precisely** - Run `turbo run test --filter=web` for just one package
|
||||
|
||||
**Root Tasks are a fallback** for tasks that truly cannot run per-package:
|
||||
|
||||
```json
|
||||
// AVOID unless necessary - sequential, not parallelized, can't filter
|
||||
{
|
||||
"scripts": {
|
||||
"lint": "eslint apps/web && eslint apps/api && eslint packages/ui"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Root turbo.json
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://turborepo.dev/schema.v2.json",
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
|
||||
},
|
||||
"lint": {},
|
||||
"test": {
|
||||
"dependsOn": ["build"]
|
||||
},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Directory Organization
|
||||
|
||||
### Grouping Packages
|
||||
|
||||
You can group packages by adding more workspace paths:
|
||||
|
||||
```yaml
|
||||
# pnpm-workspace.yaml
|
||||
packages:
|
||||
- "apps/*"
|
||||
- "packages/*"
|
||||
- "packages/config/*" # Grouped configs
|
||||
- "packages/features/*" # Feature packages
|
||||
```
|
||||
|
||||
This allows:
|
||||
|
||||
```
|
||||
packages/
|
||||
├── ui/
|
||||
├── utils/
|
||||
├── config/
|
||||
│ ├── eslint/
|
||||
│ ├── typescript/
|
||||
│ └── tailwind/
|
||||
└── features/
|
||||
├── auth/
|
||||
└── payments/
|
||||
```
|
||||
|
||||
### What NOT to Do
|
||||
|
||||
```yaml
|
||||
# BAD: Nested wildcards cause ambiguous behavior
|
||||
packages:
|
||||
- "packages/**" # Don't do this!
|
||||
```
|
||||
|
||||
## Package Anatomy
|
||||
|
||||
### Minimum Required Files
|
||||
|
||||
```
|
||||
packages/ui/
|
||||
├── package.json # Required: Makes it a package
|
||||
├── src/ # Source code
|
||||
│ └── button.tsx
|
||||
└── tsconfig.json # TypeScript config (if using TS)
|
||||
```
|
||||
|
||||
### package.json Requirements
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@repo/ui", // Unique, namespaced name
|
||||
"version": "0.0.0", // Version (can be 0.0.0 for internal)
|
||||
"private": true, // Prevents accidental publishing
|
||||
"exports": { // Entry points
|
||||
"./button": "./src/button.tsx"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## TypeScript Configuration
|
||||
|
||||
### Shared Base Config
|
||||
|
||||
Create a shared TypeScript config package:
|
||||
|
||||
```
|
||||
packages/
|
||||
└── typescript-config/
|
||||
├── package.json
|
||||
├── base.json
|
||||
├── nextjs.json
|
||||
└── library.json
|
||||
```
|
||||
|
||||
```json
|
||||
// packages/typescript-config/base.json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "bundler",
|
||||
"module": "ESNext",
|
||||
"target": "ES2022"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Extending in Packages
|
||||
|
||||
```json
|
||||
// packages/ui/tsconfig.json
|
||||
{
|
||||
"extends": "@repo/typescript-config/library.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
```
|
||||
|
||||
### No Root tsconfig.json
|
||||
|
||||
You likely don't need a `tsconfig.json` in the workspace root. Each package should have its own config extending from the shared config package.
|
||||
|
||||
## ESLint Configuration
|
||||
|
||||
### Shared Config Package
|
||||
|
||||
```
|
||||
packages/
|
||||
└── eslint-config/
|
||||
├── package.json
|
||||
├── base.js
|
||||
├── next.js
|
||||
└── library.js
|
||||
```
|
||||
|
||||
```json
|
||||
// packages/eslint-config/package.json
|
||||
{
|
||||
"name": "@repo/eslint-config",
|
||||
"exports": {
|
||||
"./base": "./base.js",
|
||||
"./next": "./next.js",
|
||||
"./library": "./library.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Using in Packages
|
||||
|
||||
```js
|
||||
// apps/web/.eslintrc.js
|
||||
module.exports = {
|
||||
extends: ["@repo/eslint-config/next"],
|
||||
};
|
||||
```
|
||||
|
||||
## Lockfile
|
||||
|
||||
A lockfile is **required** for:
|
||||
|
||||
- Reproducible builds
|
||||
- Turborepo to understand package dependencies
|
||||
- Cache correctness
|
||||
|
||||
Without a lockfile, you'll see unpredictable behavior.
|
||||
169
.agents/skills/turborepo/references/caching/gotchas.md
Normal file
169
.agents/skills/turborepo/references/caching/gotchas.md
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# Debugging Cache Issues
|
||||
|
||||
## Diagnostic Tools
|
||||
|
||||
### `--summarize`
|
||||
|
||||
Generates a JSON file with all hash inputs. Compare two runs to find differences.
|
||||
|
||||
```bash
|
||||
turbo build --summarize
|
||||
# Creates .turbo/runs/<run-id>.json
|
||||
```
|
||||
|
||||
The summary includes:
|
||||
|
||||
- Global hash and its inputs
|
||||
- Per-task hashes and their inputs
|
||||
- Environment variables that affected the hash
|
||||
|
||||
**Comparing runs:**
|
||||
|
||||
```bash
|
||||
# Run twice, compare the summaries
|
||||
diff .turbo/runs/<first-run>.json .turbo/runs/<second-run>.json
|
||||
```
|
||||
|
||||
### `--dry` / `--dry=json`
|
||||
|
||||
See what would run without executing anything:
|
||||
|
||||
```bash
|
||||
turbo build --dry
|
||||
turbo build --dry=json # machine-readable output
|
||||
```
|
||||
|
||||
Shows cache status for each task without running them.
|
||||
|
||||
### `--force`
|
||||
|
||||
Skip reading cache, re-execute all tasks:
|
||||
|
||||
```bash
|
||||
turbo build --force
|
||||
```
|
||||
|
||||
Useful to verify tasks actually work (not just cached results).
|
||||
|
||||
## Unexpected Cache Misses
|
||||
|
||||
**Symptom:** Task runs when you expected a cache hit.
|
||||
|
||||
### Environment Variable Changed
|
||||
|
||||
Check if an env var in the `env` key changed:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": ["API_URL", "NODE_ENV"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Different `API_URL` between runs = cache miss.
|
||||
|
||||
### .env File Changed
|
||||
|
||||
`.env` files aren't tracked by default. Add to `inputs`:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.local"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or use `globalDependencies` for repo-wide env files:
|
||||
|
||||
```json
|
||||
{
|
||||
"globalDependencies": [".env"]
|
||||
}
|
||||
```
|
||||
|
||||
### Lockfile Changed
|
||||
|
||||
Installing/updating packages changes the global hash.
|
||||
|
||||
### Source Files Changed
|
||||
|
||||
Any file in the package (or in `inputs`) triggers a miss.
|
||||
|
||||
### turbo.json Changed
|
||||
|
||||
Config changes invalidate the global hash.
|
||||
|
||||
## Incorrect Cache Hits
|
||||
|
||||
**Symptom:** Cached output is stale/wrong.
|
||||
|
||||
### Missing Environment Variable
|
||||
|
||||
Task uses an env var not listed in `env`:
|
||||
|
||||
```javascript
|
||||
// build.js
|
||||
const apiUrl = process.env.API_URL; // not tracked!
|
||||
```
|
||||
|
||||
Fix: add to task config:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": ["API_URL"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Missing File in Inputs
|
||||
|
||||
Task reads a file outside default inputs:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": [
|
||||
"$TURBO_DEFAULT$",
|
||||
"../../shared-config.json" // file outside package
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Useful Flags
|
||||
|
||||
```bash
|
||||
# Only show output for cache misses
|
||||
turbo build --output-logs=new-only
|
||||
|
||||
# Show output for everything (debugging)
|
||||
turbo build --output-logs=full
|
||||
|
||||
# See why tasks are running
|
||||
turbo build --verbosity=2
|
||||
```
|
||||
|
||||
## Quick Checklist
|
||||
|
||||
Cache miss when expected hit:
|
||||
|
||||
1. Run with `--summarize`, compare with previous run
|
||||
2. Check env vars with `--dry=json`
|
||||
3. Look for lockfile/config changes in git
|
||||
|
||||
Cache hit when expected miss:
|
||||
|
||||
1. Verify env var is in `env` array
|
||||
2. Verify file is in `inputs` array
|
||||
3. Check if file is outside package directory
|
||||
127
.agents/skills/turborepo/references/caching/remote-cache.md
Normal file
127
.agents/skills/turborepo/references/caching/remote-cache.md
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
# Remote Caching
|
||||
|
||||
Share cache artifacts across your team and CI pipelines.
|
||||
|
||||
## Benefits
|
||||
|
||||
- Team members get cache hits from each other's work
|
||||
- CI gets cache hits from local development (and vice versa)
|
||||
- Dramatically faster CI runs after first build
|
||||
- No more "works on my machine" rebuilds
|
||||
|
||||
## Vercel Remote Cache
|
||||
|
||||
Free, zero-config when deploying on Vercel. For local dev and other CI:
|
||||
|
||||
### Local Development Setup
|
||||
|
||||
```bash
|
||||
# Authenticate with Vercel
|
||||
npx turbo login
|
||||
|
||||
# Link repo to your Vercel team
|
||||
npx turbo link
|
||||
```
|
||||
|
||||
This creates `.turbo/config.json` with your team info (gitignored by default).
|
||||
|
||||
### CI Setup
|
||||
|
||||
Set these environment variables:
|
||||
|
||||
```bash
|
||||
TURBO_TOKEN=<your-token>
|
||||
TURBO_TEAM=<your-team-slug>
|
||||
```
|
||||
|
||||
Get your token from Vercel dashboard → Settings → Tokens.
|
||||
|
||||
**GitHub Actions example:**
|
||||
|
||||
```yaml
|
||||
- name: Build
|
||||
run: npx turbo build
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
```
|
||||
|
||||
## Configuration in turbo.json
|
||||
|
||||
```json
|
||||
{
|
||||
"remoteCache": {
|
||||
"enabled": true,
|
||||
"signature": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Options:
|
||||
|
||||
- `enabled`: toggle remote cache (default: true when authenticated)
|
||||
- `signature`: require artifact signing (default: false)
|
||||
|
||||
## Artifact Signing
|
||||
|
||||
Verify cache artifacts haven't been tampered with:
|
||||
|
||||
```bash
|
||||
# Set a secret key (use same key across all environments)
|
||||
export TURBO_REMOTE_CACHE_SIGNATURE_KEY="your-secret-key"
|
||||
```
|
||||
|
||||
Enable in config:
|
||||
|
||||
```json
|
||||
{
|
||||
"remoteCache": {
|
||||
"signature": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Signed artifacts can only be restored if the signature matches.
|
||||
|
||||
## Self-Hosted Options
|
||||
|
||||
Community implementations for running your own cache server:
|
||||
|
||||
- **turbo-remote-cache** (Node.js) - supports S3, GCS, Azure
|
||||
- **turborepo-remote-cache** (Go) - lightweight, S3-compatible
|
||||
- **ducktape** (Rust) - high-performance option
|
||||
|
||||
Configure with environment variables:
|
||||
|
||||
```bash
|
||||
TURBO_API=https://your-cache-server.com
|
||||
TURBO_TOKEN=your-auth-token
|
||||
TURBO_TEAM=your-team
|
||||
```
|
||||
|
||||
## Cache Behavior Control
|
||||
|
||||
```bash
|
||||
# Disable remote cache for a run
|
||||
turbo build --remote-cache-read-only # read but don't write
|
||||
turbo build --no-cache # skip cache entirely
|
||||
|
||||
# Environment variable alternative
|
||||
TURBO_REMOTE_ONLY=true # only use remote, skip local
|
||||
```
|
||||
|
||||
## Debugging Remote Cache
|
||||
|
||||
```bash
|
||||
# Verbose output shows cache operations
|
||||
turbo build --verbosity=2
|
||||
|
||||
# Check if remote cache is configured
|
||||
turbo config
|
||||
```
|
||||
|
||||
Look for:
|
||||
|
||||
- "Remote caching enabled" in output
|
||||
- Upload/download messages during runs
|
||||
- "cache hit, replaying output" with remote cache indicator
|
||||
162
.agents/skills/turborepo/references/ci/github-actions.md
Normal file
162
.agents/skills/turborepo/references/ci/github-actions.md
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
# GitHub Actions
|
||||
|
||||
Complete setup guide for Turborepo with GitHub Actions.
|
||||
|
||||
## Basic Workflow Structure
|
||||
|
||||
```yaml
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build and Test
|
||||
run: turbo run build test lint
|
||||
```
|
||||
|
||||
## Package Manager Setup
|
||||
|
||||
### pnpm
|
||||
|
||||
```yaml
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
```
|
||||
|
||||
### Yarn
|
||||
|
||||
```yaml
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'yarn'
|
||||
|
||||
- run: yarn install --frozen-lockfile
|
||||
```
|
||||
|
||||
### Bun
|
||||
|
||||
```yaml
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- run: bun install --frozen-lockfile
|
||||
```
|
||||
|
||||
## Remote Cache Setup
|
||||
|
||||
### 1. Create Vercel Access Token
|
||||
|
||||
1. Go to [Vercel Dashboard](https://vercel.com/account/tokens)
|
||||
2. Create a new token with appropriate scope
|
||||
3. Copy the token value
|
||||
|
||||
### 2. Add Secrets and Variables
|
||||
|
||||
In your GitHub repository settings:
|
||||
|
||||
**Secrets** (Settings > Secrets and variables > Actions > Secrets):
|
||||
|
||||
- `TURBO_TOKEN`: Your Vercel access token
|
||||
|
||||
**Variables** (Settings > Secrets and variables > Actions > Variables):
|
||||
|
||||
- `TURBO_TEAM`: Your Vercel team slug
|
||||
|
||||
### 3. Add to Workflow
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
```
|
||||
|
||||
## Alternative: actions/cache
|
||||
|
||||
If you can't use remote cache, cache Turborepo's local cache directory:
|
||||
|
||||
```yaml
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: .turbo
|
||||
key: turbo-${{ runner.os }}-${{ hashFiles('**/turbo.json', '**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
turbo-${{ runner.os }}-
|
||||
```
|
||||
|
||||
Note: This is less effective than remote cache since it's per-branch.
|
||||
|
||||
## Complete Example
|
||||
|
||||
```yaml
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: 9
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: turbo run build --affected
|
||||
|
||||
- name: Test
|
||||
run: turbo run test --affected
|
||||
|
||||
- name: Lint
|
||||
run: turbo run lint --affected
|
||||
```
|
||||
145
.agents/skills/turborepo/references/ci/patterns.md
Normal file
145
.agents/skills/turborepo/references/ci/patterns.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# CI Optimization Patterns
|
||||
|
||||
Strategies for efficient CI/CD with Turborepo.
|
||||
|
||||
## PR vs Main Branch Builds
|
||||
|
||||
### PR Builds: Only Affected
|
||||
|
||||
Test only what changed in the PR:
|
||||
|
||||
```yaml
|
||||
- name: Test (PR)
|
||||
if: github.event_name == 'pull_request'
|
||||
run: turbo run build test --affected
|
||||
```
|
||||
|
||||
### Main Branch: Full Build
|
||||
|
||||
Ensure complete validation on merge:
|
||||
|
||||
```yaml
|
||||
- name: Test (Main)
|
||||
if: github.ref == 'refs/heads/main'
|
||||
run: turbo run build test
|
||||
```
|
||||
|
||||
## Custom Git Ranges with --filter
|
||||
|
||||
For advanced scenarios, use `--filter` with git refs:
|
||||
|
||||
```bash
|
||||
# Changes since specific commit
|
||||
turbo run test --filter="...[abc123]"
|
||||
|
||||
# Changes between refs
|
||||
turbo run test --filter="...[main...HEAD]"
|
||||
|
||||
# Changes in last 3 commits
|
||||
turbo run test --filter="...[HEAD~3]"
|
||||
```
|
||||
|
||||
## Caching Strategies
|
||||
|
||||
### Remote Cache (Recommended)
|
||||
|
||||
Best performance - shared across all CI runs and developers:
|
||||
|
||||
```yaml
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
|
||||
```
|
||||
|
||||
### actions/cache Fallback
|
||||
|
||||
When remote cache isn't available:
|
||||
|
||||
```yaml
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: .turbo
|
||||
key: turbo-${{ runner.os }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
turbo-${{ runner.os }}-${{ github.ref }}-
|
||||
turbo-${{ runner.os }}-
|
||||
```
|
||||
|
||||
Limitations:
|
||||
|
||||
- Cache is branch-scoped
|
||||
- PRs restore from base branch cache
|
||||
- Less efficient than remote cache
|
||||
|
||||
## Matrix Builds
|
||||
|
||||
Test across Node versions:
|
||||
|
||||
```yaml
|
||||
strategy:
|
||||
matrix:
|
||||
node: [18, 20, 22]
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- run: turbo run test
|
||||
```
|
||||
|
||||
## Parallelizing Across Jobs
|
||||
|
||||
Split tasks into separate jobs:
|
||||
|
||||
```yaml
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: turbo run lint --affected
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: turbo run test --affected
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint, test]
|
||||
steps:
|
||||
- run: turbo run build
|
||||
```
|
||||
|
||||
### Cache Considerations
|
||||
|
||||
When parallelizing:
|
||||
|
||||
- Each job has separate cache writes
|
||||
- Remote cache handles this automatically
|
||||
- With actions/cache, use unique keys per job to avoid conflicts
|
||||
|
||||
```yaml
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: .turbo
|
||||
key: turbo-${{ runner.os }}-${{ github.job }}-${{ github.sha }}
|
||||
```
|
||||
|
||||
## Conditional Tasks
|
||||
|
||||
Skip expensive tasks on draft PRs:
|
||||
|
||||
```yaml
|
||||
- name: E2E Tests
|
||||
if: github.event.pull_request.draft == false
|
||||
run: turbo run test:e2e --affected
|
||||
```
|
||||
|
||||
Or require label for full test:
|
||||
|
||||
```yaml
|
||||
- name: Full Test Suite
|
||||
if: contains(github.event.pull_request.labels.*.name, 'full-test')
|
||||
run: turbo run test
|
||||
```
|
||||
103
.agents/skills/turborepo/references/ci/vercel.md
Normal file
103
.agents/skills/turborepo/references/ci/vercel.md
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Vercel Deployment
|
||||
|
||||
Turborepo integrates seamlessly with Vercel for monorepo deployments.
|
||||
|
||||
## Remote Cache
|
||||
|
||||
Remote caching is **automatically enabled** when deploying to Vercel. No configuration needed - Vercel detects Turborepo and enables caching.
|
||||
|
||||
This means:
|
||||
|
||||
- No `TURBO_TOKEN` or `TURBO_TEAM` setup required on Vercel
|
||||
- Cache is shared across all deployments
|
||||
- Preview and production builds benefit from cache
|
||||
|
||||
## turbo-ignore
|
||||
|
||||
Skip unnecessary builds when a package hasn't changed using `turbo-ignore`.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npx turbo-ignore
|
||||
```
|
||||
|
||||
Or install globally in your project:
|
||||
|
||||
```bash
|
||||
pnpm add -D turbo-ignore
|
||||
```
|
||||
|
||||
### Setup in Vercel
|
||||
|
||||
1. Go to your project in Vercel Dashboard
|
||||
2. Navigate to Settings > Git > Ignored Build Step
|
||||
3. Select "Custom" and enter:
|
||||
|
||||
```bash
|
||||
npx turbo-ignore
|
||||
```
|
||||
|
||||
### How It Works
|
||||
|
||||
`turbo-ignore` checks if the current package (or its dependencies) changed since the last successful deployment:
|
||||
|
||||
1. Compares current commit to last deployed commit
|
||||
2. Uses Turborepo's dependency graph
|
||||
3. Returns exit code 0 (skip) if no changes
|
||||
4. Returns exit code 1 (build) if changes detected
|
||||
|
||||
### Options
|
||||
|
||||
```bash
|
||||
# Check specific package
|
||||
npx turbo-ignore web
|
||||
|
||||
# Use specific comparison ref
|
||||
npx turbo-ignore --fallback=HEAD~1
|
||||
|
||||
# Verbose output
|
||||
npx turbo-ignore --verbose
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Set environment variables in Vercel Dashboard:
|
||||
|
||||
1. Go to Project Settings > Environment Variables
|
||||
2. Add variables for each environment (Production, Preview, Development)
|
||||
|
||||
Common variables:
|
||||
|
||||
- `DATABASE_URL`
|
||||
- `API_KEY`
|
||||
- Package-specific config
|
||||
|
||||
## Monorepo Root Directory
|
||||
|
||||
For monorepos, set the root directory in Vercel:
|
||||
|
||||
1. Project Settings > General > Root Directory
|
||||
2. Set to the package path (e.g., `apps/web`)
|
||||
|
||||
Vercel automatically:
|
||||
|
||||
- Installs dependencies from monorepo root
|
||||
- Runs build from the package directory
|
||||
- Detects framework settings
|
||||
|
||||
## Build Command
|
||||
|
||||
Vercel auto-detects `turbo run build` when `turbo.json` exists at root.
|
||||
|
||||
Override if needed:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=web
|
||||
```
|
||||
|
||||
Or for production-only optimizations:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=web --env-mode=strict
|
||||
```
|
||||
297
.agents/skills/turborepo/references/cli/commands.md
Normal file
297
.agents/skills/turborepo/references/cli/commands.md
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
# turbo run Flags Reference
|
||||
|
||||
Full docs: https://turborepo.dev/docs/reference/run
|
||||
|
||||
## Package Selection
|
||||
|
||||
### `--filter` / `-F`
|
||||
|
||||
Select specific packages to run tasks in.
|
||||
|
||||
```bash
|
||||
turbo build --filter=web
|
||||
turbo build -F=@repo/ui -F=@repo/utils
|
||||
turbo test --filter=./apps/*
|
||||
```
|
||||
|
||||
See `filtering/` for complete syntax (globs, dependencies, git ranges).
|
||||
|
||||
### Task Identifier Syntax (v2.2.4+)
|
||||
|
||||
Run specific package tasks directly:
|
||||
|
||||
```bash
|
||||
turbo run web#build # Build web package
|
||||
turbo run web#build docs#lint # Multiple specific tasks
|
||||
```
|
||||
|
||||
### `--affected`
|
||||
|
||||
Run only in packages changed since the base branch.
|
||||
|
||||
```bash
|
||||
turbo build --affected
|
||||
turbo test --affected --filter=./apps/* # combine with filter
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
|
||||
- Default: compares `main...HEAD`
|
||||
- In GitHub Actions: auto-detects `GITHUB_BASE_REF`
|
||||
- Override base: `TURBO_SCM_BASE=development turbo build --affected`
|
||||
- Override head: `TURBO_SCM_HEAD=your-branch turbo build --affected`
|
||||
|
||||
**Requires git history** - shallow clones may fall back to running all tasks.
|
||||
|
||||
## Execution Control
|
||||
|
||||
### `--dry` / `--dry=json`
|
||||
|
||||
Preview what would run without executing.
|
||||
|
||||
```bash
|
||||
turbo build --dry # human-readable
|
||||
turbo build --dry=json # machine-readable
|
||||
```
|
||||
|
||||
### `--force`
|
||||
|
||||
Ignore all cached artifacts, re-run everything.
|
||||
|
||||
```bash
|
||||
turbo build --force
|
||||
```
|
||||
|
||||
### `--concurrency`
|
||||
|
||||
Limit parallel task execution.
|
||||
|
||||
```bash
|
||||
turbo build --concurrency=4 # max 4 tasks
|
||||
turbo build --concurrency=50% # 50% of CPU cores
|
||||
```
|
||||
|
||||
### `--continue`
|
||||
|
||||
Keep running other tasks when one fails.
|
||||
|
||||
```bash
|
||||
turbo build test --continue
|
||||
```
|
||||
|
||||
### `--only`
|
||||
|
||||
Run only the specified task, skip its dependencies.
|
||||
|
||||
```bash
|
||||
turbo build --only # skip running dependsOn tasks
|
||||
```
|
||||
|
||||
### `--parallel` (Discouraged)
|
||||
|
||||
Ignores task graph dependencies, runs all tasks simultaneously. **Avoid using this flag**—if tasks need to run in parallel, configure `dependsOn` correctly instead. Using `--parallel` bypasses Turborepo's dependency graph, which can cause race conditions and incorrect builds.
|
||||
|
||||
## Cache Control
|
||||
|
||||
### `--cache`
|
||||
|
||||
Fine-grained cache behavior control.
|
||||
|
||||
```bash
|
||||
# Default: read/write both local and remote
|
||||
turbo build --cache=local:rw,remote:rw
|
||||
|
||||
# Read-only local, no remote
|
||||
turbo build --cache=local:r,remote:
|
||||
|
||||
# Disable local, read-only remote
|
||||
turbo build --cache=local:,remote:r
|
||||
|
||||
# Disable all caching
|
||||
turbo build --cache=local:,remote:
|
||||
```
|
||||
|
||||
## Output & Debugging
|
||||
|
||||
### `--graph`
|
||||
|
||||
Generate task graph visualization.
|
||||
|
||||
```bash
|
||||
turbo build --graph # opens in browser
|
||||
turbo build --graph=graph.svg # SVG file
|
||||
turbo build --graph=graph.png # PNG file
|
||||
turbo build --graph=graph.json # JSON data
|
||||
turbo build --graph=graph.mermaid # Mermaid diagram
|
||||
```
|
||||
|
||||
### `--summarize`
|
||||
|
||||
Generate JSON run summary for debugging.
|
||||
|
||||
```bash
|
||||
turbo build --summarize
|
||||
# creates .turbo/runs/<run-id>.json
|
||||
```
|
||||
|
||||
### `--output-logs`
|
||||
|
||||
Control log output verbosity.
|
||||
|
||||
```bash
|
||||
turbo build --output-logs=full # all logs (default)
|
||||
turbo build --output-logs=new-only # only cache misses
|
||||
turbo build --output-logs=errors-only # only failures
|
||||
turbo build --output-logs=none # silent
|
||||
```
|
||||
|
||||
### `--profile`
|
||||
|
||||
Generate Chrome tracing profile for performance analysis.
|
||||
|
||||
```bash
|
||||
turbo build --profile=profile.json
|
||||
# open chrome://tracing and load the file
|
||||
```
|
||||
|
||||
### `--verbosity` / `-v`
|
||||
|
||||
Control turbo's own log level.
|
||||
|
||||
```bash
|
||||
turbo build -v # verbose
|
||||
turbo build -vv # more verbose
|
||||
turbo build -vvv # maximum verbosity
|
||||
```
|
||||
|
||||
## Environment
|
||||
|
||||
### `--env-mode`
|
||||
|
||||
Control environment variable handling.
|
||||
|
||||
```bash
|
||||
turbo build --env-mode=strict # only declared env vars (default)
|
||||
turbo build --env-mode=loose # include all env vars in hash
|
||||
```
|
||||
|
||||
## UI
|
||||
|
||||
### `--ui`
|
||||
|
||||
Select output interface.
|
||||
|
||||
```bash
|
||||
turbo build --ui=tui # interactive terminal UI (default in TTY)
|
||||
turbo build --ui=stream # streaming logs (default in CI)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# turbo-ignore
|
||||
|
||||
Full docs: https://turborepo.dev/docs/reference/turbo-ignore
|
||||
|
||||
Skip CI work when nothing relevant changed. Useful for skipping container setup.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```bash
|
||||
# Check if build is needed for current package (uses Automatic Package Scoping)
|
||||
npx turbo-ignore
|
||||
|
||||
# Check specific package
|
||||
npx turbo-ignore web
|
||||
|
||||
# Check specific task
|
||||
npx turbo-ignore --task=test
|
||||
```
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0`: No changes detected - skip CI work
|
||||
- `1`: Changes detected - proceed with CI
|
||||
|
||||
## CI Integration Example
|
||||
|
||||
```yaml
|
||||
# GitHub Actions
|
||||
- name: Check for changes
|
||||
id: turbo-ignore
|
||||
run: npx turbo-ignore web
|
||||
continue-on-error: true
|
||||
|
||||
- name: Build
|
||||
if: steps.turbo-ignore.outcome == 'failure' # changes detected
|
||||
run: pnpm build
|
||||
```
|
||||
|
||||
## Comparison Depth
|
||||
|
||||
Default: compares to parent commit (`HEAD^1`).
|
||||
|
||||
```bash
|
||||
# Compare to specific commit
|
||||
npx turbo-ignore --fallback=abc123
|
||||
|
||||
# Compare to branch
|
||||
npx turbo-ignore --fallback=main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Other Commands
|
||||
|
||||
## turbo boundaries
|
||||
|
||||
Check workspace violations (experimental).
|
||||
|
||||
```bash
|
||||
turbo boundaries
|
||||
```
|
||||
|
||||
See `references/boundaries/` for configuration.
|
||||
|
||||
## turbo watch
|
||||
|
||||
Re-run tasks on file changes.
|
||||
|
||||
```bash
|
||||
turbo watch build test
|
||||
```
|
||||
|
||||
See `references/watch/` for details.
|
||||
|
||||
## turbo prune
|
||||
|
||||
Create sparse checkout for Docker.
|
||||
|
||||
```bash
|
||||
turbo prune web --docker
|
||||
```
|
||||
|
||||
## turbo link / unlink
|
||||
|
||||
Connect/disconnect Remote Cache.
|
||||
|
||||
```bash
|
||||
turbo link # connect to Vercel Remote Cache
|
||||
turbo unlink # disconnect
|
||||
```
|
||||
|
||||
## turbo login / logout
|
||||
|
||||
Authenticate with Remote Cache provider.
|
||||
|
||||
```bash
|
||||
turbo login # authenticate
|
||||
turbo logout # log out
|
||||
```
|
||||
|
||||
## turbo generate
|
||||
|
||||
Scaffold new packages.
|
||||
|
||||
```bash
|
||||
turbo generate
|
||||
```
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
# Global Options Reference
|
||||
|
||||
Options that affect all tasks. Full docs: https://turborepo.dev/docs/reference/configuration
|
||||
|
||||
## globalEnv
|
||||
|
||||
Environment variables affecting all task hashes.
|
||||
|
||||
```json
|
||||
{
|
||||
"globalEnv": ["CI", "NODE_ENV", "VERCEL_*"]
|
||||
}
|
||||
```
|
||||
|
||||
Use for variables that should invalidate all caches when changed.
|
||||
|
||||
## globalDependencies
|
||||
|
||||
Files that affect all task hashes.
|
||||
|
||||
```json
|
||||
{
|
||||
"globalDependencies": [
|
||||
"tsconfig.json",
|
||||
".env",
|
||||
"pnpm-lock.yaml"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Lockfile is included by default. Add shared configs here.
|
||||
|
||||
## globalPassThroughEnv
|
||||
|
||||
Variables available to tasks but not included in hash.
|
||||
|
||||
```json
|
||||
{
|
||||
"globalPassThroughEnv": ["AWS_SECRET_KEY", "GITHUB_TOKEN"]
|
||||
}
|
||||
```
|
||||
|
||||
Use for credentials that shouldn't affect cache keys.
|
||||
|
||||
## cacheDir
|
||||
|
||||
Custom cache location. Default: `node_modules/.cache/turbo`.
|
||||
|
||||
```json
|
||||
{
|
||||
"cacheDir": ".turbo/cache"
|
||||
}
|
||||
```
|
||||
|
||||
## daemon
|
||||
|
||||
Background process for faster subsequent runs. Default: `true`.
|
||||
|
||||
```json
|
||||
{
|
||||
"daemon": false
|
||||
}
|
||||
```
|
||||
|
||||
Disable in CI or when debugging.
|
||||
|
||||
## envMode
|
||||
|
||||
How unspecified env vars are handled. Default: `"strict"`.
|
||||
|
||||
```json
|
||||
{
|
||||
"envMode": "strict" // Only specified vars available
|
||||
// or
|
||||
"envMode": "loose" // All vars pass through
|
||||
}
|
||||
```
|
||||
|
||||
Strict mode catches missing env declarations.
|
||||
|
||||
## ui
|
||||
|
||||
Terminal UI mode. Default: `"stream"`.
|
||||
|
||||
```json
|
||||
{
|
||||
"ui": "tui" // Interactive terminal UI
|
||||
// or
|
||||
"ui": "stream" // Traditional streaming logs
|
||||
}
|
||||
```
|
||||
|
||||
TUI provides better UX for parallel tasks.
|
||||
|
||||
## remoteCache
|
||||
|
||||
Configure remote caching.
|
||||
|
||||
```json
|
||||
{
|
||||
"remoteCache": {
|
||||
"enabled": true,
|
||||
"signature": true,
|
||||
"timeout": 30,
|
||||
"uploadTimeout": 60
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Option | Default | Description |
|
||||
| --------------- | ---------------------- | ------------------------------------------------------ |
|
||||
| `enabled` | `true` | Enable/disable remote caching |
|
||||
| `signature` | `false` | Sign artifacts with `TURBO_REMOTE_CACHE_SIGNATURE_KEY` |
|
||||
| `preflight` | `false` | Send OPTIONS request before cache requests |
|
||||
| `timeout` | `30` | Timeout in seconds for cache operations |
|
||||
| `uploadTimeout` | `60` | Timeout in seconds for uploads |
|
||||
| `apiUrl` | `"https://vercel.com"` | Remote cache API endpoint |
|
||||
| `loginUrl` | `"https://vercel.com"` | Login endpoint |
|
||||
| `teamId` | - | Team ID (must start with `team_`) |
|
||||
| `teamSlug` | - | Team slug for querystring |
|
||||
|
||||
See https://turborepo.dev/docs/core-concepts/remote-caching for setup.
|
||||
|
||||
## concurrency
|
||||
|
||||
Default: `"10"`
|
||||
|
||||
Limit parallel task execution.
|
||||
|
||||
```json
|
||||
{
|
||||
"concurrency": "4" // Max 4 tasks at once
|
||||
// or
|
||||
"concurrency": "50%" // 50% of available CPUs
|
||||
}
|
||||
```
|
||||
|
||||
## futureFlags
|
||||
|
||||
Enable experimental features that will become default in future versions.
|
||||
|
||||
```json
|
||||
{
|
||||
"futureFlags": {
|
||||
"errorsOnlyShowHash": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `errorsOnlyShowHash`
|
||||
|
||||
When using `outputLogs: "errors-only"`, show task hashes on start/completion:
|
||||
|
||||
- Cache miss: `cache miss, executing <hash> (only logging errors)`
|
||||
- Cache hit: `cache hit, replaying logs (no errors) <hash>`
|
||||
|
||||
## noUpdateNotifier
|
||||
|
||||
Disable update notifications when new turbo versions are available.
|
||||
|
||||
```json
|
||||
{
|
||||
"noUpdateNotifier": true
|
||||
}
|
||||
```
|
||||
|
||||
## dangerouslyDisablePackageManagerCheck
|
||||
|
||||
Bypass the `packageManager` field requirement. Use for incremental migration.
|
||||
|
||||
```json
|
||||
{
|
||||
"dangerouslyDisablePackageManagerCheck": true
|
||||
}
|
||||
```
|
||||
|
||||
**Warning**: Unstable lockfiles can cause unpredictable behavior.
|
||||
|
||||
## Git Worktree Cache Sharing (Pre-release)
|
||||
|
||||
When working in Git worktrees, Turborepo automatically shares local cache between the main worktree and linked worktrees.
|
||||
|
||||
**How it works:**
|
||||
|
||||
- Detects worktree configuration
|
||||
- Redirects cache to main worktree's `.turbo/cache`
|
||||
- Works alongside Remote Cache
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- Cache hits across branches
|
||||
- Reduced disk usage
|
||||
- Faster branch switching
|
||||
|
||||
**Disabled by**: Setting explicit `cacheDir` in turbo.json.
|
||||
348
.agents/skills/turborepo/references/configuration/gotchas.md
Normal file
348
.agents/skills/turborepo/references/configuration/gotchas.md
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
# Configuration Gotchas
|
||||
|
||||
Common mistakes and how to fix them.
|
||||
|
||||
## #1 Root Scripts Not Using `turbo run`
|
||||
|
||||
Root `package.json` scripts for turbo tasks MUST use `turbo run`, not direct commands.
|
||||
|
||||
```json
|
||||
// WRONG - bypasses turbo, no parallelization or caching
|
||||
{
|
||||
"scripts": {
|
||||
"build": "bun build",
|
||||
"dev": "bun dev"
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT - delegates to turbo
|
||||
{
|
||||
"scripts": {
|
||||
"build": "turbo run build",
|
||||
"dev": "turbo run dev"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why this matters:** Running `bun build` or `npm run build` at root bypasses Turborepo entirely - no parallelization, no caching, no dependency graph awareness.
|
||||
|
||||
## #2 Using `&&` to Chain Turbo Tasks
|
||||
|
||||
Don't use `&&` to chain tasks that turbo should orchestrate.
|
||||
|
||||
```json
|
||||
// WRONG - changeset:publish chains turbo task with non-turbo command
|
||||
{
|
||||
"scripts": {
|
||||
"changeset:publish": "bun build && changeset publish"
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT - use turbo run, let turbo handle dependencies
|
||||
{
|
||||
"scripts": {
|
||||
"changeset:publish": "turbo run build && changeset publish"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the second command (`changeset publish`) depends on build outputs, the turbo task should run through turbo to get caching and parallelization benefits.
|
||||
|
||||
## #3 Overly Broad globalDependencies
|
||||
|
||||
`globalDependencies` affects hash for ALL tasks in ALL packages. Be specific.
|
||||
|
||||
```json
|
||||
// WRONG - affects all hashes
|
||||
{
|
||||
"globalDependencies": ["**/.env.*local"]
|
||||
}
|
||||
|
||||
// CORRECT - move to specific tasks that need it
|
||||
{
|
||||
"globalDependencies": [".env"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"],
|
||||
"outputs": ["dist/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why this matters:** `**/.env.*local` matches .env files in ALL packages, causing unnecessary cache invalidation. Instead:
|
||||
|
||||
- Use `globalDependencies` only for truly global files (root `.env`)
|
||||
- Use task-level `inputs` for package-specific .env files with `$TURBO_DEFAULT$` to preserve default behavior
|
||||
|
||||
## #4 Repetitive Task Configuration
|
||||
|
||||
Look for repeated configuration across tasks that can be collapsed.
|
||||
|
||||
```json
|
||||
// WRONG - repetitive env and inputs across tasks
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": ["API_URL", "DATABASE_URL"],
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"]
|
||||
},
|
||||
"test": {
|
||||
"env": ["API_URL", "DATABASE_URL"],
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BETTER - use globalEnv and globalDependencies
|
||||
{
|
||||
"globalEnv": ["API_URL", "DATABASE_URL"],
|
||||
"globalDependencies": [".env*"],
|
||||
"tasks": {
|
||||
"build": {},
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**When to use global vs task-level:**
|
||||
|
||||
- `globalEnv` / `globalDependencies` - affects ALL tasks, use for truly shared config
|
||||
- Task-level `env` / `inputs` - use when only specific tasks need it
|
||||
|
||||
## #5 Using `../` to Traverse Out of Package in `inputs`
|
||||
|
||||
Don't use relative paths like `../` to reference files outside the package. Use `$TURBO_ROOT$` instead.
|
||||
|
||||
```json
|
||||
// WRONG - traversing out of package
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": ["$TURBO_DEFAULT$", "../shared-config.json"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT - use $TURBO_ROOT$ for repo root
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": ["$TURBO_DEFAULT$", "$TURBO_ROOT$/shared-config.json"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## #6 MOST COMMON MISTAKE: Creating Root Tasks
|
||||
|
||||
**DO NOT create Root Tasks. ALWAYS create package tasks.**
|
||||
|
||||
When you need to create a task (build, lint, test, typecheck, etc.):
|
||||
|
||||
1. Add the script to **each relevant package's** `package.json`
|
||||
2. Register the task in root `turbo.json`
|
||||
3. Root `package.json` only contains `turbo run <task>`
|
||||
|
||||
```json
|
||||
// WRONG - DO NOT DO THIS
|
||||
// Root package.json with task logic
|
||||
{
|
||||
"scripts": {
|
||||
"build": "cd apps/web && next build && cd ../api && tsc",
|
||||
"lint": "eslint apps/ packages/",
|
||||
"test": "vitest"
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT - DO THIS
|
||||
// apps/web/package.json
|
||||
{ "scripts": { "build": "next build", "lint": "eslint .", "test": "vitest" } }
|
||||
|
||||
// apps/api/package.json
|
||||
{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }
|
||||
|
||||
// packages/ui/package.json
|
||||
{ "scripts": { "build": "tsc", "lint": "eslint .", "test": "vitest" } }
|
||||
|
||||
// Root package.json - ONLY delegates
|
||||
{ "scripts": { "build": "turbo run build", "lint": "turbo run lint", "test": "turbo run test" } }
|
||||
|
||||
// turbo.json - register tasks
|
||||
{
|
||||
"tasks": {
|
||||
"build": { "dependsOn": ["^build"], "outputs": ["dist/**"] },
|
||||
"lint": {},
|
||||
"test": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why this matters:**
|
||||
|
||||
- Package tasks run in **parallel** across all packages
|
||||
- Each package's output is cached **individually**
|
||||
- You can **filter** to specific packages: `turbo run test --filter=web`
|
||||
|
||||
Root Tasks (`//#taskname`) defeat all these benefits. Only use them for tasks that truly cannot exist in any package (extremely rare).
|
||||
|
||||
## #7 Tasks That Need Parallel Execution + Cache Invalidation
|
||||
|
||||
Some tasks can run in parallel (don't need built output from dependencies) but must still invalidate cache when dependency source code changes. Using `dependsOn: ["^taskname"]` forces sequential execution. Using no dependencies breaks cache invalidation.
|
||||
|
||||
**Use Transit Nodes for these tasks:**
|
||||
|
||||
```json
|
||||
// WRONG - forces sequential execution (SLOW)
|
||||
"my-task": {
|
||||
"dependsOn": ["^my-task"]
|
||||
}
|
||||
|
||||
// ALSO WRONG - no dependency awareness (INCORRECT CACHING)
|
||||
"my-task": {}
|
||||
|
||||
// CORRECT - use Transit Nodes for parallel + correct caching
|
||||
{
|
||||
"tasks": {
|
||||
"transit": { "dependsOn": ["^transit"] },
|
||||
"my-task": { "dependsOn": ["transit"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Why Transit Nodes work:**
|
||||
|
||||
- `transit` creates dependency relationships without matching any actual script
|
||||
- Tasks that depend on `transit` gain dependency awareness
|
||||
- Since `transit` completes instantly (no script), tasks run in parallel
|
||||
- Cache correctly invalidates when dependency source code changes
|
||||
|
||||
**How to identify tasks that need this pattern:** Look for tasks that read source files from dependencies but don't need their build outputs.
|
||||
|
||||
## Missing outputs for File-Producing Tasks
|
||||
|
||||
**Before flagging missing `outputs`, check what the task actually produces:**
|
||||
|
||||
1. Read the package's script (e.g., `"build": "tsc"`, `"test": "vitest"`)
|
||||
2. Determine if it writes files to disk or only outputs to stdout
|
||||
3. Only flag if the task produces files that should be cached
|
||||
|
||||
```json
|
||||
// WRONG - build produces files but they're not cached
|
||||
"build": {
|
||||
"dependsOn": ["^build"]
|
||||
}
|
||||
|
||||
// CORRECT - outputs are cached
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["dist/**"]
|
||||
}
|
||||
```
|
||||
|
||||
No `outputs` key is fine for stdout-only tasks. For file-producing tasks, missing `outputs` means Turbo has nothing to cache.
|
||||
|
||||
## Forgetting ^ in dependsOn
|
||||
|
||||
```json
|
||||
// WRONG - looks for "build" in SAME package (infinite loop or missing)
|
||||
"build": {
|
||||
"dependsOn": ["build"]
|
||||
}
|
||||
|
||||
// CORRECT - runs dependencies' build first
|
||||
"build": {
|
||||
"dependsOn": ["^build"]
|
||||
}
|
||||
```
|
||||
|
||||
The `^` means "in dependency packages", not "in this package".
|
||||
|
||||
## Missing persistent on Dev Tasks
|
||||
|
||||
```json
|
||||
// WRONG - dependent tasks hang waiting for dev to "finish"
|
||||
"dev": {
|
||||
"cache": false
|
||||
}
|
||||
|
||||
// CORRECT
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
```
|
||||
|
||||
## Package Config Missing extends
|
||||
|
||||
```json
|
||||
// WRONG - packages/web/turbo.json
|
||||
{
|
||||
"tasks": {
|
||||
"build": { "outputs": [".next/**"] }
|
||||
}
|
||||
}
|
||||
|
||||
// CORRECT
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": { "outputs": [".next/**"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Without `"extends": ["//"]`, Package Configurations are invalid.
|
||||
|
||||
## Root Tasks Need Special Syntax
|
||||
|
||||
To run a task defined only in root `package.json`:
|
||||
|
||||
```bash
|
||||
# WRONG
|
||||
turbo run format
|
||||
|
||||
# CORRECT
|
||||
turbo run //#format
|
||||
```
|
||||
|
||||
And in dependsOn:
|
||||
|
||||
```json
|
||||
"build": {
|
||||
"dependsOn": ["//#codegen"] // Root package's codegen
|
||||
}
|
||||
```
|
||||
|
||||
## Overwriting Default Inputs
|
||||
|
||||
```json
|
||||
// WRONG - only watches test files, ignores source changes
|
||||
"test": {
|
||||
"inputs": ["tests/**"]
|
||||
}
|
||||
|
||||
// CORRECT - extends defaults, adds test files
|
||||
"test": {
|
||||
"inputs": ["$TURBO_DEFAULT$", "tests/**"]
|
||||
}
|
||||
```
|
||||
|
||||
Without `$TURBO_DEFAULT$`, you replace all default file watching.
|
||||
|
||||
## Caching Tasks with Side Effects
|
||||
|
||||
```json
|
||||
// WRONG - deploy might be skipped on cache hit
|
||||
"deploy": {
|
||||
"dependsOn": ["build"]
|
||||
}
|
||||
|
||||
// CORRECT
|
||||
"deploy": {
|
||||
"dependsOn": ["build"],
|
||||
"cache": false
|
||||
}
|
||||
```
|
||||
|
||||
Always disable cache for deploy, publish, or mutation tasks.
|
||||
285
.agents/skills/turborepo/references/configuration/tasks.md
Normal file
285
.agents/skills/turborepo/references/configuration/tasks.md
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
# Task Configuration Reference
|
||||
|
||||
Full docs: https://turborepo.dev/docs/reference/configuration#tasks
|
||||
|
||||
## dependsOn
|
||||
|
||||
Controls task execution order.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build", // Dependencies' build tasks first
|
||||
"codegen", // Same package's codegen task first
|
||||
"shared#build" // Specific package's build task
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Syntax | Meaning |
|
||||
| ---------- | ------------------------------------ |
|
||||
| `^task` | Run `task` in all dependencies first |
|
||||
| `task` | Run `task` in same package first |
|
||||
| `pkg#task` | Run specific package's task first |
|
||||
|
||||
The `^` prefix is crucial - without it, you're referencing the same package.
|
||||
|
||||
### Transit Nodes for Parallel Tasks
|
||||
|
||||
For tasks like `lint` and `check-types` that can run in parallel but need dependency-aware caching:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"transit": { "dependsOn": ["^transit"] },
|
||||
"lint": { "dependsOn": ["transit"] },
|
||||
"check-types": { "dependsOn": ["transit"] }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**DO NOT use `dependsOn: ["^lint"]`** - this forces sequential execution.
|
||||
**DO NOT use `dependsOn: []`** - this breaks cache invalidation.
|
||||
|
||||
The `transit` task creates dependency relationships without running anything (no matching script), so tasks run in parallel with correct caching.
|
||||
|
||||
## outputs
|
||||
|
||||
Glob patterns for files to cache. **If omitted, nothing is cached.**
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"outputs": ["dist/**", "build/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Framework examples:**
|
||||
|
||||
```json
|
||||
// Next.js
|
||||
"outputs": [".next/**", "!.next/cache/**"]
|
||||
|
||||
// Vite
|
||||
"outputs": ["dist/**"]
|
||||
|
||||
// TypeScript (tsc)
|
||||
"outputs": ["dist/**", "*.tsbuildinfo"]
|
||||
|
||||
// No file outputs (lint, typecheck)
|
||||
"outputs": []
|
||||
```
|
||||
|
||||
Use `!` prefix to exclude patterns from caching.
|
||||
|
||||
## inputs
|
||||
|
||||
Files considered when calculating task hash. Defaults to all tracked files in package.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"test": {
|
||||
"inputs": ["src/**", "tests/**", "vitest.config.ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Special values:**
|
||||
|
||||
| Value | Meaning |
|
||||
| --------------------- | --------------------------------------- |
|
||||
| `$TURBO_DEFAULT$` | Include default inputs, then add/remove |
|
||||
| `$TURBO_ROOT$/<path>` | Reference files from repo root |
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": [
|
||||
"$TURBO_DEFAULT$",
|
||||
"!README.md",
|
||||
"$TURBO_ROOT$/tsconfig.base.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## env
|
||||
|
||||
Environment variables to include in task hash.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": [
|
||||
"API_URL",
|
||||
"NEXT_PUBLIC_*", // Wildcard matching
|
||||
"!DEBUG" // Exclude from hash
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Variables listed here affect cache hits - changing the value invalidates cache.
|
||||
|
||||
## cache
|
||||
|
||||
Enable/disable caching for a task. Default: `true`.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"dev": { "cache": false },
|
||||
"deploy": { "cache": false }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Disable for: dev servers, deploy commands, tasks with side effects.
|
||||
|
||||
## persistent
|
||||
|
||||
Mark long-running tasks that don't exit. Default: `false`.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"dev": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Required for dev servers - without it, dependent tasks wait forever.
|
||||
|
||||
## interactive
|
||||
|
||||
Allow task to receive stdin input. Default: `false`.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"login": {
|
||||
"cache": false,
|
||||
"interactive": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## outputLogs
|
||||
|
||||
Control when logs are shown. Options: `full`, `hash-only`, `new-only`, `errors-only`, `none`.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"outputLogs": "new-only" // Only show logs on cache miss
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## with
|
||||
|
||||
Run tasks alongside this task. For long-running tasks that need runtime dependencies.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"dev": {
|
||||
"with": ["api#dev"],
|
||||
"persistent": true,
|
||||
"cache": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Unlike `dependsOn`, `with` runs tasks concurrently (not sequentially). Use for dev servers that need other services running.
|
||||
|
||||
## interruptible
|
||||
|
||||
Allow `turbo watch` to restart the task on changes. Default: `false`.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"dev": {
|
||||
"persistent": true,
|
||||
"interruptible": true,
|
||||
"cache": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use for dev servers that don't automatically detect dependency changes.
|
||||
|
||||
## description (Pre-release)
|
||||
|
||||
Human-readable description of the task.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"description": "Compiles the application for production deployment"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For documentation only - doesn't affect execution or caching.
|
||||
|
||||
## passThroughEnv
|
||||
|
||||
Environment variables available at runtime but NOT included in cache hash.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"passThroughEnv": ["AWS_SECRET_KEY", "GITHUB_TOKEN"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Warning**: Changes to these vars won't cause cache misses. Use `env` if changes should invalidate cache.
|
||||
|
||||
## extends (Package Configuration only)
|
||||
|
||||
Control task inheritance in Package Configurations.
|
||||
|
||||
```json
|
||||
// packages/ui/turbo.json
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"lint": {
|
||||
"extends": false // Exclude from this package
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| Value | Behavior |
|
||||
| ---------------- | -------------------------------------------------------------- |
|
||||
| `true` (default) | Inherit from root turbo.json |
|
||||
| `false` | Exclude task from package, or define fresh without inheritance |
|
||||
145
.agents/skills/turborepo/references/environment/gotchas.md
Normal file
145
.agents/skills/turborepo/references/environment/gotchas.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Environment Variable Gotchas
|
||||
|
||||
Common mistakes and how to fix them.
|
||||
|
||||
## .env Files Must Be in `inputs`
|
||||
|
||||
Turbo does NOT read `.env` files. Your framework (Next.js, Vite, etc.) or `dotenv` loads them. But Turbo needs to know when they change.
|
||||
|
||||
**Wrong:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": ["DATABASE_URL"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Right:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": ["DATABASE_URL"],
|
||||
"inputs": ["$TURBO_DEFAULT$", ".env", ".env.local", ".env.production"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Strict Mode Filters CI Variables
|
||||
|
||||
In strict mode, CI provider variables (GITHUB_TOKEN, GITLAB_CI, etc.) are filtered unless explicitly listed.
|
||||
|
||||
**Symptom:** Task fails with "authentication required" or "permission denied" in CI.
|
||||
|
||||
**Solution:**
|
||||
|
||||
```json
|
||||
{
|
||||
"globalPassThroughEnv": ["GITHUB_TOKEN", "GITLAB_CI", "CI"]
|
||||
}
|
||||
```
|
||||
|
||||
## passThroughEnv Doesn't Affect Hash
|
||||
|
||||
Variables in `passThroughEnv` are available at runtime but changes WON'T trigger rebuilds.
|
||||
|
||||
**Dangerous example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"passThroughEnv": ["API_URL"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `API_URL` changes from staging to production, Turbo may serve a cached build pointing to the wrong API.
|
||||
|
||||
**Use passThroughEnv only for:**
|
||||
|
||||
- Auth tokens that don't affect output (SENTRY_AUTH_TOKEN)
|
||||
- CI metadata (GITHUB_RUN_ID)
|
||||
- Variables consumed after build (deploy credentials)
|
||||
|
||||
## Runtime-Created Variables Are Invisible
|
||||
|
||||
Turbo captures env vars at startup. Variables created during execution aren't seen.
|
||||
|
||||
**Won't work:**
|
||||
|
||||
```bash
|
||||
# In package.json scripts
|
||||
"build": "export API_URL=$COMPUTED_VALUE && next build"
|
||||
```
|
||||
|
||||
**Solution:** Set vars before invoking turbo:
|
||||
|
||||
```bash
|
||||
API_URL=$COMPUTED_VALUE turbo run build
|
||||
```
|
||||
|
||||
## Different .env Files for Different Environments
|
||||
|
||||
If you use `.env.development` and `.env.production`, both should be in inputs.
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"inputs": [
|
||||
"$TURBO_DEFAULT$",
|
||||
".env",
|
||||
".env.local",
|
||||
".env.development",
|
||||
".env.development.local",
|
||||
".env.production",
|
||||
".env.production.local"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Next.js Example
|
||||
|
||||
```json
|
||||
{
|
||||
"$schema": "https://turborepo.dev/schema.v2.json",
|
||||
"globalEnv": ["CI", "NODE_ENV", "VERCEL"],
|
||||
"globalPassThroughEnv": ["GITHUB_TOKEN", "VERCEL_URL"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["^build"],
|
||||
"env": [
|
||||
"DATABASE_URL",
|
||||
"NEXT_PUBLIC_*",
|
||||
"!NEXT_PUBLIC_ANALYTICS_ID"
|
||||
],
|
||||
"passThroughEnv": ["SENTRY_AUTH_TOKEN"],
|
||||
"inputs": [
|
||||
"$TURBO_DEFAULT$",
|
||||
".env",
|
||||
".env.local",
|
||||
".env.production",
|
||||
".env.production.local"
|
||||
],
|
||||
"outputs": [".next/**", "!.next/cache/**"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This config:
|
||||
|
||||
- Hashes DATABASE*URL and NEXT_PUBLIC*\* vars (except analytics)
|
||||
- Passes through SENTRY_AUTH_TOKEN without hashing
|
||||
- Includes all .env file variants in the hash
|
||||
- Makes CI tokens available globally
|
||||
101
.agents/skills/turborepo/references/environment/modes.md
Normal file
101
.agents/skills/turborepo/references/environment/modes.md
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
# Environment Modes
|
||||
|
||||
Turborepo supports different modes for handling environment variables during task execution.
|
||||
|
||||
## Strict Mode (Default)
|
||||
|
||||
Only explicitly configured variables are available to tasks.
|
||||
|
||||
**Behavior:**
|
||||
|
||||
- Tasks only see vars listed in `env`, `globalEnv`, `passThroughEnv`, or `globalPassThroughEnv`
|
||||
- Unlisted vars are filtered out
|
||||
- Tasks fail if they require unlisted variables
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- Guarantees cache correctness
|
||||
- Prevents accidental dependencies on system vars
|
||||
- Reproducible builds across machines
|
||||
|
||||
```bash
|
||||
# Explicit (though it's the default)
|
||||
turbo run build --env-mode=strict
|
||||
```
|
||||
|
||||
## Loose Mode
|
||||
|
||||
All system environment variables are available to tasks.
|
||||
|
||||
```bash
|
||||
turbo run build --env-mode=loose
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
|
||||
- Every system env var is passed through
|
||||
- Only vars in `env`/`globalEnv` affect the hash
|
||||
- Other vars are available but NOT hashed
|
||||
|
||||
**Risks:**
|
||||
|
||||
- Cache may restore incorrect results if unhashed vars changed
|
||||
- "Works on my machine" bugs
|
||||
- CI vs local environment mismatches
|
||||
|
||||
**Use case:** Migrating legacy projects or debugging strict mode issues.
|
||||
|
||||
## Framework Inference (Automatic)
|
||||
|
||||
Turborepo automatically detects frameworks and includes their conventional env vars.
|
||||
|
||||
### Inferred Variables by Framework
|
||||
|
||||
| Framework | Pattern |
|
||||
| ---------------- | ------------------- |
|
||||
| Next.js | `NEXT_PUBLIC_*` |
|
||||
| Vite | `VITE_*` |
|
||||
| Create React App | `REACT_APP_*` |
|
||||
| Gatsby | `GATSBY_*` |
|
||||
| Nuxt | `NUXT_*`, `NITRO_*` |
|
||||
| Expo | `EXPO_PUBLIC_*` |
|
||||
| Astro | `PUBLIC_*` |
|
||||
| SvelteKit | `PUBLIC_*` |
|
||||
| Remix | `REMIX_*` |
|
||||
| Redwood | `REDWOOD_ENV_*` |
|
||||
| Sanity | `SANITY_STUDIO_*` |
|
||||
| Solid | `VITE_*` |
|
||||
|
||||
### Disabling Framework Inference
|
||||
|
||||
Globally via CLI:
|
||||
|
||||
```bash
|
||||
turbo run build --framework-inference=false
|
||||
```
|
||||
|
||||
Or exclude specific patterns in config:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": {
|
||||
"build": {
|
||||
"env": ["!NEXT_PUBLIC_*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Why Disable?
|
||||
|
||||
- You want explicit control over all env vars
|
||||
- Framework vars shouldn't bust the cache (e.g., analytics IDs)
|
||||
- Debugging unexpected cache misses
|
||||
|
||||
## Checking Environment Mode
|
||||
|
||||
Use `--dry-run` to see which vars affect each task:
|
||||
|
||||
```bash
|
||||
turbo run build --dry-run=json | jq '.tasks[].environmentVariables'
|
||||
```
|
||||
152
.agents/skills/turborepo/references/filtering/patterns.md
Normal file
152
.agents/skills/turborepo/references/filtering/patterns.md
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
# Common Filter Patterns
|
||||
|
||||
Practical examples for typical monorepo scenarios.
|
||||
|
||||
## Single Package
|
||||
|
||||
Run task in one package:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=web
|
||||
turbo run test --filter=@acme/api
|
||||
```
|
||||
|
||||
## Package with Dependencies
|
||||
|
||||
Build a package and everything it depends on:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=web...
|
||||
```
|
||||
|
||||
Useful for: ensuring all dependencies are built before the target.
|
||||
|
||||
## Package Dependents
|
||||
|
||||
Run in all packages that depend on a library:
|
||||
|
||||
```bash
|
||||
turbo run test --filter=...ui
|
||||
```
|
||||
|
||||
Useful for: testing consumers after changing a shared package.
|
||||
|
||||
## Dependents Only (Exclude Target)
|
||||
|
||||
Test packages that depend on ui, but not ui itself:
|
||||
|
||||
```bash
|
||||
turbo run test --filter=...^ui
|
||||
```
|
||||
|
||||
## Changed Packages
|
||||
|
||||
Run only in packages with file changes since last commit:
|
||||
|
||||
```bash
|
||||
turbo run lint --filter=[HEAD^1]
|
||||
```
|
||||
|
||||
Since a specific branch point:
|
||||
|
||||
```bash
|
||||
turbo run lint --filter=[main...HEAD]
|
||||
```
|
||||
|
||||
## Changed + Dependents (PR Builds)
|
||||
|
||||
Run in changed packages AND packages that depend on them:
|
||||
|
||||
```bash
|
||||
turbo run build test --filter=...[HEAD^1]
|
||||
```
|
||||
|
||||
Or use the shortcut:
|
||||
|
||||
```bash
|
||||
turbo run build test --affected
|
||||
```
|
||||
|
||||
## Directory-Based
|
||||
|
||||
Run in all apps:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=./apps/*
|
||||
```
|
||||
|
||||
Run in specific directories:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=./apps/web --filter=./apps/api
|
||||
```
|
||||
|
||||
## Scope-Based
|
||||
|
||||
Run in all packages under a scope:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=@acme/*
|
||||
```
|
||||
|
||||
## Exclusions
|
||||
|
||||
Run in all apps except admin:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=./apps/* --filter=!admin
|
||||
```
|
||||
|
||||
Run everywhere except specific packages:
|
||||
|
||||
```bash
|
||||
turbo run lint --filter=!legacy-app --filter=!deprecated-pkg
|
||||
```
|
||||
|
||||
## Complex Combinations
|
||||
|
||||
Apps that changed, plus their dependents:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=...[HEAD^1] --filter=./apps/*
|
||||
```
|
||||
|
||||
All packages except docs, but only if changed:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=[main...HEAD] --filter=!docs
|
||||
```
|
||||
|
||||
## Debugging Filters
|
||||
|
||||
Use `--dry` to see what would run without executing:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=web... --dry
|
||||
```
|
||||
|
||||
Use `--dry=json` for machine-readable output:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=...[HEAD^1] --dry=json
|
||||
```
|
||||
|
||||
## CI/CD Patterns
|
||||
|
||||
PR validation (most common):
|
||||
|
||||
```bash
|
||||
turbo run build test lint --affected
|
||||
```
|
||||
|
||||
Deploy only changed apps:
|
||||
|
||||
```bash
|
||||
turbo run deploy --filter=./apps/* --filter=[main...HEAD]
|
||||
```
|
||||
|
||||
Full rebuild of specific app and deps:
|
||||
|
||||
```bash
|
||||
turbo run build --filter=production-app...
|
||||
```
|
||||
111
.agents/skills/unit-tests/SKILL.md
Normal file
111
.agents/skills/unit-tests/SKILL.md
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
---
|
||||
name: unit-tests
|
||||
description: |
|
||||
Guardrails for adding unit tests in bklit-ui without over-testing. Use when the
|
||||
user mentions unit test, unit tests, tests, test coverage, add tests, write tests,
|
||||
vitest, jest, or asks whether something should be tested.
|
||||
---
|
||||
|
||||
# Unit Tests (bklit-ui)
|
||||
|
||||
Read this skill before proposing or writing tests. **Default stance: fewer, higher-signal tests.**
|
||||
|
||||
---
|
||||
|
||||
## When to add tests
|
||||
|
||||
Add tests when they **lock in behavior that is easy to break silently**:
|
||||
|
||||
| Worth testing | Why |
|
||||
|---------------|-----|
|
||||
| Pure functions / modules | Stable inputs → outputs; fast; no DOM |
|
||||
| Formatters, parsers, scale math, bounds | Regression on string/number output is user-visible |
|
||||
| Codegen / export / registry helpers | Output shape is the contract |
|
||||
| Non-obvious edge cases | Empty data, reversed ranges, clamping |
|
||||
|
||||
**Examples in this repo:** `chart-formatters.test.ts`, `highlight-segment-bounds.test.ts`, `animation.test.ts`, `apps/web/lib/studio/__tests__/*`.
|
||||
|
||||
---
|
||||
|
||||
## When NOT to add tests (unless explicitly requested)
|
||||
|
||||
Do **not** add tests just to increase coverage or “be thorough”:
|
||||
|
||||
| Skip | Why |
|
||||
|------|-----|
|
||||
| React component render smoke tests | visx, motion, portals → brittle; manual/docs check is cheaper |
|
||||
| `memo()` / guard extractions (#65-style) | Structural perf refactors; output unchanged; RTL mount assertions are heavy |
|
||||
| Default prop passthrough | `formatValue = intFmt` — types + formatter tests cover it |
|
||||
| Third-party library behavior | Don’t re-test d3, visx, or React |
|
||||
| Trivial getters / one-line wrappers | No regression signal |
|
||||
| Snapshot entire chart SVG/JSX | High churn, low signal |
|
||||
|
||||
If the user asks “should we test X?” — **say no** when X falls in this table, and suggest a lighter alternative (pure helper test, manual check, CI build).
|
||||
|
||||
---
|
||||
|
||||
## Repo conventions
|
||||
|
||||
**Runner:** Node built-in test runner + `tsx` (not Jest/Vitest unless the repo adopts them later).
|
||||
|
||||
```bash
|
||||
pnpm test # root — turbo runs packages with a test script
|
||||
pnpm --filter @bklitui/ui test
|
||||
cd apps/web && pnpm test
|
||||
```
|
||||
|
||||
**Place tests:** `**/__tests__/**/*.test.ts` next to the code under test.
|
||||
|
||||
**Pattern:**
|
||||
|
||||
```ts
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "node:test";
|
||||
import { myFn } from "../my-module";
|
||||
|
||||
describe("myFn", () => {
|
||||
it("handles empty input", () => {
|
||||
assert.equal(myFn([]), expected);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Equivalence tests** (preferred for formatters): assert shared module output matches the **previous inline** call (e.g. `toLocaleDateString` with same locale/options) so tests stay timezone-safe and prove no visual regression.
|
||||
|
||||
**New package test script:** add to `package.json`:
|
||||
|
||||
```json
|
||||
"test": "node scripts/run-tests.mjs"
|
||||
```
|
||||
|
||||
Use a small `scripts/run-tests.mjs` that collects `*.test.ts` from `__tests__` and invokes `node --import tsx --test` — shell globs break on Linux CI. Add `tsx` as a devDependency if missing. Wire into root `turbo.json` `test` task; CI runs `pnpm test`.
|
||||
|
||||
---
|
||||
|
||||
## Decision checklist (run before writing)
|
||||
|
||||
1. Is the logic **pure** or extractable to pure functions? → Test that. Consider extracting first.
|
||||
2. Would a test fail on a **real user-visible bug**? → Good candidate.
|
||||
3. Does it need **jsdom / RTL / Playwright**? → Stop; justify to user or defer to manual/visual check.
|
||||
4. Did the user **ask** for tests? → Still apply this skill; don’t over-deliver.
|
||||
5. How many cases? → **3–10 focused cases**, not exhaustive matrices.
|
||||
|
||||
---
|
||||
|
||||
## What to tell the user
|
||||
|
||||
When recommending tests, be explicit:
|
||||
|
||||
- **Add:** “Test `chart-formatters.ts` — pure, high regression value.”
|
||||
- **Skip:** “Skip component tests for the memo split — no output change; build + manual chart docs are enough.”
|
||||
- **CI:** Mention `pnpm test` in PR test plan when adding or changing tests.
|
||||
|
||||
---
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- Adding Jest/Vitest/Testing Library for a one-off without team buy-in
|
||||
- Mocking entire chart context to assert `useMemo` call counts
|
||||
- Testing implementation details (hook order, internal component names)
|
||||
- Duplicating type-checker work (`expect(typeof x).toBe('function')`)
|
||||
- Committing tests that only pass locally due to hard-coded timezone/locale strings
|
||||
129
.agents/skills/wiki-llms-txt/SKILL.md
Normal file
129
.agents/skills/wiki-llms-txt/SKILL.md
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
---
|
||||
name: wiki-llms-txt
|
||||
description: Generates llms.txt and llms-full.txt files for LLM-friendly project documentation following the llms.txt specification. Use when the user wants to create LLM-readable summaries, llms.txt files, or make their wiki accessible to language models.
|
||||
license: MIT
|
||||
metadata:
|
||||
author: Microsoft
|
||||
version: "1.0.0"
|
||||
---
|
||||
|
||||
# llms.txt Generator
|
||||
|
||||
Generate `llms.txt` and `llms-full.txt` files that provide LLM-friendly access to wiki documentation, following the [llms.txt specification](https://llmstxt.org/).
|
||||
|
||||
## When This Skill Activates
|
||||
|
||||
- User asks to generate `llms.txt` or mentions the llms.txt standard
|
||||
- User wants to make documentation "LLM-friendly" or "LLM-readable"
|
||||
- User asks for a project summary file for language models
|
||||
- User mentions `llms-full.txt` or context-expanded documentation
|
||||
|
||||
## Source Repository Resolution (MUST DO FIRST)
|
||||
|
||||
Before generating, resolve the source repository context:
|
||||
|
||||
1. **Check for git remote**: Run `git remote get-url origin`
|
||||
2. **Ask the user**: _"Is this a local-only repository, or do you have a source repository URL?"_
|
||||
- Remote URL → store as `REPO_URL`
|
||||
- Local → use relative paths only
|
||||
3. **Determine default branch**: Run `git rev-parse --abbrev-ref HEAD`
|
||||
4. **Do NOT proceed** until resolved
|
||||
|
||||
## llms.txt Format (Spec-Compliant)
|
||||
|
||||
The file follows the [llms.txt specification](https://llmstxt.org/):
|
||||
|
||||
```markdown
|
||||
# {Project Name}
|
||||
|
||||
> {Dense one-paragraph summary — what it does, who it's for, key technologies}
|
||||
|
||||
{Important context paragraphs — constraints, architectural philosophy, non-obvious things}
|
||||
|
||||
## {Section Name}
|
||||
|
||||
- [{Page Title}]({relative-path-to-md}): {One-sentence description of what the reader will learn}
|
||||
|
||||
## Optional
|
||||
|
||||
- [{Page Title}]({relative-path-to-md}): {Description — these can be skipped for shorter context}
|
||||
```
|
||||
|
||||
### Key Rules
|
||||
|
||||
1. **H1** — Project name (exactly one, required)
|
||||
2. **Blockquote** — Dense, specific summary (required). Must be unique to THIS project.
|
||||
3. **Context paragraphs** — Non-obvious constraints, things LLMs would get wrong without being told
|
||||
4. **H2 sections** — Organized by topic, each with a list of `[Title](url): Description` entries
|
||||
5. **"Optional" H2** — Special meaning: links here can be skipped for shorter context
|
||||
6. **Relative links** — All paths relative to wiki directory
|
||||
7. **Dynamic** — ALL content derived from actual wiki pages, not templates
|
||||
8. **Section order** — Most important first: Onboarding → Architecture → Getting Started → Deep Dive → Optional
|
||||
|
||||
### Description Quality
|
||||
|
||||
| ❌ Bad | ✅ Good |
|
||||
|--------|---------|
|
||||
| "Architecture overview" | "System architecture showing how Orleans grains communicate via message passing with at-least-once delivery" |
|
||||
| "Getting started guide" | "Prerequisites, local dev setup with Docker Compose, and first API call walkthrough" |
|
||||
| "The API reference" | "REST endpoints with auth requirements, rate limits, and request/response schemas" |
|
||||
|
||||
## llms-full.txt Format
|
||||
|
||||
Same structure as `llms.txt` but with full content inlined:
|
||||
|
||||
```markdown
|
||||
# {Project Name}
|
||||
|
||||
> {Same summary}
|
||||
|
||||
{Same context}
|
||||
|
||||
## {Section Name}
|
||||
|
||||
<doc title="{Page Title}" path="{relative-path}">
|
||||
{Full markdown content — frontmatter stripped, citations and diagrams preserved}
|
||||
</doc>
|
||||
```
|
||||
|
||||
### Inlining Rules
|
||||
|
||||
- **Strip YAML frontmatter** (`---` blocks) from each page
|
||||
- **Preserve Mermaid diagrams** — keep `` ```mermaid `` fences intact
|
||||
- **Preserve citations** — all `[file:line](URL)` links stay as-is
|
||||
- **Preserve tables** — all markdown tables stay intact
|
||||
- **Preserve `<!-- Sources: -->` comments** — these provide diagram provenance
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This skill works best when wiki pages already exist (via `/deep-wiki:generate` or `/deep-wiki:page`). If no wiki exists yet:
|
||||
|
||||
1. Suggest running `/deep-wiki:generate` first
|
||||
2. OR generate a minimal `llms.txt` from README + source code scan (without wiki page links)
|
||||
|
||||
## Output Files
|
||||
|
||||
Generate three files:
|
||||
|
||||
| File | Purpose | Discoverability |
|
||||
|------|---------|-----------------|
|
||||
| `./llms.txt` | Root discovery file | Standard path per llms.txt spec. GitHub MCP `get_file_contents` and `search_code` find this first. |
|
||||
| `wiki/llms.txt` | Wiki-relative links | For VitePress deployment and wiki-internal navigation. |
|
||||
| `wiki/llms-full.txt` | Full inlined content | Comprehensive reference for agents needing all docs in one file. |
|
||||
|
||||
The root `./llms.txt` links into `wiki/` (e.g., `[Guide](./wiki/onboarding/contributor-guide.md)`). The `wiki/llms.txt` uses wiki-relative paths (e.g., `[Guide](./onboarding/contributor-guide.md)`).
|
||||
|
||||
If a root `llms.txt` already exists and was NOT generated by deep-wiki, do NOT overwrite it.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
Before finalizing:
|
||||
|
||||
- [ ] All linked files in `llms.txt` actually exist
|
||||
- [ ] All `<doc>` blocks in `llms-full.txt` have real content (not empty)
|
||||
- [ ] Blockquote is specific to this project (not generic boilerplate)
|
||||
- [ ] Sections ordered by importance
|
||||
- [ ] No duplicate page entries across sections
|
||||
- [ ] "Optional" section only contains truly optional content
|
||||
- [ ] `llms.txt` is concise (1-5 KB)
|
||||
- [ ] `llms-full.txt` contains all wiki pages
|
||||
Loading…
Add table
Add a link
Reference in a new issue