start-vibing-stacks 2.2.0 → 2.4.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/README.md +2 -2
- package/dist/detector.js +23 -11
- package/dist/index.js +78 -5
- package/dist/scanner.d.ts +12 -0
- package/dist/scanner.js +501 -0
- package/dist/setup.js +46 -1
- package/dist/types.d.ts +24 -0
- package/dist/ui.js +6 -5
- package/package.json +1 -1
- package/stacks/_shared/config/security-rules.json +27 -5
- package/stacks/_shared/hooks/user-prompt-submit.ts +26 -2
- package/stacks/frontend/react/skills/preline-ui/SKILL.md +31 -35
- package/stacks/frontend/react/skills/react-standards/SKILL.md +20 -20
- package/stacks/frontend/react/skills/react-ui-patterns/SKILL.md +78 -42
- package/stacks/frontend/react/skills/tailwind-patterns/SKILL.md +1 -1
- package/stacks/frontend/react/skills/zod-validation/SKILL.md +84 -18
- package/stacks/frontend/react-inertia/skills/inertia-react/SKILL.md +342 -0
- package/stacks/frontend/react-inertia/skills/react-standards/SKILL.md +267 -0
- package/stacks/nodejs/skills/nextjs-app-router/SKILL.md +101 -0
- package/stacks/nodejs/stack.json +43 -121
- package/stacks/php/skills/laravel-octane/SKILL.md +155 -53
- package/stacks/php/skills/laravel-patterns/SKILL.md +244 -39
- package/stacks/php/skills/php-patterns/SKILL.md +113 -53
- package/stacks/php/skills/security-scan-php/SKILL.md +161 -43
- package/stacks/php/stack.json +19 -6
- package/templates/CLAUDE-nodejs.md +323 -0
- package/templates/CLAUDE-php.md +233 -33
|
@@ -1,80 +1,198 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Laravel Security Scan
|
|
2
2
|
|
|
3
|
-
## OWASP Top 10 for
|
|
3
|
+
## OWASP Top 10 for Laravel
|
|
4
4
|
|
|
5
5
|
### A01 - Broken Access Control
|
|
6
6
|
|
|
7
7
|
```php
|
|
8
|
-
//
|
|
9
|
-
$userId = $
|
|
8
|
+
// WRONG: Trust user input for authorization
|
|
9
|
+
$userId = $request->input('user_id');
|
|
10
10
|
$user = User::find($userId);
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
$user =
|
|
12
|
+
// CORRECT: Use authenticated session
|
|
13
|
+
$user = $request->user();
|
|
14
|
+
|
|
15
|
+
// CORRECT: Scope queries by authenticated user
|
|
16
|
+
$query = Resource::query();
|
|
17
|
+
if (!$request->user()->isAdmin()) {
|
|
18
|
+
$query->where('user_id', $request->user()->id);
|
|
19
|
+
}
|
|
14
20
|
```
|
|
15
21
|
|
|
22
|
+
**Rules:**
|
|
23
|
+
- NEVER return unscoped queries — always filter by authenticated user
|
|
24
|
+
- Use Policies for authorization logic
|
|
25
|
+
- Use Form Requests with `authorize()` method
|
|
26
|
+
- Use Route Model Binding with scoping
|
|
27
|
+
|
|
16
28
|
### A02 - Cryptographic Failures
|
|
17
29
|
|
|
18
30
|
```php
|
|
19
|
-
//
|
|
31
|
+
// WRONG
|
|
20
32
|
md5($password);
|
|
21
33
|
sha1($password);
|
|
22
34
|
|
|
23
|
-
//
|
|
24
|
-
|
|
25
|
-
|
|
35
|
+
// CORRECT (Laravel handles this via Hash facade)
|
|
36
|
+
Hash::make($password);
|
|
37
|
+
Hash::check($input, $hashedPassword);
|
|
38
|
+
|
|
39
|
+
// For API tokens: use Laravel Sanctum
|
|
40
|
+
// Table: users_access_tokens (UUIDs + Soft Deletes)
|
|
41
|
+
// All token actions logged via Loggable trait
|
|
26
42
|
```
|
|
27
43
|
|
|
28
44
|
### A03 - Injection
|
|
29
45
|
|
|
30
46
|
```php
|
|
31
|
-
//
|
|
32
|
-
$
|
|
47
|
+
// WRONG: SQL Injection via DB::raw
|
|
48
|
+
$users = DB::select("SELECT * FROM users WHERE id = {$id}");
|
|
49
|
+
DB::raw("WHERE name = '{$name}'");
|
|
33
50
|
|
|
34
|
-
//
|
|
35
|
-
$
|
|
36
|
-
$stmt->execute([':id' => $id]);
|
|
51
|
+
// CORRECT: Eloquent (preferred)
|
|
52
|
+
$user = User::findOrFail($id);
|
|
37
53
|
|
|
38
|
-
//
|
|
39
|
-
$
|
|
40
|
-
|
|
41
|
-
|
|
54
|
+
// CORRECT: Query Builder with bindings
|
|
55
|
+
$users = DB::table('users')->where('name', $name)->get();
|
|
56
|
+
|
|
57
|
+
// CORRECT: Raw with parameter binding (last resort)
|
|
58
|
+
$results = DB::select('SELECT * FROM users WHERE id = ?', [$id]);
|
|
42
59
|
```
|
|
43
60
|
|
|
44
61
|
### A07 - XSS Prevention
|
|
45
62
|
|
|
46
63
|
```php
|
|
47
|
-
//
|
|
48
|
-
|
|
64
|
+
// WRONG: Unescaped output in Blade
|
|
65
|
+
{!! $userInput !!}
|
|
49
66
|
|
|
50
|
-
//
|
|
51
|
-
|
|
67
|
+
// CORRECT: Auto-escaped (Blade default)
|
|
68
|
+
{{ $userInput }}
|
|
52
69
|
|
|
53
|
-
//
|
|
54
|
-
{
|
|
55
|
-
{!! $trustedHtml !!} // Only for trusted content
|
|
70
|
+
// Only use {!! !!} for TRUSTED, pre-sanitized HTML
|
|
71
|
+
{!! $trustedHtmlFromCMS !!}
|
|
56
72
|
```
|
|
57
73
|
|
|
58
|
-
|
|
74
|
+
### A08 - Insecure Deserialization
|
|
75
|
+
|
|
76
|
+
```php
|
|
77
|
+
// WRONG: Unserialize user data
|
|
78
|
+
$data = unserialize($request->input('data'));
|
|
79
|
+
|
|
80
|
+
// CORRECT: Use JSON with Model casts
|
|
81
|
+
protected $casts = [
|
|
82
|
+
'metadata' => 'array',
|
|
83
|
+
'settings' => 'array',
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
// CORRECT: Defensive JSON handling
|
|
87
|
+
$data = is_string($model->data)
|
|
88
|
+
? json_decode($model->data, true)
|
|
89
|
+
: $model->data;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Laravel-Specific Security
|
|
93
|
+
|
|
94
|
+
### Mass Assignment Protection
|
|
95
|
+
|
|
96
|
+
```php
|
|
97
|
+
// WRONG: No protection
|
|
98
|
+
$user = User::create($request->all());
|
|
99
|
+
|
|
100
|
+
// CORRECT: Define $fillable
|
|
101
|
+
class User extends Model {
|
|
102
|
+
protected $fillable = ['name', 'email'];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// CORRECT: Use Form Request validated data
|
|
106
|
+
$user = User::create($request->validated());
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Environment Variables
|
|
110
|
+
|
|
111
|
+
```php
|
|
112
|
+
// WRONG: env() outside config files (null when cached)
|
|
113
|
+
$apiKey = env('API_KEY');
|
|
59
114
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
115
|
+
// CORRECT: Use config files
|
|
116
|
+
// config/services.php
|
|
117
|
+
'stripe' => ['key' => env('STRIPE_KEY')],
|
|
118
|
+
|
|
119
|
+
// In code:
|
|
120
|
+
$apiKey = config('services.stripe.key');
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### CSRF & Authentication
|
|
124
|
+
|
|
125
|
+
```php
|
|
126
|
+
// Blade forms — CSRF automatic
|
|
127
|
+
<form method="POST">
|
|
128
|
+
@csrf
|
|
129
|
+
...
|
|
130
|
+
</form>
|
|
131
|
+
|
|
132
|
+
// API routes — use Sanctum middleware
|
|
133
|
+
Route::middleware('auth:sanctum')->group(function () {
|
|
134
|
+
Route::apiResource('users', UserController::class);
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### File Upload Validation
|
|
139
|
+
|
|
140
|
+
```php
|
|
141
|
+
public function rules(): array
|
|
142
|
+
{
|
|
143
|
+
return [
|
|
144
|
+
'avatar' => [
|
|
145
|
+
'required',
|
|
146
|
+
'image',
|
|
147
|
+
'mimes:jpg,jpeg,png,webp',
|
|
148
|
+
'max:2048', // 2MB
|
|
149
|
+
'dimensions:max_width=2000,max_height=2000',
|
|
150
|
+
],
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Octane Security Considerations
|
|
156
|
+
|
|
157
|
+
```php
|
|
158
|
+
// WRONG: Static state leaks user data between requests
|
|
159
|
+
class AuthService {
|
|
160
|
+
private static ?User $currentUser = null; // LEAKS!
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// WRONG: Superglobals are stale in Octane
|
|
164
|
+
$token = $_SERVER['HTTP_AUTHORIZATION'];
|
|
165
|
+
|
|
166
|
+
// CORRECT: Use Request object
|
|
167
|
+
$token = $request->bearerToken();
|
|
168
|
+
$user = $request->user();
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Security Checklist
|
|
70
172
|
|
|
71
|
-
|
|
173
|
+
- [ ] All queries use Eloquent or Query Builder (no raw SQL with user input)
|
|
174
|
+
- [ ] All Blade output uses `{{ }}` (auto-escaped)
|
|
175
|
+
- [ ] CSRF protection on all forms (`@csrf`)
|
|
176
|
+
- [ ] Passwords use `Hash::make()` / `Hash::check()`
|
|
177
|
+
- [ ] File uploads validated (type, size, dimensions)
|
|
178
|
+
- [ ] API routes protected with Sanctum middleware
|
|
179
|
+
- [ ] Models define `$fillable` (no `$guarded = []`)
|
|
180
|
+
- [ ] Queries scoped by authenticated user (unless admin)
|
|
181
|
+
- [ ] No `env()` calls outside config files
|
|
182
|
+
- [ ] No static state in services (Octane-safe)
|
|
183
|
+
- [ ] No superglobals (`$_GET`, `$_POST`, `$_SESSION`)
|
|
184
|
+
- [ ] Sensitive headers set (X-Content-Type-Options, X-Frame-Options, CSP)
|
|
185
|
+
- [ ] Rate limiting on authentication endpoints
|
|
186
|
+
|
|
187
|
+
## Sensitive Patterns (FORBIDDEN)
|
|
72
188
|
|
|
73
189
|
| Pattern | Risk |
|
|
74
190
|
|---------|------|
|
|
75
|
-
|
|
|
76
|
-
| `
|
|
191
|
+
| `DB::raw()` with user input | SQL Injection |
|
|
192
|
+
| `{!! $userInput !!}` | XSS |
|
|
77
193
|
| `md5()` / `sha1()` for passwords | Weak hashing |
|
|
78
|
-
|
|
|
79
|
-
| `
|
|
80
|
-
|
|
|
194
|
+
| Dynamic code execution | RCE |
|
|
195
|
+
| `unserialize()` on user data | Object injection |
|
|
196
|
+
| `$request->all()` without `$fillable` | Mass assignment |
|
|
197
|
+
| `env()` in runtime code | Null after config cache |
|
|
198
|
+
| Static user state in services | Data leaks in Octane |
|
package/stacks/php/stack.json
CHANGED
|
@@ -27,13 +27,20 @@
|
|
|
27
27
|
"name": "Laravel 12 + Octane (RoadRunner) + Inertia.js",
|
|
28
28
|
"icon": "🚀",
|
|
29
29
|
"detectFiles": ["artisan", "rr.yaml"],
|
|
30
|
-
"default": true
|
|
30
|
+
"default": true,
|
|
31
|
+
"skills": [
|
|
32
|
+
"laravel-patterns",
|
|
33
|
+
"laravel-octane"
|
|
34
|
+
]
|
|
31
35
|
},
|
|
32
36
|
{
|
|
33
37
|
"id": "laravel",
|
|
34
38
|
"name": "Laravel 12 (standard)",
|
|
35
39
|
"icon": "🏗️",
|
|
36
|
-
"detectFiles": ["artisan", "bootstrap/app.php"]
|
|
40
|
+
"detectFiles": ["artisan", "bootstrap/app.php"],
|
|
41
|
+
"skills": [
|
|
42
|
+
"laravel-patterns"
|
|
43
|
+
]
|
|
37
44
|
}
|
|
38
45
|
],
|
|
39
46
|
"databases": [
|
|
@@ -46,12 +53,20 @@
|
|
|
46
53
|
"id": "react-inertia",
|
|
47
54
|
"name": "ReactJS 19 + Inertia.js + TailwindCSS 4",
|
|
48
55
|
"icon": "⚛️",
|
|
49
|
-
"default": true
|
|
56
|
+
"default": true,
|
|
57
|
+
"frameworks": ["laravel", "laravel-octane"]
|
|
50
58
|
},
|
|
51
59
|
{
|
|
52
60
|
"id": "blade",
|
|
53
61
|
"name": "Blade + TailwindCSS 4",
|
|
54
|
-
"icon": "🖼️"
|
|
62
|
+
"icon": "🖼️",
|
|
63
|
+
"frameworks": ["laravel", "laravel-octane"]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "livewire",
|
|
67
|
+
"name": "Livewire + Alpine.js",
|
|
68
|
+
"icon": "⚡",
|
|
69
|
+
"frameworks": ["laravel", "laravel-octane"]
|
|
55
70
|
},
|
|
56
71
|
{
|
|
57
72
|
"id": "none",
|
|
@@ -68,8 +83,6 @@
|
|
|
68
83
|
"phpstan-analysis",
|
|
69
84
|
"composer-workflow",
|
|
70
85
|
"security-scan-php",
|
|
71
|
-
"laravel-patterns",
|
|
72
|
-
"laravel-octane",
|
|
73
86
|
"api-design"
|
|
74
87
|
],
|
|
75
88
|
"requirements": [
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
> **CHARACTER LIMIT**: Max 40,000 chars. Validate with `wc -m CLAUDE.md` before commit.
|
|
4
|
+
|
|
5
|
+
## Last Change
|
|
6
|
+
|
|
7
|
+
**Branch:** main
|
|
8
|
+
**Date:** {{DATE}}
|
|
9
|
+
**Summary:** Initial project setup with start-vibing-stacks (Node.js)
|
|
10
|
+
|
|
11
|
+
## 30 Seconds Overview
|
|
12
|
+
|
|
13
|
+
{{PROJECT_NAME}} is a TypeScript project using {{FRAMEWORK}}.
|
|
14
|
+
|
|
15
|
+
## Stack
|
|
16
|
+
|
|
17
|
+
| Component | Technology |
|
|
18
|
+
|-----------|------------|
|
|
19
|
+
| Runtime | Bun / Node.js 20+ |
|
|
20
|
+
| Language | TypeScript **strict mode** |
|
|
21
|
+
| Framework | {{FRAMEWORK}} |
|
|
22
|
+
| Database | {{DATABASE}} |
|
|
23
|
+
| Validation | Zod |
|
|
24
|
+
| Testing | Vitest + Playwright |
|
|
25
|
+
| UI | React + Tailwind + shadcn |
|
|
26
|
+
| Data | TanStack Query + Sonner |
|
|
27
|
+
| Forms | react-hook-form + Zod |
|
|
28
|
+
|
|
29
|
+
## Architecture
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
project/
|
|
33
|
+
├── CLAUDE.md # This file (40k char max)
|
|
34
|
+
├── .claude/
|
|
35
|
+
│ ├── agents/ # 6 active subagents
|
|
36
|
+
│ ├── skills/ # Skill systems (auto-injected)
|
|
37
|
+
│ ├── hooks/ # Validation hooks
|
|
38
|
+
│ ├── config/ # Project configuration
|
|
39
|
+
│ └── commands/ # Slash commands (/feature, /fix, /research, /validate)
|
|
40
|
+
├── src/
|
|
41
|
+
│ ├── app/ # Next.js App Router pages
|
|
42
|
+
│ │ ├── (marketing)/ # Route group — public pages
|
|
43
|
+
│ │ ├── (app)/ # Route group — authenticated
|
|
44
|
+
│ │ │ └── dashboard/
|
|
45
|
+
│ │ │ ├── page.tsx
|
|
46
|
+
│ │ │ └── _components/ # Page-specific components
|
|
47
|
+
│ │ ├── api/ # Route handlers (API endpoints)
|
|
48
|
+
│ │ ├── layout.tsx # Root layout with providers
|
|
49
|
+
│ │ └── loading.tsx # Global loading skeleton
|
|
50
|
+
│ ├── components/
|
|
51
|
+
│ │ ├── ui/ # shadcn primitives (Button, Input)
|
|
52
|
+
│ │ ├── layout/ # Header, Sidebar, Footer
|
|
53
|
+
│ │ ├── shared/ # Cross-feature components
|
|
54
|
+
│ │ └── providers.tsx # Context providers (client)
|
|
55
|
+
│ ├── lib/
|
|
56
|
+
│ │ ├── utils.ts # cn utility (clsx + tailwind-merge)
|
|
57
|
+
│ │ ├── api/ # API client instances
|
|
58
|
+
│ │ └── validations/ # Zod schemas
|
|
59
|
+
│ ├── hooks/ # Custom React hooks
|
|
60
|
+
│ └── styles/ # Global styles, theme tokens
|
|
61
|
+
├── types/ # ALL TypeScript interfaces (MANDATORY)
|
|
62
|
+
├── tests/
|
|
63
|
+
│ ├── unit/ # Vitest unit tests
|
|
64
|
+
│ └── e2e/ # Playwright E2E tests
|
|
65
|
+
├── public/ # Static assets
|
|
66
|
+
├── tsconfig.json # TypeScript config (strict: true)
|
|
67
|
+
├── next.config.ts # Framework config
|
|
68
|
+
├── tailwind.config.ts # TailwindCSS config
|
|
69
|
+
├── vitest.config.ts # Test config
|
|
70
|
+
└── package.json # Dependencies
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Workflow
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
0. TODO LIST → Create detailed todo list from prompt
|
|
77
|
+
1. BRANCH → Create feature/ | fix/ | refactor/ | test/
|
|
78
|
+
2. RESEARCH → Run research-web agent for NEW features
|
|
79
|
+
3. IMPLEMENT → Follow project rules + strict types
|
|
80
|
+
4. TEST → Run tester agent (Vitest / Playwright)
|
|
81
|
+
5. DOCUMENT → Run documenter agent for modified files
|
|
82
|
+
6. UPDATE → Update THIS FILE (CLAUDE.md) with changes
|
|
83
|
+
7. QUALITY → bun run typecheck && lint && test
|
|
84
|
+
8. COMMIT → Conventional commits, merge to main
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## CLAUDE.md Update Rules
|
|
88
|
+
|
|
89
|
+
> After ANY implementation, update this file to reflect the current state.
|
|
90
|
+
|
|
91
|
+
| Change Type | Sections to Update |
|
|
92
|
+
|-------------|-------------------|
|
|
93
|
+
| Any file change | Last Change (branch, date, summary) |
|
|
94
|
+
| API/routes | Critical Rules, Architecture |
|
|
95
|
+
| UI components | Architecture, Component Organization |
|
|
96
|
+
| New feature | 30s Overview, Architecture |
|
|
97
|
+
| New gotcha | FORBIDDEN or NRY |
|
|
98
|
+
| New dependency | Stack |
|
|
99
|
+
| Workflow change | Workflow section |
|
|
100
|
+
|
|
101
|
+
1. **Last Change** documents WHAT was done
|
|
102
|
+
2. **Other sections** document HOW things work NOW
|
|
103
|
+
3. **Both must be current** — updating only Last Change is insufficient
|
|
104
|
+
4. Keep only the LATEST Last Change entry (no stacking)
|
|
105
|
+
|
|
106
|
+
## Agent System
|
|
107
|
+
|
|
108
|
+
### Subagents (6)
|
|
109
|
+
|
|
110
|
+
| Agent | Purpose |
|
|
111
|
+
|-------|---------|
|
|
112
|
+
| **research-web** | Researches best practices before new features |
|
|
113
|
+
| **documenter** | Maps files to domains, creates/updates domain docs |
|
|
114
|
+
| **domain-updater** | Records problems, solutions, learnings |
|
|
115
|
+
| **commit-manager** | Manages git commits, conventional format |
|
|
116
|
+
| **claude-md-compactor** | Compacts CLAUDE.md when > 40k chars |
|
|
117
|
+
| **tester** | Creates tests with Vitest/Playwright |
|
|
118
|
+
|
|
119
|
+
### Skills
|
|
120
|
+
|
|
121
|
+
| Category | Skills |
|
|
122
|
+
|----------|--------|
|
|
123
|
+
| **Development** | typescript-strict, react-patterns, nextjs-app-router, zod-validation, shadcn-ui, tailwind-patterns |
|
|
124
|
+
| **Quality** | quality-gate, security-scan, test-coverage, final-check |
|
|
125
|
+
| **Infrastructure** | docker-patterns, git-workflow, performance-patterns, debugging-patterns |
|
|
126
|
+
| **Documentation** | codebase-knowledge, docs-tracker, research-cache, ui-ux-audit |
|
|
127
|
+
|
|
128
|
+
## Critical Rules
|
|
129
|
+
|
|
130
|
+
- **TypeScript strict mode** — `strict: true` in tsconfig.json
|
|
131
|
+
- **Bracket notation** for env vars: `process.env['VARIABLE']`
|
|
132
|
+
- **Zod validation** for ALL external input (forms, API, env)
|
|
133
|
+
- **Server Components by default** — push `'use client'` to leaf components
|
|
134
|
+
- **Parallel data fetching** — `Promise.all()` over waterfall
|
|
135
|
+
- **Type everything** — no `any`, use `unknown` for truly unknown data
|
|
136
|
+
- **Loading states** — every data page must have `loading.tsx`
|
|
137
|
+
- **Error boundaries** — every route should handle errors gracefully
|
|
138
|
+
- **Conventional commits** — `feat:`, `fix:`, `refactor:`, `docs:`, `chore:`
|
|
139
|
+
|
|
140
|
+
### Environment Variables Security (MANDATORY)
|
|
141
|
+
|
|
142
|
+
> **NEVER expose secrets to the browser.** `NEXT_PUBLIC_*` vars are embedded in the JS bundle and visible to anyone.
|
|
143
|
+
|
|
144
|
+
| Prefix | Where it runs | Safe for |
|
|
145
|
+
|--------|--------------|----------|
|
|
146
|
+
| `NEXT_PUBLIC_*` | Browser + Server | Public URLs, analytics IDs, Stripe **publishable** key (`pk_`) |
|
|
147
|
+
| No prefix | Server ONLY | API keys, secrets, tokens, database URLs, private keys |
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
// .env.local
|
|
151
|
+
OPENAI_KEY=sk-abc123 // Server only — SAFE
|
|
152
|
+
STRIPE_SECRET_KEY=sk_live_abc // Server only — SAFE
|
|
153
|
+
NEXT_PUBLIC_APP_URL=https://myapp.com // Public — OK (no secret)
|
|
154
|
+
NEXT_PUBLIC_STRIPE_KEY=pk_live_abc // Public — OK (publishable key)
|
|
155
|
+
|
|
156
|
+
// NEXT_PUBLIC_OPENAI_KEY=sk-abc123 // FORBIDDEN — exposed in browser bundle!
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### API Proxy Pattern (MANDATORY for external APIs)
|
|
160
|
+
|
|
161
|
+
> **ALL calls to external APIs with secrets MUST go through server-side Route Handlers or Server Actions. NEVER call external APIs directly from client components.**
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// app/api/chat/route.ts — Server-side proxy (token NEVER leaves server)
|
|
165
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
166
|
+
|
|
167
|
+
export async function POST(req: NextRequest) {
|
|
168
|
+
const { message } = await req.json();
|
|
169
|
+
const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
170
|
+
headers: { Authorization: `Bearer ${process.env['OPENAI_KEY']}` },
|
|
171
|
+
method: 'POST',
|
|
172
|
+
body: JSON.stringify({ model: 'gpt-4', messages: [{ role: 'user', content: message }] }),
|
|
173
|
+
});
|
|
174
|
+
return NextResponse.json(await response.json());
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// components/chat.tsx — Client calls YOUR API, not the external one
|
|
178
|
+
'use client';
|
|
179
|
+
const response = await fetch('/api/chat', {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
body: JSON.stringify({ message }),
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Types Location
|
|
186
|
+
|
|
187
|
+
- **ALL** interfaces/types MUST be in `types/` folder
|
|
188
|
+
- **NEVER** define types in `src/` files
|
|
189
|
+
- **EXCEPTION:** Zod inferred types (`z.infer<typeof Schema>`)
|
|
190
|
+
|
|
191
|
+
### TypeScript Strict
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
process.env['VARIABLE']; // CORRECT (bracket notation)
|
|
195
|
+
source: 'listed' as const; // CORRECT (literal type)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Component Organization
|
|
199
|
+
|
|
200
|
+
| Question | Location |
|
|
201
|
+
|----------|----------|
|
|
202
|
+
| Used in ONE page only? | `app/[page]/_components/` |
|
|
203
|
+
| Used across 2+ features? | `components/shared/` |
|
|
204
|
+
| UI primitive (Button, Input)? | `components/ui/` |
|
|
205
|
+
| Layout element (Header)? | `components/layout/` |
|
|
206
|
+
|
|
207
|
+
| Lines | Action |
|
|
208
|
+
|-------|--------|
|
|
209
|
+
| < 200 | Keep in single file |
|
|
210
|
+
| 200-400 | Consider splitting |
|
|
211
|
+
| > 400 | **MUST split** into smaller components |
|
|
212
|
+
|
|
213
|
+
## FORBIDDEN
|
|
214
|
+
|
|
215
|
+
### Security (CRITICAL)
|
|
216
|
+
|
|
217
|
+
| Action | Reason |
|
|
218
|
+
|--------|--------|
|
|
219
|
+
| `NEXT_PUBLIC_` with API keys/secrets/tokens | Exposes credentials in browser JS bundle — use server-side proxy |
|
|
220
|
+
| Call external APIs from client components | Leaks tokens — route through `app/api/` Route Handlers |
|
|
221
|
+
| `process.env['SECRET']` in `'use client'` files | Only `NEXT_PUBLIC_*` vars reach the browser — use Server Actions |
|
|
222
|
+
| Hardcode API keys in source code | Use `.env.local` + server-side access only |
|
|
223
|
+
| Commit `.env.local` / `.env` to git | Add to `.gitignore` — use `.env.example` with empty values |
|
|
224
|
+
|
|
225
|
+
### Code Quality
|
|
226
|
+
|
|
227
|
+
| Action | Reason |
|
|
228
|
+
|--------|--------|
|
|
229
|
+
| `any` type | Defeats strict mode — use `unknown` |
|
|
230
|
+
| Skip typecheck | TypeScript errors become runtime bugs |
|
|
231
|
+
| Relative imports (shared) | Breaks when files move — use `@/` alias |
|
|
232
|
+
| Define types in `src/` | Must be in `types/` folder |
|
|
233
|
+
| `'use client'` at top-level layouts | Breaks server rendering, push to leaves |
|
|
234
|
+
| Waterfall data fetching | Use `Promise.all()` for parallel |
|
|
235
|
+
| Skip `loading.tsx` on data pages | Flash of empty content |
|
|
236
|
+
| Files > 400 lines | MUST split into smaller components |
|
|
237
|
+
| Wildcard icon imports | Use named: `import { X } from 'lucide-react'` |
|
|
238
|
+
| `var` keyword | Use `const` or `let` |
|
|
239
|
+
| Raw `console.log` in production | Use structured logging |
|
|
240
|
+
|
|
241
|
+
### Workflow
|
|
242
|
+
|
|
243
|
+
| Action | Reason |
|
|
244
|
+
|--------|--------|
|
|
245
|
+
| Commit directly to main | Create feature/fix branches |
|
|
246
|
+
| Skip research for new features | Leads to outdated patterns |
|
|
247
|
+
| Skip todo list creation | Loses track of tasks |
|
|
248
|
+
| Skip documenter agent | Documentation is mandatory |
|
|
249
|
+
| Skip domain documentation | MUST update domains/*.md |
|
|
250
|
+
| Stack Last Change entries | Keep only the LATEST |
|
|
251
|
+
| Use MUI/Chakra | Use shadcn/ui + Radix |
|
|
252
|
+
| Skip CLAUDE.md update | MUST update after implementations |
|
|
253
|
+
|
|
254
|
+
## UI Architecture
|
|
255
|
+
|
|
256
|
+
> Web apps MUST have **separate UIs** for each platform, NOT just "responsive design".
|
|
257
|
+
|
|
258
|
+
| Platform | Layout |
|
|
259
|
+
|----------|--------|
|
|
260
|
+
| Mobile (375px) | Full-screen modals, bottom nav, touch-first |
|
|
261
|
+
| Tablet (768px) | Condensed dropdowns, hybrid nav |
|
|
262
|
+
| Desktop (1280px+) | Sidebar left, top navbar with search |
|
|
263
|
+
|
|
264
|
+
## Quality Gates
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
bun run typecheck # MUST pass
|
|
268
|
+
bun run lint # MUST pass
|
|
269
|
+
bun run test # MUST pass
|
|
270
|
+
bun run build # MUST pass
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## Domain Documentation
|
|
274
|
+
|
|
275
|
+
> Domain docs prevent Claude from re-exploring the codebase every session.
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
.claude/skills/codebase-knowledge/domains/
|
|
279
|
+
├── authentication.md
|
|
280
|
+
├── api.md
|
|
281
|
+
├── database.md
|
|
282
|
+
├── ui-components.md
|
|
283
|
+
└── [domain-name].md
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Each domain file tracks: Files, Connections, Recent Commits, Attention Points, Problems & Solutions.
|
|
287
|
+
|
|
288
|
+
## Commit Format
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
[type]: [description]
|
|
292
|
+
|
|
293
|
+
- Detail 1
|
|
294
|
+
- Detail 2
|
|
295
|
+
|
|
296
|
+
Generated with Claude Code
|
|
297
|
+
Co-Authored-By: Claude <noreply@anthropic.com>
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Types: `feat`, `fix`, `refactor`, `docs`, `chore`
|
|
301
|
+
|
|
302
|
+
## NRY (Never Repeat Yourself)
|
|
303
|
+
|
|
304
|
+
- Multi-line bash with `\` continuations (breaks permissions)
|
|
305
|
+
- Relative paths in permission patterns
|
|
306
|
+
- Using bash for file operations (use Read/Write/Edit tools)
|
|
307
|
+
- Ignoring context size (use `/compact`)
|
|
308
|
+
|
|
309
|
+
## Configuration
|
|
310
|
+
|
|
311
|
+
Project settings in `.claude/config/`:
|
|
312
|
+
|
|
313
|
+
- `active-project.json` — Stack, framework, database, skills
|
|
314
|
+
- `domain-mapping.json` — File-to-domain mapping
|
|
315
|
+
- `quality-gates.json` — Quality check commands
|
|
316
|
+
- `testing-config.json` — Test framework config
|
|
317
|
+
- `security-rules.json` — Security audit rules
|
|
318
|
+
- `standards-review.json` — Imported project standards
|
|
319
|
+
|
|
320
|
+
## Setup by start-vibing-stacks
|
|
321
|
+
|
|
322
|
+
This project was set up with `npx start-vibing-stacks`.
|
|
323
|
+
For updates: `npx start-vibing-stacks --force`
|