Skip to main content

launch-light

Lightweight productivity bootstrap for greenfield TypeScript projects — pnpm workspace with apps/api/ (NestJS) by default, BFF-ready so adding apps/web/ later requires zero restructuring. OpenRouter + LangSmith env slots, a colored start-services.sh launcher that hard-fails when a service doesn't come up, README, and .env.example. NO Docker, NO Postgres, NO infra. Generates a runnable scaffold in seconds without asking domain questions. Use when the user says "/launch-light", "init", "init setup", "bootstrap minimal", "spin up Nest", "give me a scratch Nest app". Hands off to /launch-scratch-project the moment Docker/Postgres/Redis/queues/multi-service enter the picture.

Zur Installation springen

Quellinformationen

Repository
DevOtts/all-skills-you-will-ever-need
Letzte Quellaktivität
26. Mai 2026 um 01:49
Erkannte Sprache von SKILL.md
Englisch
Sterne
4
Forks
0

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
launch-light
description
Lightweight productivity bootstrap for greenfield TypeScript projects — pnpm workspace with apps/api/ (NestJS) by default, BFF-ready so adding apps/web/ later requires zero restructuring. OpenRouter + LangSmith env slots, a colored start-services.sh launcher that hard-fails when a service doesn't come up, README, and .env.example. NO Docker, NO Postgres, NO infra. Generates a runnable scaffold in seconds without asking domain questions. Use when the user says "/launch-light", "init", "init setup", "bootstrap minimal", "spin up Nest", "give me a scratch Nest app". Hands off to /launch-scratch-project the moment Docker/Postgres/Redis/queues/multi-service enter the picture.
author
DevOtts
author_url
https://github.com/DevOtts
# launch-light Fast, opinionated bootstrap for a greenfield TypeScript service. **Always BFF-ready**: scaffolds a pnpm workspace with `apps/api/` (NestJS) on day 1, so when someone later asks for a frontend it's just `apps/web/` dropped in next to it — no restructuring, no shared-tsconfig surprises, no "the API tsconfig swept the React code" failure modes. **No Docker. No databases. No queues.** The moment the user needs any of those, hand off to `/launch-scratch-project`. ## Soft reminder (not a gate) If this scaffold is for a graded exercise (interview, take-home, coaching session), spend a minute framing the problem out loud first — the scaffold is intentionally trivial so the graded thinking happens after, not during. But this skill will *not* refuse to run if you skip the framing — that's the user's call. ## When to use this skill vs `/launch-scratch-project` | Use `launch-light` | Use `/launch-scratch-project` | |---|---| | NestJS API (and optionally a Next.js/Vite app later) | Need Postgres/Redis/MongoDB/RabbitMQ/MinIO/etc. | | In-memory state or JSON-file DB | Need a docker-compose stack | | Single-shot scaffold, no questionnaire | Want the multi-service questionnaire + plan-approval flow | | `start-services.sh` for `pnpm dev` | `init.sh` for `docker compose up` + per-service docs | | < 5 minutes of bootstrap | Multi-service spike or interview build | If the user mentions any of: **Postgres, Redis, MongoDB, RabbitMQ, Kafka, MinIO, MailHog, ClickHouse, docker, queue, broker, persistence, multi-service** → STOP and say: > "That needs `/launch-scratch-project` — it has the questionnaire for picking services and generates the docker-compose stack. `/launch-light` is no-infra by design." Don't extend `launch-light` with Docker — that's the explicit boundary. ## Decoding the user's prompt There's only one mode: pnpm workspace with `apps/api/`. Parse for these optional flags: | User says | Effect | |---|---| | `/launch-light`, `init`, `init setup`, `bootstrap`, just NestJS | default workspace + apps/api | | `with frontend`, `with web`, `BFF`, `+ web`, `+ Next` | ALSO scaffold `apps/web/` (Next.js 15) on the same pass | | `--skip-llm` or `no openrouter` | drop OpenRouter env + helper | | `--skip-langsmith` or `no tracing` | drop LangSmith env + package | Don't ask a questionnaire. Generate, hand back. If the user later wants a frontend, `/iteration-impl` will drop `apps/web/` in alongside `apps/api/` — the workspace shape never has to change. ## What it generates ``` . ├── apps/ │ └── api/ # NestJS — :3001 — the single-app default │ ├── src/ │ │ ├── main.ts # bootstrap, listens on :3001 │ │ ├── app.module.ts │ │ ├── app.controller.ts # GET /health → { ok: true } │ │ ├── app.controller.spec.ts # seed failing test (intentional RED) │ │ └── llm/ │ │ └── openrouter.ts # configured OpenAI-SDK client → OpenRouter │ ├── package.json # "name": "api", nest start, vitest │ ├── tsconfig.json # scoped: include src/**/*.ts, exclude dist/node_modules │ ├── vitest.config.ts │ └── nest-cli.json ├── .env # symlinked into each app by start-services.sh ├── .env.example # OPENROUTER_*, LANGSMITH_*, PORT ├── .gitignore ├── package.json # workspace root (pnpm -r dev, no app deps here) ├── pnpm-workspace.yaml # packages: ["apps/*"] ├── start-services.sh # chmod +x — runs whatever apps it finds, hard-fails on unhealthy └── README.md ``` If the user invoked with `+ web` / BFF, ALSO scaffold: ``` └── apps/ └── web/ # Next.js 15 App Router — :3000 ├── app/page.tsx # calls API via /api proxy ├── app/api/health/route.ts ├── next.config.ts # rewrites /api/* → http://localhost:3001/* ├── package.json └── tsconfig.json # scoped to its own dir, "jsx": "preserve" ``` The workspace shape is the load-bearing decision. Adding `apps/web/` later (manually or via `/iteration-impl`) requires no restructuring because each app already has its own `package.json` and `tsconfig.json` — they were never coupled. In all cases: - **Vitest** is the test runner (not Jest — faster, consistent across web + api) - **One seed failing test** so TDD has a RED to start from - **`start-services.sh`** is the canonical launcher; `pnpm -r --parallel dev` works too - **`.runtime/`** holds process logs (gitignored) ## Phase 1 — Confirm and dispatch Print a one-screen confirmation (no questionnaire): ``` ## launch-light bootstrap Layout: pnpm workspace — apps/api/ [+ apps/web/ if requested] LLM: OpenRouter (env slot + apps/api/src/llm/openrouter.ts helper) [or "skipped"] Tracing: LangSmith (env slot + langsmith package) [or "skipped"] Runner: start-services.sh (colored, port preflight, watchdog, hard-fails on API/Web not ready) Tests: Vitest with one seed failing test Files I'll write: - package.json (workspace root), pnpm-workspace.yaml - apps/api/{package.json, tsconfig.json, vitest.config.ts, nest-cli.json} - apps/api/src/{main.ts, app.module.ts, app.controller.ts (+ spec)} - apps/api/src/llm/openrouter.ts (if LLM enabled) - apps/web/* (only if + web was requested) - .env.example, .gitignore - start-services.sh (chmod +x) - README.md Proceed? (y/n) ``` If `y` → write everything in one pass. If `n` → ask what to change. ## Phase 2 — Generate ### 2.1 Workspace root `pnpm-workspace.yaml`: ```yaml packages: - "apps/*" ``` `package.json` (workspace root): ```json { "name": "<project>", "private": true, "scripts": { "dev": "pnpm -r --parallel dev", "build": "pnpm -r build", "test": "pnpm -r test" }, "devDependencies": { "typescript": "^5.6.0" } } ``` ### 2.2 `apps/api/package.json` ```json { "name": "api", "version": "0.0.1", "private": true, "scripts": { "dev": "nest start --watch", "build": "nest build", "start": "node dist/main.js", "test": "vitest run", "test:watch": "vitest" }, "dependencies": { "@nestjs/common": "^10.4.0", "@nestjs/core": "^10.4.0", "@nestjs/platform-express": "^10.4.0", "dotenv": "^16.4.0", "openai": "^4.70.0", "langsmith": "^0.2.0", "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1" }, "devDependencies": { "@nestjs/cli": "^10.4.0", "@nestjs/testing": "^10.4.0", "@types/node": "^22.0.0", "typescript": "^5.6.0", "vitest": "^2.1.0", "vite-tsconfig-paths": "^5.0.0" } } ``` Drop `langsmith` if `--skip-langsmith`. Drop `openai` if `--skip-llm`. ### 2.3 `apps/api/tsconfig.json` — scoped from day 1 ```json { "compilerOptions": { "module": "commonjs", "declaration": true, "removeComments": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "allowSyntheticDefaultImports": true, "target": "ES2021", "sourceMap": true, "outDir": "./dist", "baseUrl": "./", "incremental": true, "skipLibCheck": true, "strictNullChecks": true, "noImplicitAny": true, "strictBindCallApply": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "esModuleInterop": true, "resolveJsonModule": true }, "include": ["src/**/*.ts", "scripts/**/*.ts"], "exclude": ["node_modules", "dist"] } ``` **Why explicit `include`/`exclude`:** without them, `tsc --watch` (spawned by `nest start --watch`) sweeps the whole project tree. If a sibling folder ever appears (e.g. `apps/web/`, `evals/`, a scaffolded mockup) and the tsconfig is unscoped, tsc tries to compile its `.ts`/`.tsx` files, fails on JSX or React types, and **Nest never boots cleanly — the API silently dies and the only symptom is `Internal Server Error` from a frontend proxy.** Ship the tsconfig scoped from day 1; it costs nothing. ### 2.4 NestJS source files (`apps/api/src/`) **`main.ts`** ```typescript import 'dotenv/config'; // MUST be first — populates process.env before any module that reads env at load time (e.g. an OpenAI/LangSmith client constructed at module scope). nest start does NOT auto-load .env. import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); const port = Number(process.env.PORT ?? 3001); await app.listen(port); console.log(`API listening on http://localhost:${port}`); } bootstrap(); ``` **`app.module.ts`** ```typescript import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; @Module({ controllers: [AppController], }) export class AppModule {} ``` **`app.controller.ts`** ```typescript import { Controller, Get } from '@nestjs/common'; @Controller() export class AppController { @Get('health') health() { return { ok: true }; } } ``` **`app.controller.spec.ts`** (seed failing test — intentional RED) ```typescript import { describe, it, expect } from 'vitest'; import { AppController } from './app.controller'; describe('AppController', () => { it('health returns ok=true', () => { const c = new AppController(); expect(c.health()).toEqual({ ok: true }); }); it('TODO: replace this with the real first feature test', () => { expect.fail('Seed RED — delete or rewrite this test as your first real failing test.'); }); }); ``` The first test passes; the second is the deliberate RED that the TDD loop starts from. ### 2.5 OpenRouter helper (if LLM enabled) **`apps/api/src/llm/openrouter.ts`** ```typescript import OpenAI from 'openai'; import { wrapOpenAI } from 'langsmith/wrappers'; const apiKey = process.env.OPENROUTER_API_KEY; if (!apiKey) { throw new Error('OPENROUTER_API_KEY is missing — see .env.example'); } // `wrapOpenAI` emits LangSmith runs with token counts + cost estimates for every // `chat.completions.create` call. // // Two enhancements you almost always want on top of the bare wrapper: // // 1. NAME EVERY CALL. By default each run shows up in LangSmith as "ChatOpenAI", // which is useless when one request fires 3+ LLM calls. Pass a descriptive // `name` via `langsmithExtra` on each call — see `lsExtra` helper below. // // 2. WRAP THE CALLER WITH `traceable` TO GET A WATERFALL. Without a parent span // every `chat.completions.create` is a top-level run — flat list, no chain // of thought. Wrapping the function that makes the calls creates a parent // span; nested wrapOpenAI calls auto-attach as children via AsyncLocalStorage. // See "LangSmith tracing waterfall" below for the pattern. // // COST CAVEAT: LangSmith computes cost from its built-in price table for the // un-prefixed model id (e.g. `claude-sonnet-4-5`, not the OpenRouter slug // `anthropic/claude-sonnet-4.5`). OpenRouter adds margin on top, so the "cost" // number is directional, not your actual OpenRouter bill. Token counts are // correct. export const openrouter = wrapOpenAI( new OpenAI({ apiKey, baseURL: 'https://openrouter.ai/api/v1', defaultHeaders: { 'HTTP-Referer': process.env.OPENROUTER_SITE_URL ?? 'http://localhost:3001', 'X-Title': process.env.OPENROUTER_APP_NAME ?? '<project>', }, }), ); export const DEFAULT_MODEL = process.env.OPENROUTER_MODEL ?? 'anthropic/claude-sonnet-4.5'; // LangSmith's pricing table keys on un-prefixed canonical model ids // (e.g. `claude-sonnet-4-5`, not the OpenRouter slug `anthropic/claude-sonnet-4.5`). export function toLangSmithModelName(openrouterModelId: string): string {
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen