skills/power-stack/SKILL.md
Bun/Elysia/React/MUI monorepo stack blueprint. Use when scaffolding a new project, adding a new app or package to the monorepo, choosing dependencies, or making architectural decisions. Contains the full tech stack, conventions, and wiring patterns.
npx skillsauth add avantmedialtd/skills power-stackInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
A self-contained blueprint for reconstructing this monorepo stack from scratch.
Bun workspaces. Root package.json declares "workspaces": ["apps/*", "packages/*"].
├── apps/
│ ├── api/ # REST backend (Bun + Elysia)
│ └── web/ # SPA frontend (React + Vite)
├── packages/
│ └── shared/ # Shared types, validators, permission utils
├── e2e/ # Playwright E2E test suite
├── openspec/ # Spec-driven change management
├── docker-compose.yml # PostgreSQL + app + test containers
├── tsconfig.json # Project references root
└── vitest.config.ts # Multi-project test config
TypeScript project references: root tsconfig.json uses "files": [] + "references" to delegate to each workspace. Each workspace tsconfig has "composite": true. Type-check with tsc --build --noEmit.
bun install, bun run)"type": "module")bundlerexperimentalDecorators + emitDecoratorMetadata enabled for TypeORMManaged via .env at the project root (with .env.example checked in). Backend reads via process.env, frontend via Vite's VITE_ prefix.
| Variable | Used by | Purpose | Default |
| --------------------- | -------- | ------------------------------- | -------------------- |
| POSTGRES_HOST | API | Database host | localhost |
| POSTGRES_PORT | API | Database port | 16002 |
| POSTGRES_USER | API | Database user | postgres |
| POSTGRES_PASSWORD | API | Database password | postgres |
| POSTGRES_DB | API | Database name | avant_id |
| API_PORT | API | HTTP listen port | 16000 |
| JWT_SECRET | API | Signing key for JWTs | dev fallback |
| WEB_URL | API | Frontend origin (CORS/redirects)| http://localhost:16001 |
| NODE_ENV | API | Environment mode | development |
| VITE_API_URL | Web | API base URL for frontend | http://localhost:16000 |
apps/api)Elysia 1.4 — lightweight Bun-native HTTP framework. Entry point imports reflect-metadata first, initializes TypeORM DataSource, then calls app.listen(port).
App composition via .use() plugin pattern:
new Elysia()
.use(cors())
.get('/health', () => ({ status: 'ok' }))
.use(authRoutes)
.use(adminRoutes);
Route groups use new Elysia({ prefix: '/auth' }) with request body validation via Elysia's t.Object() schema.
Use set.status pattern, not error():
async ({ body, set }) => {
set.status = 400;
return { error: 'Bad Request', message: 'Details' };
};
Elysia .derive() extracts Bearer token from Authorization header, verifies JWT via jose, and injects { user, auth } into context. .macro() defines requireAuth, requireAdmin, and requireAppPermission guards that check permissions and return 401/403.
DataSource configPOSTGRES_HOST, POSTGRES_PORT, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DBsynchronize: false always, schema changes via migrations only (packages/db/src/db/migrations/*.ts)bun run migrate (calls packages/db/src/db/migrate.ts)bun run generate-migration (uses TypeORM CLI against packages/db/src/data-source.ts)packages/db/src/db/migrate.ts:
import 'reflect-metadata';
import { AppDataSource } from './data-source';
async function runMigrations() {
try {
await AppDataSource.initialize();
console.log('Running migrations...');
await AppDataSource.runMigrations();
console.log('Migrations completed successfully');
await AppDataSource.destroy();
} catch (error) {
console.error('Migration failed:', error);
process.exit(1);
}
}
runMigrations();
@PrimaryGeneratedColumn('uuid'))@Column({ type: 'text' }), @Column({ type: 'boolean' }))@CreateDateColumn({ type: 'timestamptz' }) / @UpdateDateColumn@OneToMany('UserPermission', 'user')Relation<T> from TypeORM@node-rs/argon2jose library. Access tokens (short-lived) + refresh tokens (rotated, family-tracked for reuse detection). Revocation on reuse.| Prefix | Purpose |
| -------- | ------------------------------------------------------------------------ |
| /auth | Register, login, logout, refresh, me, password reset, email verification |
| /admin | User management, app management (requires admin permission) |
apps/web)react-dom/client (createRoot)react-router-dom) with BrowserRouter@vitejs/plugin-react)src/
├── main.tsx # Entry: StrictMode > ThemeModeProvider > BrowserRouter > AuthProvider > App
├── App.tsx # Route definitions
├── theme.ts # MUI createTheme (dark-only, neon brutalist)
├── api/
│ ├── authFetch.ts # Fetch wrapper with auto token refresh
│ └── admin.ts # Admin API calls
├── context/
│ ├── AuthContext.tsx # Auth state (login, register, logout, permissions)
│ └── ThemeContext.tsx # Theme mode provider
├── pages/ # Route pages (Login, Register, Dashboard, admin/*)
└── components/ # Shared components (AdminRoute, Logo, etc.)
AuthContext stores JWT tokens in localStorage (accessToken, refreshToken, tokenExpiresAt). authFetch wraps fetch() to:
Authorization: Bearer headerauth:logout custom event on unrecoverable failureProactive refresh timer runs every 13 minutes. Visibility change handler refreshes on tab focus if token is near expiry.
Dark-only neon brutalist design: borderRadius: 0 everywhere, heavy borders (2-4px), box shadows offset to bottom-right (4px 4px 0 #000), hover transforms (translate(-2px, -2px)), uppercase headings/buttons with tight letter-spacing. Palette: deep navy backgrounds (#0f0f23, #1a1a2e), indigo primary (#6366f1), amber secondary (#f59e0b), neon green success (#00ff88), hot pink error (#ff3366).
Vite builds static files. Served by nginx:alpine with a custom nginx.conf.
packages/shared)Pure TypeScript, no runtime dependencies. Exports via subpath: . (root), ./types, ./utils. Contains shared types, interfaces, and utility functions used by both backend and frontend. Built with tsc to dist/.
Vitest 4, configured as multi-project workspace in root vitest.config.ts. Three projects: shared, api, web. Each runs src/**/*.test.ts files. Run with bun run test.
Playwright 1.58 in a dedicated e2e/ package. Three categories:
*Api.spec.ts — API-only (no browser)*.spec.ts — UI tests (Chromium + mobile viewports)visual-*.spec.ts — Visual regression (baselines in e2e/tests/visual-baselines/)Runs in Docker Compose with --profile testing: spins up PostgreSQL, API, web (nginx), runs migrations, then executes Playwright.
Multi-stage oven/bun:1 image:
deps — install workspace dependenciesbuild — copy source, build shared packagerunner — copy built artifacts, run bun run src/index.ts directly (no compile step for API)Multi-stage build, final stage is nginx:alpine serving the Vite output from /usr/share/nginx/html.
| Service | Image/Build | Purpose | Profile |
| -------------- | ------------------- | ----------------------- | ------------- |
| postgres | postgres:17-alpine | Database | (default) |
| api | apps/api/Dockerfile | REST backend | apps, testing |
| web | apps/web/Dockerfile | Static frontend (nginx) | apps, testing |
| migrate-seed | (same as api) | Run migrations once | testing |
| e2e | e2e/Dockerfile | Playwright test runner | testing |
Health checks on all services. E2E waits for postgres + api + web to be healthy before running.
Jenkins pipeline (Groovy Jenkinsfile):
bun install per workspacebun run testgit pull && docker compose --profile apps up -d --build| Tool | Config | Purpose |
| --------------- | ----------------------- | ---------------------- |
| oxlint | oxlint . | Fast Rust-based linter |
| Prettier | prettier --write . | Code formatting |
| tsc --build | Project references mode | Type checking |
Spec-driven change management. Initialize with bunx openspec init. Structure:
openspec/
├── config.yaml # Schema and project context
├── specs/ # Living specifications (one dir per feature)
└── changes/ # Change proposals and artifacts
└── archive/ # Completed changes
Each feature has a spec.md describing its current state. Changes go through a structured workflow: proposal → artifacts (tasks, delta specs) → implementation → verification → archive. Specs are updated as changes land.
| Package | Purpose | | ---------------- | ------------------------- | | elysia | HTTP framework | | @elysiajs/cors | CORS middleware | | typeorm | ORM (PostgreSQL) | | pg | PostgreSQL driver | | jose | JWT signing/verification | | @node-rs/argon2 | Password hashing | | reflect-metadata | TypeORM decorator support |
| Package | Purpose | | ------------------- | ------------------------ | | react / react-dom | UI library | | @mui/material | Component library | | @emotion/react | CSS-in-JS (MUI peer dep) | | @emotion/styled | Styled components | | @mui/icons-material | Icon set | | react-router-dom | Client routing | | vite | Dev server + bundler |
| Package | Purpose | | ------------------ | ------------------------------- | | typescript | Type checking | | vitest | Unit test framework | | @playwright/test | E2E test framework | | oxlint | Linting | | prettier | Formatting | | bun run --parallel | Parallel dev scripts (built-in) |
development
Manage Bitbucket Cloud pull requests, comments, tasks, and pipelines from the command line. Use when working with PRs, reviewing code, leaving inline comments, creating PR tasks, triggering or inspecting Bitbucket Pipelines, or looking up reviewer account IDs.
development
Opinionated TypeScript and React development standards from Avant Media. Use when scaffolding new components, reviewing code, writing TypeScript interfaces or types, setting up project structure, creating React hooks, or working on any TypeScript/React codebase. Also use when the user asks about best practices, patterns, or conventions for TypeScript or React projects, even if they don't explicitly mention "standards."
tools
Assign a Jira issue to yourself and convert it into an OpenSpec proposal. Use when the user says "start work", "pick up an issue", "take the next ticket", or provides a Jira key and wants to begin work on it. Handles issue selection from the backlog, assignment, transition to In Progress, and scaffolding an OpenSpec change with Jira context embedded in the proposal.
tools
Manage Jira issues from the command line. Use when working with Jira issues, creating tasks, updating status, assigning work, linking issues, managing versions, setting or reading custom fields (Story Points, Sprint, Severity, etc.), or searching for issues.