cortexhawk 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cortexhawk-team.yml +65 -0
- package/CHANGELOG.md +268 -0
- package/CLAUDE.md +96 -0
- package/LICENSE +21 -0
- package/PACKS.md +14 -0
- package/README.md +418 -0
- package/REGISTRY.md +23 -0
- package/agents/architect.md +46 -0
- package/agents/brainstormer.md +57 -0
- package/agents/code-simplifier.md +56 -0
- package/agents/codebase-mapper.md +63 -0
- package/agents/copywriter.md +48 -0
- package/agents/debugger.md +44 -0
- package/agents/designer.md +53 -0
- package/agents/devops.md +49 -0
- package/agents/docs-manager.md +50 -0
- package/agents/fullstack-developer.md +55 -0
- package/agents/git-manager.md +63 -0
- package/agents/implementer.md +30 -0
- package/agents/journal-writer.md +53 -0
- package/agents/planner.md +52 -0
- package/agents/project-manager.md +50 -0
- package/agents/researcher.md +46 -0
- package/agents/reviewer.md +63 -0
- package/agents/security-auditor.md +92 -0
- package/agents/teacher.md +71 -0
- package/agents/tester.md +41 -0
- package/commands/api-gen.md +17 -0
- package/commands/backlog.md +26 -0
- package/commands/bootstrap.md +32 -0
- package/commands/brainstorm.md +18 -0
- package/commands/build.md +16 -0
- package/commands/chain.md +46 -0
- package/commands/changelog.md +16 -0
- package/commands/check.md +40 -0
- package/commands/ci.md +32 -0
- package/commands/context.md +35 -0
- package/commands/debug.md +16 -0
- package/commands/deploy.md +16 -0
- package/commands/doc.md +15 -0
- package/commands/export.md +17 -0
- package/commands/journal.md +18 -0
- package/commands/learn.md +16 -0
- package/commands/map.md +16 -0
- package/commands/migrate.md +17 -0
- package/commands/monitor.md +16 -0
- package/commands/optimize.md +17 -0
- package/commands/plan.md +17 -0
- package/commands/pulse.md +46 -0
- package/commands/refactor.md +16 -0
- package/commands/research.md +18 -0
- package/commands/review.md +16 -0
- package/commands/scan.md +19 -0
- package/commands/ship.md +17 -0
- package/commands/simplify.md +16 -0
- package/commands/task.md +32 -0
- package/commands/tdd.md +17 -0
- package/commands/test.md +16 -0
- package/commands/upgrade.md +27 -0
- package/cortexhawk +450 -0
- package/hooks/agent-analytics.sh +67 -0
- package/hooks/branch-guard.sh +56 -0
- package/hooks/codex-dispatcher.sh +84 -0
- package/hooks/commit-guard.sh +71 -0
- package/hooks/compose.yml +47 -0
- package/hooks/dependency-check.sh +56 -0
- package/hooks/file-guard.sh +69 -0
- package/hooks/hooks.json +46 -0
- package/hooks/self-review.sh +71 -0
- package/hooks/session-start.sh +132 -0
- package/hooks/session-telemetry.sh +60 -0
- package/hooks/test-reminder.sh +75 -0
- package/install.sh +3805 -0
- package/mcp/README.md +37 -0
- package/mcp/context7.json +8 -0
- package/mcp/puppeteer.json +8 -0
- package/mcp/sequential-thinking.json +8 -0
- package/modes/default.md +5 -0
- package/modes/fast.md +5 -0
- package/modes/learn.md +9 -0
- package/modes/orchestration.md +5 -0
- package/modes/pair.md +10 -0
- package/modes/research.md +5 -0
- package/modes/review.md +5 -0
- package/package.json +32 -0
- package/profiles/api.json +27 -0
- package/profiles/data.json +23 -0
- package/profiles/fullstack.json +27 -0
- package/scripts/autodetect-profile.sh +68 -0
- package/scripts/benchmark.sh +106 -0
- package/scripts/chain-post-save.sh +23 -0
- package/scripts/generate-plans-index.sh +50 -0
- package/scripts/git-workflow-init.sh +115 -0
- package/scripts/install-codex.sh +128 -0
- package/scripts/interactive-init.sh +264 -0
- package/scripts/post-install-audit.sh +130 -0
- package/scripts/validate.sh +214 -0
- package/settings.json +90 -0
- package/setup.sh +67 -0
- package/skills/databases/schema-designer/SKILL.md +54 -0
- package/skills/databases/sql-optimizer/SKILL.md +37 -0
- package/skills/devops/ci-cd/SKILL.md +59 -0
- package/skills/devops/deployment/SKILL.md +49 -0
- package/skills/devops/docker/SKILL.md +57 -0
- package/skills/frameworks/api-design/SKILL.md +103 -0
- package/skills/frameworks/fastapi/SKILL.md +68 -0
- package/skills/frameworks/nextjs/SKILL.md +74 -0
- package/skills/frameworks/python/SKILL.md +89 -0
- package/skills/frameworks/react/SKILL.md +83 -0
- package/skills/frameworks/sveltekit/SKILL.md +69 -0
- package/skills/frameworks/tailwindcss/SKILL.md +75 -0
- package/skills/frameworks/typescript/SKILL.md +94 -0
- package/skills/meta/mcp-builder/SKILL.md +54 -0
- package/skills/meta/skill-creator/SKILL.md +43 -0
- package/skills/optimization/performance/SKILL.md +70 -0
- package/skills/quality/complexity-analyzer/SKILL.md +52 -0
- package/skills/quality/error-handling/SKILL.md +123 -0
- package/skills/quality/log-analyzer/SKILL.md +31 -0
- package/skills/quality/pattern-detector/SKILL.md +50 -0
- package/skills/security/auth-analyzer/SKILL.md +96 -0
- package/skills/security/compliance-checker/SKILL.md +92 -0
- package/skills/security/container-security/SKILL.md +128 -0
- package/skills/security/dependency-auditor/SKILL.md +100 -0
- package/skills/security/encryption/SKILL.md +94 -0
- package/skills/security/incident-response/SKILL.md +127 -0
- package/skills/security/secrets/SKILL.md +93 -0
- package/skills/security/security-headers/SKILL.md +83 -0
- package/skills/security/security-logging/SKILL.md +107 -0
- package/skills/security/vulnerability-scanner/SKILL.md +114 -0
- package/skills/testing/e2e-testing/SKILL.md +119 -0
- package/skills/testing/tdd/SKILL.md +40 -0
- package/skills/testing/test-generator/SKILL.md +39 -0
- package/skills/workflow/commit/SKILL.md +61 -0
- package/skills/workflow/confidence-check/SKILL.md +90 -0
- package/skills/workflow/pr-review-comments/SKILL.md +81 -0
- package/skills/workflow/pr-review-comments/scripts/fetch_comments.py +237 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-design
|
|
3
|
+
description: REST API conventions — URL structure, pagination, error responses, versioning, and HATEOAS basics.
|
|
4
|
+
detect: package.json:express package.json:fastify requirements.txt:fastapi pyproject.toml:fastapi
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# API Design
|
|
8
|
+
|
|
9
|
+
## URL Structure
|
|
10
|
+
```
|
|
11
|
+
GET /api/v1/users # List (paginated)
|
|
12
|
+
POST /api/v1/users # Create
|
|
13
|
+
GET /api/v1/users/:id # Read
|
|
14
|
+
PUT /api/v1/users/:id # Full update
|
|
15
|
+
PATCH /api/v1/users/:id # Partial update
|
|
16
|
+
DELETE /api/v1/users/:id # Delete
|
|
17
|
+
|
|
18
|
+
# Nested resources (max 2 levels)
|
|
19
|
+
GET /api/v1/users/:id/orders
|
|
20
|
+
POST /api/v1/users/:id/orders
|
|
21
|
+
|
|
22
|
+
# Filters, sorting, search via query params
|
|
23
|
+
GET /api/v1/users?status=active&sort=-created_at&q=john
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Pagination
|
|
27
|
+
```json
|
|
28
|
+
// Cursor-based (preferred for large datasets)
|
|
29
|
+
GET /api/v1/items?cursor=abc123&limit=20
|
|
30
|
+
|
|
31
|
+
{
|
|
32
|
+
"data": [...],
|
|
33
|
+
"pagination": {
|
|
34
|
+
"next_cursor": "def456",
|
|
35
|
+
"has_more": true,
|
|
36
|
+
"limit": 20
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Offset-based (simpler, fine for small datasets)
|
|
41
|
+
GET /api/v1/items?page=2&per_page=20
|
|
42
|
+
|
|
43
|
+
{
|
|
44
|
+
"data": [...],
|
|
45
|
+
"pagination": {
|
|
46
|
+
"page": 2,
|
|
47
|
+
"per_page": 20,
|
|
48
|
+
"total": 150,
|
|
49
|
+
"total_pages": 8
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Error Response Shape
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"error": {
|
|
58
|
+
"code": "VALIDATION_ERROR",
|
|
59
|
+
"message": "Invalid input",
|
|
60
|
+
"details": [
|
|
61
|
+
{ "field": "email", "message": "must be a valid email address" },
|
|
62
|
+
{ "field": "age", "message": "must be >= 0" }
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## HTTP Status Codes
|
|
69
|
+
| Code | When |
|
|
70
|
+
|---|---|
|
|
71
|
+
| 200 | Success (GET, PUT, PATCH) |
|
|
72
|
+
| 201 | Created (POST) |
|
|
73
|
+
| 204 | No content (DELETE) |
|
|
74
|
+
| 400 | Validation error, malformed request |
|
|
75
|
+
| 401 | Not authenticated |
|
|
76
|
+
| 403 | Authenticated but not authorized |
|
|
77
|
+
| 404 | Resource not found |
|
|
78
|
+
| 409 | Conflict (duplicate, state mismatch) |
|
|
79
|
+
| 422 | Semantically invalid (valid JSON, wrong values) |
|
|
80
|
+
| 429 | Rate limited |
|
|
81
|
+
| 500 | Server error (never expose internals) |
|
|
82
|
+
|
|
83
|
+
## Versioning
|
|
84
|
+
```
|
|
85
|
+
# URL path (simple, explicit)
|
|
86
|
+
/api/v1/users
|
|
87
|
+
/api/v2/users
|
|
88
|
+
|
|
89
|
+
# Header (cleaner URLs, harder to test)
|
|
90
|
+
Accept: application/vnd.api+json; version=2
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Prefer URL path versioning. Only bump major version for breaking changes.
|
|
94
|
+
|
|
95
|
+
## Checklist
|
|
96
|
+
- Nouns for resources, never verbs (`/users` not `/getUsers`)
|
|
97
|
+
- Plural resource names (`/users` not `/user`)
|
|
98
|
+
- Consistent error shape across all endpoints
|
|
99
|
+
- Pagination on ALL list endpoints — never return unbounded results
|
|
100
|
+
- Rate limiting with `Retry-After` header
|
|
101
|
+
- `Content-Type: application/json` on all responses
|
|
102
|
+
- CORS configured for allowed origins only
|
|
103
|
+
- Idempotency keys on POST endpoints for safe retries
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fastapi
|
|
3
|
+
description: FastAPI routes, Pydantic models, dependencies, middleware, and async patterns.
|
|
4
|
+
detect: requirements.txt:fastapi pyproject.toml:fastapi
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# FastAPI
|
|
8
|
+
|
|
9
|
+
## Route Patterns
|
|
10
|
+
```python
|
|
11
|
+
from fastapi import FastAPI, HTTPException, Depends, Query
|
|
12
|
+
from pydantic import BaseModel, Field
|
|
13
|
+
|
|
14
|
+
app = FastAPI()
|
|
15
|
+
|
|
16
|
+
class ItemCreate(BaseModel):
|
|
17
|
+
name: str = Field(..., min_length=1, max_length=100)
|
|
18
|
+
price: float = Field(..., gt=0)
|
|
19
|
+
|
|
20
|
+
class ItemResponse(BaseModel):
|
|
21
|
+
id: int
|
|
22
|
+
name: str
|
|
23
|
+
price: float
|
|
24
|
+
model_config = {"from_attributes": True}
|
|
25
|
+
|
|
26
|
+
@app.get("/items/{item_id}", response_model=ItemResponse)
|
|
27
|
+
async def get_item(item_id: int):
|
|
28
|
+
item = await db.get(item_id)
|
|
29
|
+
if not item:
|
|
30
|
+
raise HTTPException(status_code=404, detail="Item not found")
|
|
31
|
+
return item
|
|
32
|
+
|
|
33
|
+
@app.post("/items", response_model=ItemResponse, status_code=201)
|
|
34
|
+
async def create_item(item: ItemCreate):
|
|
35
|
+
return await db.create(item.model_dump())
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Dependencies
|
|
39
|
+
```python
|
|
40
|
+
async def get_current_user(token: str = Depends(oauth2_scheme)):
|
|
41
|
+
user = await verify_token(token)
|
|
42
|
+
if not user:
|
|
43
|
+
raise HTTPException(status_code=401, detail="Invalid token")
|
|
44
|
+
return user
|
|
45
|
+
|
|
46
|
+
@app.get("/me")
|
|
47
|
+
async def read_me(user: User = Depends(get_current_user)):
|
|
48
|
+
return user
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Middleware
|
|
52
|
+
```python
|
|
53
|
+
@app.middleware("http")
|
|
54
|
+
async def add_correlation_id(request: Request, call_next):
|
|
55
|
+
request.state.correlation_id = str(uuid4())
|
|
56
|
+
response = await call_next(request)
|
|
57
|
+
response.headers["X-Correlation-ID"] = request.state.correlation_id
|
|
58
|
+
return response
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Checklist
|
|
62
|
+
- Pydantic models for ALL request/response bodies — never use raw dicts
|
|
63
|
+
- `response_model` on every route to control output shape
|
|
64
|
+
- Use `Depends()` for auth, DB sessions, shared logic — not global state
|
|
65
|
+
- Async routes for I/O-bound work, sync for CPU-bound
|
|
66
|
+
- `HTTPException` with clear status codes and detail messages
|
|
67
|
+
- Background tasks via `BackgroundTasks` for non-blocking work
|
|
68
|
+
- Use `lifespan` context manager for startup/shutdown logic
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs
|
|
3
|
+
description: Next.js App Router, Server/Client Components, API routes, caching, and data fetching.
|
|
4
|
+
detect: package.json:next
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Next.js (App Router)
|
|
8
|
+
|
|
9
|
+
## File Conventions
|
|
10
|
+
```
|
|
11
|
+
app/
|
|
12
|
+
├── layout.tsx # Root layout (required)
|
|
13
|
+
├── page.tsx # Home page
|
|
14
|
+
├── loading.tsx # Loading UI (Suspense boundary)
|
|
15
|
+
├── error.tsx # Error boundary ('use client')
|
|
16
|
+
├── not-found.tsx # 404 page
|
|
17
|
+
├── api/route.ts # API route handler
|
|
18
|
+
└── [slug]/page.tsx # Dynamic route
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Server vs Client Components
|
|
22
|
+
```tsx
|
|
23
|
+
// Server Component (default) — access DB, secrets, no hooks
|
|
24
|
+
export default async function Page() {
|
|
25
|
+
const data = await db.query('SELECT * FROM items');
|
|
26
|
+
return <ItemList items={data} />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Client Component — interactivity, hooks, browser APIs
|
|
30
|
+
'use client';
|
|
31
|
+
export default function Counter() {
|
|
32
|
+
const [count, setCount] = useState(0);
|
|
33
|
+
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Data Fetching
|
|
38
|
+
```tsx
|
|
39
|
+
// Server Component — fetch with caching
|
|
40
|
+
async function getData() {
|
|
41
|
+
const res = await fetch('https://api.example.com/data', {
|
|
42
|
+
next: { revalidate: 3600 } // ISR: revalidate every hour
|
|
43
|
+
});
|
|
44
|
+
if (!res.ok) throw new Error('Failed to fetch');
|
|
45
|
+
return res.json();
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API Routes
|
|
50
|
+
```typescript
|
|
51
|
+
// app/api/items/route.ts
|
|
52
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
53
|
+
|
|
54
|
+
export async function GET(request: NextRequest) {
|
|
55
|
+
const items = await db.findMany();
|
|
56
|
+
return NextResponse.json(items);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function POST(request: NextRequest) {
|
|
60
|
+
const body = await request.json();
|
|
61
|
+
const item = await db.create(body);
|
|
62
|
+
return NextResponse.json(item, { status: 201 });
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Checklist
|
|
67
|
+
- Default to Server Components — add `'use client'` only when needed (hooks, events, browser APIs)
|
|
68
|
+
- Fetch data in Server Components, pass to Client Components as props
|
|
69
|
+
- Use `loading.tsx` for Suspense boundaries, `error.tsx` for error boundaries
|
|
70
|
+
- API routes in `app/api/` — use `NextRequest`/`NextResponse`
|
|
71
|
+
- Dynamic routes: `[slug]` for single, `[...slug]` for catch-all
|
|
72
|
+
- Metadata: export `metadata` object or `generateMetadata` function
|
|
73
|
+
- Use `redirect()` and `notFound()` from `next/navigation`
|
|
74
|
+
- Environment: `NEXT_PUBLIC_` prefix for client-exposed vars
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python
|
|
3
|
+
description: Python typing, async/await, packaging, virtual environments, and best practices.
|
|
4
|
+
detect: requirements.txt pyproject.toml
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Python
|
|
8
|
+
|
|
9
|
+
## Typing
|
|
10
|
+
```python
|
|
11
|
+
from typing import TypeVar, Generic, Protocol
|
|
12
|
+
from collections.abc import Sequence, Callable
|
|
13
|
+
|
|
14
|
+
# Function annotations
|
|
15
|
+
def process(items: list[str], limit: int = 10) -> dict[str, int]:
|
|
16
|
+
return {item: len(item) for item in items[:limit]}
|
|
17
|
+
|
|
18
|
+
# TypeVar for generics
|
|
19
|
+
T = TypeVar('T')
|
|
20
|
+
def first(items: Sequence[T]) -> T | None:
|
|
21
|
+
return items[0] if items else None
|
|
22
|
+
|
|
23
|
+
# Protocol for structural typing
|
|
24
|
+
class Readable(Protocol):
|
|
25
|
+
def read(self) -> str: ...
|
|
26
|
+
|
|
27
|
+
def load(source: Readable) -> str:
|
|
28
|
+
return source.read()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Async/Await
|
|
32
|
+
```python
|
|
33
|
+
import asyncio
|
|
34
|
+
from contextlib import asynccontextmanager
|
|
35
|
+
|
|
36
|
+
async def fetch_all(urls: list[str]) -> list[dict]:
|
|
37
|
+
async with aiohttp.ClientSession() as session:
|
|
38
|
+
tasks = [fetch_one(session, url) for url in urls]
|
|
39
|
+
return await asyncio.gather(*tasks)
|
|
40
|
+
|
|
41
|
+
@asynccontextmanager
|
|
42
|
+
async def get_db():
|
|
43
|
+
conn = await create_connection()
|
|
44
|
+
try:
|
|
45
|
+
yield conn
|
|
46
|
+
finally:
|
|
47
|
+
await conn.close()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Packaging
|
|
51
|
+
```toml
|
|
52
|
+
# pyproject.toml
|
|
53
|
+
[project]
|
|
54
|
+
name = "myproject"
|
|
55
|
+
version = "0.1.0"
|
|
56
|
+
requires-python = ">=3.11"
|
|
57
|
+
dependencies = ["fastapi>=0.100", "pydantic>=2.0"]
|
|
58
|
+
|
|
59
|
+
[project.optional-dependencies]
|
|
60
|
+
dev = ["pytest", "ruff", "mypy"]
|
|
61
|
+
|
|
62
|
+
[tool.ruff]
|
|
63
|
+
line-length = 88
|
|
64
|
+
select = ["E", "F", "I", "UP"]
|
|
65
|
+
|
|
66
|
+
[tool.mypy]
|
|
67
|
+
strict = true
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Virtual Environments
|
|
71
|
+
```bash
|
|
72
|
+
# Create and activate
|
|
73
|
+
python -m venv .venv
|
|
74
|
+
source .venv/bin/activate # Linux/macOS
|
|
75
|
+
.venv\Scripts\activate # Windows
|
|
76
|
+
|
|
77
|
+
# Modern alternative: uv
|
|
78
|
+
uv venv && uv pip install -r requirements.txt
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Checklist
|
|
82
|
+
- Type hints on all public functions — use `mypy --strict`
|
|
83
|
+
- `list[str]` not `List[str]` (Python 3.9+)
|
|
84
|
+
- `X | None` not `Optional[X]` (Python 3.10+)
|
|
85
|
+
- Dataclasses or Pydantic for structured data — not raw dicts
|
|
86
|
+
- `pathlib.Path` over `os.path` for file operations
|
|
87
|
+
- Context managers for resource management (files, connections, locks)
|
|
88
|
+
- f-strings for formatting — not `.format()` or `%`
|
|
89
|
+
- Use `ruff` for linting + formatting (replaces flake8, isort, black)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react
|
|
3
|
+
description: React hooks patterns, state management, memoization, Suspense, and error boundaries.
|
|
4
|
+
detect: package.json:react
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# React
|
|
8
|
+
|
|
9
|
+
## Hooks Patterns
|
|
10
|
+
```tsx
|
|
11
|
+
// Custom hook — extract reusable logic
|
|
12
|
+
function useDebounce<T>(value: T, delay: number): T {
|
|
13
|
+
const [debounced, setDebounced] = useState(value);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const timer = setTimeout(() => setDebounced(value), delay);
|
|
16
|
+
return () => clearTimeout(timer);
|
|
17
|
+
}, [value, delay]);
|
|
18
|
+
return debounced;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Data fetching hook
|
|
22
|
+
function useQuery<T>(url: string) {
|
|
23
|
+
const [data, setData] = useState<T | null>(null);
|
|
24
|
+
const [error, setError] = useState<Error | null>(null);
|
|
25
|
+
const [loading, setLoading] = useState(true);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
fetch(url, { signal: controller.signal })
|
|
30
|
+
.then(r => r.json()).then(setData)
|
|
31
|
+
.catch(e => { if (e.name !== 'AbortError') setError(e); })
|
|
32
|
+
.finally(() => setLoading(false));
|
|
33
|
+
return () => controller.abort();
|
|
34
|
+
}, [url]);
|
|
35
|
+
|
|
36
|
+
return { data, error, loading };
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Memoization
|
|
41
|
+
```tsx
|
|
42
|
+
// Memo — skip re-render if props unchanged
|
|
43
|
+
const ExpensiveList = memo(function ExpensiveList({ items }: Props) {
|
|
44
|
+
return items.map(item => <Item key={item.id} {...item} />);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// useMemo — expensive computation
|
|
48
|
+
const sorted = useMemo(() => items.sort(compareFn), [items]);
|
|
49
|
+
|
|
50
|
+
// useCallback — stable function reference for child props
|
|
51
|
+
const handleClick = useCallback((id: string) => {
|
|
52
|
+
setSelected(id);
|
|
53
|
+
}, []);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Error Boundaries
|
|
57
|
+
```tsx
|
|
58
|
+
class ErrorBoundary extends Component<Props, { error: Error | null }> {
|
|
59
|
+
state = { error: null };
|
|
60
|
+
static getDerivedStateFromError(error: Error) { return { error }; }
|
|
61
|
+
render() {
|
|
62
|
+
if (this.state.error) return this.props.fallback;
|
|
63
|
+
return this.props.children;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Usage
|
|
68
|
+
<ErrorBoundary fallback={<ErrorMessage />}>
|
|
69
|
+
<Suspense fallback={<Loading />}>
|
|
70
|
+
<AsyncComponent />
|
|
71
|
+
</Suspense>
|
|
72
|
+
</ErrorBoundary>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Checklist
|
|
76
|
+
- Custom hooks for shared logic — prefix with `use`
|
|
77
|
+
- `useEffect` cleanup: abort controllers, clear timers, unsubscribe
|
|
78
|
+
- Memo only when profiler shows re-render bottleneck — don't premature optimize
|
|
79
|
+
- Keys must be stable IDs — never use array index for dynamic lists
|
|
80
|
+
- Avoid derived state — compute from props/state directly
|
|
81
|
+
- `useRef` for values that don't trigger re-render (timers, DOM refs, previous values)
|
|
82
|
+
- Colocate state — lift up only when siblings need it
|
|
83
|
+
- Suspense + Error Boundaries for async content
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sveltekit
|
|
3
|
+
description: SvelteKit routing, load functions, form actions, hooks, and SSR/CSR patterns.
|
|
4
|
+
detect: package.json:svelte
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# SvelteKit
|
|
8
|
+
|
|
9
|
+
## Routing
|
|
10
|
+
```
|
|
11
|
+
src/routes/
|
|
12
|
+
├── +page.svelte # Page component
|
|
13
|
+
├── +page.ts # Universal load (runs server + client)
|
|
14
|
+
├── +page.server.ts # Server-only load + form actions
|
|
15
|
+
├── +layout.svelte # Shared layout
|
|
16
|
+
├── +layout.server.ts # Layout data loading
|
|
17
|
+
├── +error.svelte # Error boundary
|
|
18
|
+
├── +server.ts # API endpoint (GET, POST, PUT, DELETE)
|
|
19
|
+
└── [slug]/+page.svelte # Dynamic route
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Load Functions
|
|
23
|
+
```typescript
|
|
24
|
+
// +page.server.ts — server-only, access DB/secrets
|
|
25
|
+
export const load: PageServerLoad = async ({ params, locals }) => {
|
|
26
|
+
const item = await db.get(params.id);
|
|
27
|
+
if (!item) throw error(404, 'Not found');
|
|
28
|
+
return { item };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// +page.ts — universal, runs on server then client
|
|
32
|
+
export const load: PageLoad = async ({ fetch, params }) => {
|
|
33
|
+
const res = await fetch(`/api/items/${params.id}`);
|
|
34
|
+
return { item: await res.json() };
|
|
35
|
+
};
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Form Actions
|
|
39
|
+
```typescript
|
|
40
|
+
// +page.server.ts
|
|
41
|
+
export const actions: Actions = {
|
|
42
|
+
default: async ({ request, locals }) => {
|
|
43
|
+
const data = await request.formData();
|
|
44
|
+
const name = data.get('name');
|
|
45
|
+
if (!name) return fail(400, { name, missing: true });
|
|
46
|
+
await db.create({ name });
|
|
47
|
+
throw redirect(303, '/items');
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Hooks
|
|
53
|
+
```typescript
|
|
54
|
+
// src/hooks.server.ts
|
|
55
|
+
export const handle: Handle = async ({ event, resolve }) => {
|
|
56
|
+
const session = await getSession(event.cookies);
|
|
57
|
+
event.locals.user = session?.user;
|
|
58
|
+
return resolve(event);
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Checklist
|
|
63
|
+
- Use `+page.server.ts` for DB access, secrets, auth checks
|
|
64
|
+
- Use `+page.ts` for data that can be fetched client-side
|
|
65
|
+
- Form actions for mutations — progressive enhancement by default
|
|
66
|
+
- `throw redirect()` and `throw error()` — don't return them
|
|
67
|
+
- Use `$env/static/private` for server secrets, `$env/static/public` for client vars
|
|
68
|
+
- Prerender static pages: `export const prerender = true`
|
|
69
|
+
- Use `{@html}` sparingly — always sanitize user content
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tailwindcss
|
|
3
|
+
description: Tailwind CSS utility-first patterns, responsive design, dark mode, and custom configuration.
|
|
4
|
+
detect: package.json:tailwindcss
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Tailwind CSS
|
|
8
|
+
|
|
9
|
+
## Core Patterns
|
|
10
|
+
```html
|
|
11
|
+
<!-- Responsive: mobile-first -->
|
|
12
|
+
<div class="w-full md:w-1/2 lg:w-1/3">
|
|
13
|
+
|
|
14
|
+
<!-- Dark mode -->
|
|
15
|
+
<div class="bg-white dark:bg-gray-900 text-black dark:text-white">
|
|
16
|
+
|
|
17
|
+
<!-- States -->
|
|
18
|
+
<button class="bg-blue-500 hover:bg-blue-600 active:bg-blue-700 disabled:opacity-50">
|
|
19
|
+
|
|
20
|
+
<!-- Focus visible (keyboard only) -->
|
|
21
|
+
<input class="focus-visible:ring-2 focus-visible:ring-blue-500 outline-none">
|
|
22
|
+
|
|
23
|
+
<!-- Group hover -->
|
|
24
|
+
<div class="group">
|
|
25
|
+
<span class="group-hover:text-blue-500">Hover parent to change me</span>
|
|
26
|
+
</div>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Layout
|
|
30
|
+
```html
|
|
31
|
+
<!-- Flexbox -->
|
|
32
|
+
<div class="flex items-center justify-between gap-4">
|
|
33
|
+
|
|
34
|
+
<!-- Grid -->
|
|
35
|
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
36
|
+
|
|
37
|
+
<!-- Container with padding -->
|
|
38
|
+
<div class="container mx-auto px-4">
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Common Components
|
|
42
|
+
```html
|
|
43
|
+
<!-- Card -->
|
|
44
|
+
<div class="rounded-lg border bg-white p-6 shadow-sm dark:border-gray-700 dark:bg-gray-800">
|
|
45
|
+
|
|
46
|
+
<!-- Badge -->
|
|
47
|
+
<span class="inline-flex items-center rounded-full bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-800">
|
|
48
|
+
|
|
49
|
+
<!-- Input -->
|
|
50
|
+
<input class="w-full rounded-md border border-gray-300 px-3 py-2 text-sm placeholder:text-gray-400 focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500">
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Custom Config
|
|
54
|
+
```javascript
|
|
55
|
+
// tailwind.config.js
|
|
56
|
+
export default {
|
|
57
|
+
content: ['./src/**/*.{js,ts,jsx,tsx,svelte,vue}'],
|
|
58
|
+
darkMode: 'class',
|
|
59
|
+
theme: {
|
|
60
|
+
extend: {
|
|
61
|
+
colors: { brand: { 500: '#6366f1', 600: '#4f46e5' } },
|
|
62
|
+
fontFamily: { sans: ['Inter', 'sans-serif'] },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Checklist
|
|
69
|
+
- Mobile-first: write base styles, then `md:`, `lg:`, `xl:` overrides
|
|
70
|
+
- Use `dark:` variant — pair with `class` strategy for toggle support
|
|
71
|
+
- `@apply` in CSS only for highly reused utility combos (buttons, inputs)
|
|
72
|
+
- Prefer `gap-*` over margins between flex/grid children
|
|
73
|
+
- Use `sr-only` for screen-reader-only text
|
|
74
|
+
- Purge unused styles via `content` config — keep bundle small
|
|
75
|
+
- Custom colors in `theme.extend` — don't override defaults
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: typescript
|
|
3
|
+
description: TypeScript strict mode, generics, utility types, type guards, and discriminated unions.
|
|
4
|
+
detect: package.json
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# TypeScript
|
|
8
|
+
|
|
9
|
+
## Strict Config
|
|
10
|
+
```json
|
|
11
|
+
{
|
|
12
|
+
"compilerOptions": {
|
|
13
|
+
"strict": true,
|
|
14
|
+
"noUncheckedIndexedAccess": true,
|
|
15
|
+
"noUnusedLocals": true,
|
|
16
|
+
"noUnusedParameters": true,
|
|
17
|
+
"exactOptionalPropertyTypes": true
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Generics
|
|
23
|
+
```typescript
|
|
24
|
+
// Constrained generic
|
|
25
|
+
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
|
|
26
|
+
return obj[key];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Generic with default
|
|
30
|
+
type ApiResponse<T = unknown> = {
|
|
31
|
+
data: T;
|
|
32
|
+
status: number;
|
|
33
|
+
error?: string;
|
|
34
|
+
};
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Utility Types
|
|
38
|
+
```typescript
|
|
39
|
+
Partial<T> // All props optional
|
|
40
|
+
Required<T> // All props required
|
|
41
|
+
Pick<T, K> // Subset of props
|
|
42
|
+
Omit<T, K> // Remove props
|
|
43
|
+
Record<K, V> // Key-value map
|
|
44
|
+
ReturnType<F> // Function return type
|
|
45
|
+
Awaited<T> // Unwrap Promise
|
|
46
|
+
NonNullable<T> // Remove null/undefined
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Type Guards
|
|
50
|
+
```typescript
|
|
51
|
+
// Custom type guard
|
|
52
|
+
function isUser(value: unknown): value is User {
|
|
53
|
+
return typeof value === 'object' && value !== null && 'id' in value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Discriminated union
|
|
57
|
+
type Result<T> =
|
|
58
|
+
| { success: true; data: T }
|
|
59
|
+
| { success: false; error: string };
|
|
60
|
+
|
|
61
|
+
function handle<T>(result: Result<T>) {
|
|
62
|
+
if (result.success) {
|
|
63
|
+
console.log(result.data); // TypeScript knows data exists
|
|
64
|
+
} else {
|
|
65
|
+
console.error(result.error); // TypeScript knows error exists
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Patterns
|
|
71
|
+
```typescript
|
|
72
|
+
// Branded types for type safety
|
|
73
|
+
type UserId = string & { readonly __brand: 'UserId' };
|
|
74
|
+
function userId(id: string): UserId { return id as UserId; }
|
|
75
|
+
|
|
76
|
+
// Const assertion for literal types
|
|
77
|
+
const ROLES = ['admin', 'user', 'guest'] as const;
|
|
78
|
+
type Role = typeof ROLES[number]; // 'admin' | 'user' | 'guest'
|
|
79
|
+
|
|
80
|
+
// Satisfies — validate without widening
|
|
81
|
+
const config = {
|
|
82
|
+
port: 3000,
|
|
83
|
+
host: 'localhost',
|
|
84
|
+
} satisfies Record<string, string | number>;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Checklist
|
|
88
|
+
- `strict: true` always — no exceptions
|
|
89
|
+
- `unknown` over `any` — force type narrowing
|
|
90
|
+
- Discriminated unions for state machines and result types
|
|
91
|
+
- Type guards for runtime validation of external data
|
|
92
|
+
- `as const` for literal types, `satisfies` for validation without widening
|
|
93
|
+
- No type assertions (`as`) unless you've exhausted narrowing options
|
|
94
|
+
- Generic constraints with `extends` — don't leave generics unconstrained
|