| name | framework-detection |
| description | Canonical rules for detecting the frontend/backend framework of a project (React Router, Next.js, Expo+RN, NestJS, Remix, Vite+React). Reference-only skill preloaded by other skills/subagents via the skills frontmatter field. |
| when_to_use | When any skill or agent needs to determine which framework a project uses, including initial analysis, path resolution, dependency verification, and template selection. |
| user-invocable | false |
Framework Detection
Single source of truth for framework detection. Any skill or agent that previously inlined framework checks (prd, architecture, design-system, tdd, code-reviewer, ux-design-lead) must defer here.
Detection order
Inspect the target directory in this order. Stop at the first match.
1. Config-file signals
| File present at the target directory | Framework |
|---|
react-router.config.ts or react-router.config.js | React Router (v7 framework mode) |
next.config.{ts,js,mjs,cjs} | Next.js |
remix.config.{ts,js} | Remix (legacy) |
app.config.{ts,js} or app.json containing an expo key, AND expo in dependencies | Expo + React Native |
nest-cli.json | NestJS |
src-tauri/tauri.conf.json AND src-tauri/Cargo.toml | Tauri 2 |
vite.config.{ts,js,mjs} without Next.js/React Router config AND react in dependencies | Vite + React |
2. package.json dependency signals (fallback when no config file is unambiguous)
Examine dependencies plus devDependencies:
| Dependency key | Framework |
|---|
expo or react-native | Expo + React Native |
next | Next.js |
react-router or any @react-router/* package | React Router |
@nestjs/core | NestJS |
@tauri-apps/api (plus a src-tauri/ directory at the target root) | Tauri 2 |
@remix-run/react | Remix (legacy) |
react only (no framework-level deps above) | Vite + React (assume) |
3. Unknown
If none of the above match, return unknown. Do not guess. Ask the user to describe the project's framework, or inspect a nearby README.md / docs/ARCHITECTURE.md for hints.
Package manager detection
Independent of framework, detect the package manager via lockfile in the target directory:
| Lockfile | Package manager |
|---|
bun.lockb or bun.lock | bun |
pnpm-lock.yaml | pnpm |
yarn.lock | yarn |
package-lock.json | npm |
If multiple lockfiles exist, the project is inconsistent — prefer bun > pnpm > yarn > npm but raise the drift to the user for resolution.
Monorepo awareness
Before locating the target directory, check for monorepo signals. See the monorepo-detection skill for the exact rules. In a monorepo, framework detection runs inside the relevant app/package directory, not at repo root.
Output contract
Consumers expect this result shape:
type FrameworkDetectionResult = {
framework:
| 'react-router'
| 'nextjs'
| 'remix'
| 'expo'
| 'nestjs'
| 'tauri'
| 'vite-react'
| 'unknown';
packageManager: 'bun' | 'pnpm' | 'yarn' | 'npm';
targetDir: string;
monorepo: boolean;
};
Change control
Updating this skill updates detection for every consumer. When adding a new framework, also add to:
prd/SKILL.md platform table
architecture/SKILL.md template table
design-system/SKILL.md bridge matrix
.claude/config.json sourceExtensions / testFilePatterns if the framework introduces new file types.
Tauri-specific note
A Tauri project usually contains a separate frontend framework (React,
Vue, Svelte, etc.) inside src/ while src-tauri/ holds the Rust backend.
When tauri is returned, downstream consumers that care about the
frontend stack should additionally detect inside src/ to resolve the
frontend framework — that result becomes the Tauri variant in
architecture (e.g., react+rust, vue+rust).