@claude-code-mastery/starter-kit 1.0.0 → 1.2.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/.claude/.starter-kit/profiles/clean.md +7 -1
- package/.claude/.starter-kit/profiles/go.md +1 -1
- package/.claude/.starter-kit/profiles/node.md +13 -4
- package/.claude/.starter-kit/profiles/python.md +1 -1
- package/.claude/commands/show-user-guide.md +22 -20
- package/.dockerignore +39 -0
- package/.env.example +74 -0
- package/.gitignore +70 -0
- package/CLAUDE.md +838 -0
- package/README.md +111 -2073
- package/README.npm.md +190 -0
- package/bin/cli.js +22 -0
- package/package.json +14 -4
- package/playwright.config.ts +79 -0
- package/scripts/.gitkeep +0 -0
- package/scripts/build-content.ts +108 -0
- package/scripts/content/html-template.ts +144 -0
- package/scripts/content/markdown-processor.ts +261 -0
- package/scripts/content/seo-generator.ts +42 -0
- package/scripts/content/sidebar-generator.ts +71 -0
- package/scripts/content/types.ts +72 -0
- package/scripts/content.config.json +49 -0
- package/scripts/db-query.ts +143 -0
- package/scripts/queries/example-count-docs.ts +25 -0
- package/scripts/queries/example-find-user.ts +32 -0
- package/scripts/scaffold-clean.sh +591 -0
- package/scripts/scaffold-default.sh +1251 -0
- package/tsconfig.json +25 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,1251 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scaffold-default.sh — Fast batch scaffold for default profile projects
|
|
3
|
+
# Default profile: fullstack Next.js + StrictDB + Tailwind + Docker + SEO + CI
|
|
4
|
+
#
|
|
5
|
+
# Usage: bash scripts/scaffold-default.sh <project-path> <project-name> <starter-kit-root>
|
|
6
|
+
#
|
|
7
|
+
# Creates a complete default-profile project with progress indicators.
|
|
8
|
+
# This replaces ~40+ individual tool calls with a single script execution.
|
|
9
|
+
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
# ── Arguments ──────────────────────────────────────────────────────────────────
|
|
13
|
+
PROJECT_PATH="$1"
|
|
14
|
+
PROJECT_NAME="$2"
|
|
15
|
+
STARTER_KIT="$3"
|
|
16
|
+
REGISTRY="${HOME}/.claude/starter-kit-projects.json"
|
|
17
|
+
|
|
18
|
+
# ── Global install detection ───────────────────────────────────────────────────
|
|
19
|
+
if [ -f "${HOME}/.claude/starter-kit-source-path" ]; then
|
|
20
|
+
GLOBAL_INSTALLED=true
|
|
21
|
+
CLAUDE_DIR="$STARTER_KIT"
|
|
22
|
+
else
|
|
23
|
+
GLOBAL_INSTALLED=false
|
|
24
|
+
CLAUDE_DIR="$STARTER_KIT/.claude"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
# ── Validation ─────────────────────────────────────────────────────────────────
|
|
28
|
+
if [ -d "$PROJECT_PATH" ]; then
|
|
29
|
+
echo "ERROR: Directory already exists: $PROJECT_PATH"
|
|
30
|
+
echo "Remove it first or choose a different name."
|
|
31
|
+
exit 1
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
if [ ! -d "$CLAUDE_DIR/commands" ]; then
|
|
35
|
+
echo "ERROR: Starter kit not found at: $STARTER_KIT"
|
|
36
|
+
exit 1
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# ── Progress Tracking ──────────────────────────────────────────────────────────
|
|
40
|
+
TOTAL_STEPS=15
|
|
41
|
+
CURRENT=0
|
|
42
|
+
START_NS=$(date +%s%N)
|
|
43
|
+
|
|
44
|
+
progress() {
|
|
45
|
+
CURRENT=$((CURRENT + 1))
|
|
46
|
+
local pct=$((CURRENT * 100 / TOTAL_STEPS))
|
|
47
|
+
local now_ns=$(date +%s%N)
|
|
48
|
+
local elapsed_ms=$(( (now_ns - START_NS) / 1000000 ))
|
|
49
|
+
|
|
50
|
+
# Progress bar (20 chars)
|
|
51
|
+
local filled=$((pct / 5))
|
|
52
|
+
local empty=$((20 - filled))
|
|
53
|
+
local bar=""
|
|
54
|
+
for ((i=0; i<filled; i++)); do bar+="\xe2\x96\x88"; done
|
|
55
|
+
for ((i=0; i<empty; i++)); do bar+="\xe2\x96\x91"; done
|
|
56
|
+
|
|
57
|
+
# Estimated time remaining
|
|
58
|
+
local eta=""
|
|
59
|
+
if [ "$CURRENT" -eq "$TOTAL_STEPS" ]; then
|
|
60
|
+
eta="Done!"
|
|
61
|
+
elif [ "$elapsed_ms" -gt 0 ] && [ "$CURRENT" -gt 0 ]; then
|
|
62
|
+
local ms_per_step=$((elapsed_ms / CURRENT))
|
|
63
|
+
local remaining_ms=$(( (TOTAL_STEPS - CURRENT) * ms_per_step ))
|
|
64
|
+
if [ "$remaining_ms" -ge 1000 ]; then
|
|
65
|
+
local remaining_s=$((remaining_ms / 1000))
|
|
66
|
+
local remaining_frac=$(( (remaining_ms % 1000) / 100 ))
|
|
67
|
+
eta="~${remaining_s}.${remaining_frac}s remaining"
|
|
68
|
+
else
|
|
69
|
+
eta="~${remaining_ms}ms remaining"
|
|
70
|
+
fi
|
|
71
|
+
else
|
|
72
|
+
eta="estimating..."
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
printf "[%d/%d] %3d%% $(echo -e "$bar") %-40s %s\n" \
|
|
76
|
+
"$CURRENT" "$TOTAL_STEPS" "$pct" "$1" "$eta"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# ── Header ─────────────────────────────────────────────────────────────────────
|
|
80
|
+
echo ""
|
|
81
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
82
|
+
echo " NEW PROJECT: $PROJECT_NAME (default profile)"
|
|
83
|
+
echo " Next.js + StrictDB + Tailwind + Docker + SEO + CI"
|
|
84
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
85
|
+
echo ""
|
|
86
|
+
|
|
87
|
+
# ── Step 1: Create directory structure ─────────────────────────────────────────
|
|
88
|
+
progress "Creating directory structure..."
|
|
89
|
+
if [ "$GLOBAL_INSTALLED" = "false" ]; then
|
|
90
|
+
mkdir -p "$PROJECT_PATH"/.claude/{commands,skills,agents,hooks}
|
|
91
|
+
fi
|
|
92
|
+
mkdir -p "$PROJECT_PATH"/project-docs
|
|
93
|
+
mkdir -p "$PROJECT_PATH"/src/app/api/v1/health
|
|
94
|
+
mkdir -p "$PROJECT_PATH"/src/handlers
|
|
95
|
+
mkdir -p "$PROJECT_PATH"/src/adapters
|
|
96
|
+
mkdir -p "$PROJECT_PATH"/src/types
|
|
97
|
+
mkdir -p "$PROJECT_PATH"/tests/{unit,integration,e2e}
|
|
98
|
+
mkdir -p "$PROJECT_PATH"/scripts/queries
|
|
99
|
+
mkdir -p "$PROJECT_PATH"/content
|
|
100
|
+
mkdir -p "$PROJECT_PATH"/.github/workflows
|
|
101
|
+
mkdir -p "$PROJECT_PATH"/public
|
|
102
|
+
|
|
103
|
+
# ── Steps 2-4: Copy Claude infrastructure (clone users only) ──────────────────
|
|
104
|
+
# npm users already have commands/skills/hooks installed globally in ~/.claude/
|
|
105
|
+
if [ "$GLOBAL_INSTALLED" = "false" ]; then
|
|
106
|
+
progress "Copying 16 project commands..."
|
|
107
|
+
for cmd in architecture commit create-api create-e2e diagram help \
|
|
108
|
+
optimize-docker progress refactor review security-check \
|
|
109
|
+
setup show-user-guide test-plan what-is-my-ai-doing worktree; do
|
|
110
|
+
cp "$CLAUDE_DIR/commands/${cmd}.md" "$PROJECT_PATH/.claude/commands/"
|
|
111
|
+
done
|
|
112
|
+
|
|
113
|
+
progress "Copying skills, agents, 10 hooks..."
|
|
114
|
+
cp -r "$CLAUDE_DIR/skills/code-review" "$PROJECT_PATH/.claude/skills/"
|
|
115
|
+
cp -r "$CLAUDE_DIR/skills/create-service" "$PROJECT_PATH/.claude/skills/"
|
|
116
|
+
cp "$CLAUDE_DIR/agents/code-reviewer.md" "$PROJECT_PATH/.claude/agents/"
|
|
117
|
+
cp "$CLAUDE_DIR/agents/test-writer.md" "$PROJECT_PATH/.claude/agents/"
|
|
118
|
+
for hook in block-dangerous-bash.py check-file-length.py lint-on-save.sh \
|
|
119
|
+
verify-no-secrets.sh check-rybbit.sh check-branch.sh \
|
|
120
|
+
check-ports.sh check-e2e.sh check-rulecatch.sh check-env-sync.sh; do
|
|
121
|
+
cp "$CLAUDE_DIR/hooks/${hook}" "$PROJECT_PATH/.claude/hooks/"
|
|
122
|
+
done
|
|
123
|
+
chmod +x "$PROJECT_PATH/.claude/hooks/"*.sh 2>/dev/null
|
|
124
|
+
chmod +x "$PROJECT_PATH/.claude/hooks/"*.py 2>/dev/null
|
|
125
|
+
|
|
126
|
+
progress "Writing settings.json (10 hooks)..."
|
|
127
|
+
cat > "$PROJECT_PATH/.claude/settings.json" << 'SETTINGS_EOF'
|
|
128
|
+
{
|
|
129
|
+
"hooks": {
|
|
130
|
+
"PreToolUse": [
|
|
131
|
+
{
|
|
132
|
+
"matcher": "Bash",
|
|
133
|
+
"hooks": [
|
|
134
|
+
{
|
|
135
|
+
"type": "command",
|
|
136
|
+
"command": "python3 .claude/hooks/block-dangerous-bash.py"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"type": "command",
|
|
140
|
+
"command": "bash .claude/hooks/check-rybbit.sh"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"type": "command",
|
|
144
|
+
"command": "bash .claude/hooks/check-branch.sh"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"type": "command",
|
|
148
|
+
"command": "bash .claude/hooks/check-ports.sh"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"type": "command",
|
|
152
|
+
"command": "bash .claude/hooks/check-e2e.sh"
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
],
|
|
157
|
+
"PostToolUse": [
|
|
158
|
+
{
|
|
159
|
+
"matcher": "Write|Edit",
|
|
160
|
+
"hooks": [
|
|
161
|
+
{
|
|
162
|
+
"type": "command",
|
|
163
|
+
"command": "python3 .claude/hooks/check-file-length.py"
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"type": "command",
|
|
167
|
+
"command": "bash .claude/hooks/lint-on-save.sh"
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
"Stop": [
|
|
173
|
+
{
|
|
174
|
+
"hooks": [
|
|
175
|
+
{
|
|
176
|
+
"type": "command",
|
|
177
|
+
"command": "bash .claude/hooks/verify-no-secrets.sh"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"type": "command",
|
|
181
|
+
"command": "bash .claude/hooks/check-rulecatch.sh"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"type": "command",
|
|
185
|
+
"command": "bash .claude/hooks/check-env-sync.sh"
|
|
186
|
+
}
|
|
187
|
+
]
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
SETTINGS_EOF
|
|
193
|
+
else
|
|
194
|
+
progress "Skipping local .claude/ copy (commands/skills/hooks live globally)"
|
|
195
|
+
progress "Skipping local .claude/ copy (commands/skills/hooks live globally)"
|
|
196
|
+
progress "Skipping local .claude/ copy (commands/skills/hooks live globally)"
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
# ── Step 4b: Create features.json (populated manifest) ────────────────────────
|
|
200
|
+
CREATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
201
|
+
cat > "$PROJECT_PATH/.claude/features.json" << FEATURES_EOF
|
|
202
|
+
{
|
|
203
|
+
"schemaVersion": 1,
|
|
204
|
+
"installedBy": "claude-code-mastery-starter-kit",
|
|
205
|
+
"language": "node",
|
|
206
|
+
"features": {
|
|
207
|
+
"mongo": {
|
|
208
|
+
"version": "1.0.0",
|
|
209
|
+
"installedAt": "${CREATED_AT}",
|
|
210
|
+
"updatedAt": null,
|
|
211
|
+
"files": [
|
|
212
|
+
"scripts/db-query.ts",
|
|
213
|
+
"scripts/queries/example-find-user.ts",
|
|
214
|
+
"scripts/queries/example-count-docs.ts"
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
"vitest": {
|
|
218
|
+
"version": "1.0.0",
|
|
219
|
+
"installedAt": "${CREATED_AT}",
|
|
220
|
+
"updatedAt": null,
|
|
221
|
+
"files": [
|
|
222
|
+
"vitest.config.ts"
|
|
223
|
+
]
|
|
224
|
+
},
|
|
225
|
+
"playwright": {
|
|
226
|
+
"version": "1.0.0",
|
|
227
|
+
"installedAt": "${CREATED_AT}",
|
|
228
|
+
"updatedAt": null,
|
|
229
|
+
"files": [
|
|
230
|
+
"playwright.config.ts"
|
|
231
|
+
]
|
|
232
|
+
},
|
|
233
|
+
"docker": {
|
|
234
|
+
"version": "1.0.0",
|
|
235
|
+
"installedAt": "${CREATED_AT}",
|
|
236
|
+
"updatedAt": null,
|
|
237
|
+
"files": [
|
|
238
|
+
"Dockerfile"
|
|
239
|
+
]
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
FEATURES_EOF
|
|
244
|
+
|
|
245
|
+
# ── Step 5: Copy query system ─────────────────────────────────────────────────
|
|
246
|
+
progress "Copying StrictDB query system..."
|
|
247
|
+
cp "$STARTER_KIT/scripts/db-query.ts" "$PROJECT_PATH/scripts/db-query.ts"
|
|
248
|
+
cp "$STARTER_KIT/scripts/queries/example-find-user.ts" "$PROJECT_PATH/scripts/queries/"
|
|
249
|
+
cp "$STARTER_KIT/scripts/queries/example-count-docs.ts" "$PROJECT_PATH/scripts/queries/"
|
|
250
|
+
|
|
251
|
+
# ── Step 6: Create Next.js app files ──────────────────────────────────────────
|
|
252
|
+
progress "Creating Next.js app structure..."
|
|
253
|
+
|
|
254
|
+
# src/app/layout.tsx — SEO + Rybbit analytics
|
|
255
|
+
cat > "$PROJECT_PATH/src/app/layout.tsx" << 'LAYOUT_EOF'
|
|
256
|
+
import type { Metadata } from 'next';
|
|
257
|
+
import './globals.css';
|
|
258
|
+
|
|
259
|
+
export const metadata: Metadata = {
|
|
260
|
+
title: {
|
|
261
|
+
default: 'My App',
|
|
262
|
+
template: '%s — My App',
|
|
263
|
+
},
|
|
264
|
+
description: 'Built with Claude Code Mastery Starter Kit',
|
|
265
|
+
robots: { index: true, follow: true },
|
|
266
|
+
openGraph: {
|
|
267
|
+
type: 'website',
|
|
268
|
+
title: 'My App',
|
|
269
|
+
description: 'Built with Claude Code Mastery Starter Kit',
|
|
270
|
+
siteName: 'My App',
|
|
271
|
+
},
|
|
272
|
+
twitter: {
|
|
273
|
+
card: 'summary_large_image',
|
|
274
|
+
title: 'My App',
|
|
275
|
+
description: 'Built with Claude Code Mastery Starter Kit',
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export default function RootLayout({
|
|
280
|
+
children,
|
|
281
|
+
}: {
|
|
282
|
+
children: React.ReactNode;
|
|
283
|
+
}) {
|
|
284
|
+
return (
|
|
285
|
+
<html lang="en">
|
|
286
|
+
<head>
|
|
287
|
+
{process.env.NEXT_PUBLIC_RYBBIT_SITE_ID && (
|
|
288
|
+
<script
|
|
289
|
+
src={`${process.env.NEXT_PUBLIC_RYBBIT_URL || 'https://app.rybbit.io'}/api/script.js`}
|
|
290
|
+
data-site-id={process.env.NEXT_PUBLIC_RYBBIT_SITE_ID}
|
|
291
|
+
defer
|
|
292
|
+
/>
|
|
293
|
+
)}
|
|
294
|
+
</head>
|
|
295
|
+
<body>{children}</body>
|
|
296
|
+
</html>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
LAYOUT_EOF
|
|
300
|
+
|
|
301
|
+
# src/app/page.tsx
|
|
302
|
+
cat > "$PROJECT_PATH/src/app/page.tsx" << 'PAGE_EOF'
|
|
303
|
+
export default function Home() {
|
|
304
|
+
return (
|
|
305
|
+
<main className="flex min-h-screen flex-col items-center justify-center p-8">
|
|
306
|
+
<h1 className="text-4xl font-bold mb-4">Welcome</h1>
|
|
307
|
+
<p className="text-lg text-gray-600">
|
|
308
|
+
Your project is ready. Start building.
|
|
309
|
+
</p>
|
|
310
|
+
</main>
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
PAGE_EOF
|
|
314
|
+
|
|
315
|
+
# src/app/globals.css
|
|
316
|
+
cat > "$PROJECT_PATH/src/app/globals.css" << 'CSS_EOF'
|
|
317
|
+
@tailwind base;
|
|
318
|
+
@tailwind components;
|
|
319
|
+
@tailwind utilities;
|
|
320
|
+
CSS_EOF
|
|
321
|
+
|
|
322
|
+
# src/app/api/v1/health/route.ts
|
|
323
|
+
cat > "$PROJECT_PATH/src/app/api/v1/health/route.ts" << 'HEALTH_EOF'
|
|
324
|
+
import { NextResponse } from 'next/server';
|
|
325
|
+
|
|
326
|
+
export async function GET() {
|
|
327
|
+
return NextResponse.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
328
|
+
}
|
|
329
|
+
HEALTH_EOF
|
|
330
|
+
|
|
331
|
+
# src/instrumentation.ts — process signal handlers for Next.js
|
|
332
|
+
cat > "$PROJECT_PATH/src/instrumentation.ts" << 'INSTRUMENT_EOF'
|
|
333
|
+
export async function register() {
|
|
334
|
+
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
335
|
+
const { StrictDB } = await import('strictdb');
|
|
336
|
+
|
|
337
|
+
// Get or create the shared StrictDB instance
|
|
338
|
+
const db = await StrictDB.create({ uri: process.env.STRICTDB_URI! });
|
|
339
|
+
|
|
340
|
+
process.on('SIGTERM', () => db.gracefulShutdown(0));
|
|
341
|
+
process.on('SIGINT', () => db.gracefulShutdown(0));
|
|
342
|
+
process.on('uncaughtException', (err) => {
|
|
343
|
+
console.error('Uncaught Exception:', err);
|
|
344
|
+
db.gracefulShutdown(1);
|
|
345
|
+
});
|
|
346
|
+
process.on('unhandledRejection', (reason) => {
|
|
347
|
+
console.error('Unhandled Rejection:', reason);
|
|
348
|
+
db.gracefulShutdown(1);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
INSTRUMENT_EOF
|
|
353
|
+
|
|
354
|
+
# ── Step 7: Create config files (tsconfig, next, tailwind, postcss) ───────────
|
|
355
|
+
progress "Creating TypeScript + Next.js + Tailwind configs..."
|
|
356
|
+
|
|
357
|
+
cat > "$PROJECT_PATH/tsconfig.json" << 'TSCONFIG_EOF'
|
|
358
|
+
{
|
|
359
|
+
"compilerOptions": {
|
|
360
|
+
"target": "ES2022",
|
|
361
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
362
|
+
"allowJs": true,
|
|
363
|
+
"skipLibCheck": true,
|
|
364
|
+
"strict": true,
|
|
365
|
+
"noEmit": true,
|
|
366
|
+
"noUncheckedIndexedAccess": true,
|
|
367
|
+
"noImplicitOverride": true,
|
|
368
|
+
"esModuleInterop": true,
|
|
369
|
+
"module": "esnext",
|
|
370
|
+
"moduleResolution": "bundler",
|
|
371
|
+
"resolveJsonModule": true,
|
|
372
|
+
"isolatedModules": true,
|
|
373
|
+
"jsx": "preserve",
|
|
374
|
+
"incremental": true,
|
|
375
|
+
"plugins": [{ "name": "next" }],
|
|
376
|
+
"paths": {
|
|
377
|
+
"@/*": ["./src/*"]
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
381
|
+
"exclude": ["node_modules"]
|
|
382
|
+
}
|
|
383
|
+
TSCONFIG_EOF
|
|
384
|
+
|
|
385
|
+
cat > "$PROJECT_PATH/next.config.ts" << 'NEXTCONFIG_EOF'
|
|
386
|
+
import type { NextConfig } from 'next';
|
|
387
|
+
|
|
388
|
+
const nextConfig: NextConfig = {
|
|
389
|
+
reactStrictMode: true,
|
|
390
|
+
output: 'standalone',
|
|
391
|
+
images: {
|
|
392
|
+
formats: ['image/webp'],
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
export default nextConfig;
|
|
397
|
+
NEXTCONFIG_EOF
|
|
398
|
+
|
|
399
|
+
cat > "$PROJECT_PATH/tailwind.config.ts" << 'TAILWIND_EOF'
|
|
400
|
+
import type { Config } from 'tailwindcss';
|
|
401
|
+
|
|
402
|
+
const config: Config = {
|
|
403
|
+
content: [
|
|
404
|
+
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
405
|
+
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
406
|
+
],
|
|
407
|
+
theme: {
|
|
408
|
+
extend: {},
|
|
409
|
+
},
|
|
410
|
+
plugins: [],
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
export default config;
|
|
414
|
+
TAILWIND_EOF
|
|
415
|
+
|
|
416
|
+
cat > "$PROJECT_PATH/postcss.config.mjs" << 'POSTCSS_EOF'
|
|
417
|
+
/** @type {import('postcss-load-config').Config} */
|
|
418
|
+
const config = {
|
|
419
|
+
plugins: {
|
|
420
|
+
tailwindcss: {},
|
|
421
|
+
autoprefixer: {},
|
|
422
|
+
},
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
export default config;
|
|
426
|
+
POSTCSS_EOF
|
|
427
|
+
|
|
428
|
+
# ── Step 8: Create Vitest + Playwright configs ────────────────────────────────
|
|
429
|
+
progress "Creating Vitest + Playwright configs..."
|
|
430
|
+
|
|
431
|
+
cat > "$PROJECT_PATH/vitest.config.ts" << 'VITEST_EOF'
|
|
432
|
+
import { defineConfig } from 'vitest/config';
|
|
433
|
+
import path from 'path';
|
|
434
|
+
|
|
435
|
+
export default defineConfig({
|
|
436
|
+
test: {
|
|
437
|
+
globals: true,
|
|
438
|
+
environment: 'node',
|
|
439
|
+
include: ['tests/unit/**/*.test.ts', 'tests/integration/**/*.test.ts'],
|
|
440
|
+
exclude: ['tests/e2e/**/*'],
|
|
441
|
+
},
|
|
442
|
+
resolve: {
|
|
443
|
+
alias: {
|
|
444
|
+
'@': path.resolve(__dirname, './src'),
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
});
|
|
448
|
+
VITEST_EOF
|
|
449
|
+
|
|
450
|
+
cat > "$PROJECT_PATH/playwright.config.ts" << 'PLAYWRIGHT_EOF'
|
|
451
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
452
|
+
|
|
453
|
+
export default defineConfig({
|
|
454
|
+
testDir: './tests/e2e',
|
|
455
|
+
fullyParallel: true,
|
|
456
|
+
forbidOnly: !!process.env.CI,
|
|
457
|
+
retries: process.env.CI ? 2 : 0,
|
|
458
|
+
reporter: [['html'], ['list']],
|
|
459
|
+
use: {
|
|
460
|
+
baseURL: 'http://localhost:4000',
|
|
461
|
+
trace: 'on-first-retry',
|
|
462
|
+
screenshot: 'only-on-failure',
|
|
463
|
+
},
|
|
464
|
+
projects: [
|
|
465
|
+
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
466
|
+
],
|
|
467
|
+
webServer: [
|
|
468
|
+
{
|
|
469
|
+
command: 'pnpm dev:test:website',
|
|
470
|
+
port: 4000,
|
|
471
|
+
reuseExistingServer: !process.env.CI,
|
|
472
|
+
timeout: 30_000,
|
|
473
|
+
},
|
|
474
|
+
],
|
|
475
|
+
});
|
|
476
|
+
PLAYWRIGHT_EOF
|
|
477
|
+
|
|
478
|
+
# E2E example test
|
|
479
|
+
cat > "$PROJECT_PATH/tests/e2e/home.spec.ts" << 'E2E_EOF'
|
|
480
|
+
import { test, expect } from '@playwright/test';
|
|
481
|
+
|
|
482
|
+
test.describe('Home Page', () => {
|
|
483
|
+
test('loads successfully with correct content', async ({ page }) => {
|
|
484
|
+
await page.goto('/');
|
|
485
|
+
await expect(page).toHaveURL('/');
|
|
486
|
+
await expect(page.locator('h1')).toBeVisible();
|
|
487
|
+
await expect(page.locator('h1')).toContainText('Welcome');
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
test('has correct page title and metadata', async ({ page }) => {
|
|
491
|
+
await page.goto('/');
|
|
492
|
+
await expect(page).toHaveTitle(/My App/);
|
|
493
|
+
const viewport = page.viewportSize();
|
|
494
|
+
expect(viewport).toBeTruthy();
|
|
495
|
+
});
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test.describe('Health API', () => {
|
|
499
|
+
test('returns ok status', async ({ request }) => {
|
|
500
|
+
const response = await request.get('/api/v1/health');
|
|
501
|
+
expect(response.status()).toBe(200);
|
|
502
|
+
const body = await response.json();
|
|
503
|
+
expect(body.status).toBe('ok');
|
|
504
|
+
expect(body.timestamp).toBeTruthy();
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
E2E_EOF
|
|
508
|
+
|
|
509
|
+
# Unit test example
|
|
510
|
+
cat > "$PROJECT_PATH/tests/unit/example.test.ts" << 'UNIT_EOF'
|
|
511
|
+
import { describe, it, expect } from 'vitest';
|
|
512
|
+
|
|
513
|
+
describe('Example test', () => {
|
|
514
|
+
it('basic math works', () => {
|
|
515
|
+
expect(1 + 1).toBe(2);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
it('string operations work', () => {
|
|
519
|
+
const greeting = 'Hello, World!';
|
|
520
|
+
expect(greeting).toContain('Hello');
|
|
521
|
+
expect(greeting).toHaveLength(13);
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
UNIT_EOF
|
|
525
|
+
|
|
526
|
+
# ── Step 9: Create package.json ───────────────────────────────────────────────
|
|
527
|
+
progress "Creating package.json..."
|
|
528
|
+
|
|
529
|
+
cat > "$PROJECT_PATH/package.json" << PKGJSON_EOF
|
|
530
|
+
{
|
|
531
|
+
"name": "$PROJECT_NAME",
|
|
532
|
+
"version": "0.1.0",
|
|
533
|
+
"private": true,
|
|
534
|
+
"type": "module",
|
|
535
|
+
"scripts": {
|
|
536
|
+
"dev": "next dev -p 3000",
|
|
537
|
+
"dev:website": "next dev -p 3000",
|
|
538
|
+
"dev:api": "next dev -p 3001",
|
|
539
|
+
"dev:dashboard": "next dev -p 3002",
|
|
540
|
+
"dev:test:website": "PORT=4000 next dev -p 4000",
|
|
541
|
+
"dev:test:api": "PORT=4010 next dev -p 4010",
|
|
542
|
+
"build": "next build",
|
|
543
|
+
"start": "next start",
|
|
544
|
+
"typecheck": "tsc --noEmit",
|
|
545
|
+
"test": "pnpm test:unit && pnpm test:e2e",
|
|
546
|
+
"test:unit": "vitest run",
|
|
547
|
+
"test:unit:watch": "vitest",
|
|
548
|
+
"test:coverage": "vitest run --coverage",
|
|
549
|
+
"test:e2e": "pnpm test:kill-ports && playwright test",
|
|
550
|
+
"test:e2e:ui": "pnpm test:kill-ports && playwright test --ui",
|
|
551
|
+
"test:e2e:headed": "pnpm test:kill-ports && playwright test --headed",
|
|
552
|
+
"test:e2e:chromium": "pnpm test:kill-ports && playwright test --project=chromium",
|
|
553
|
+
"test:e2e:report": "playwright show-report",
|
|
554
|
+
"test:kill-ports": "lsof -ti:4000,4010,4020 | xargs kill -9 2>/dev/null || true",
|
|
555
|
+
"db:query": "tsx scripts/db-query.ts",
|
|
556
|
+
"db:query:list": "tsx scripts/db-query.ts --list",
|
|
557
|
+
"clean": "rm -rf .next coverage test-results playwright-report"
|
|
558
|
+
},
|
|
559
|
+
"dependencies": {
|
|
560
|
+
"strictdb": "^0.1.0",
|
|
561
|
+
"mongodb": "^6.5.0",
|
|
562
|
+
"next": "^15.0.0",
|
|
563
|
+
"react": "^19.0.0",
|
|
564
|
+
"react-dom": "^19.0.0"
|
|
565
|
+
},
|
|
566
|
+
"devDependencies": {
|
|
567
|
+
"@playwright/test": "^1.42.0",
|
|
568
|
+
"@types/node": "^20.0.0",
|
|
569
|
+
"@types/react": "^19.0.0",
|
|
570
|
+
"@types/react-dom": "^19.0.0",
|
|
571
|
+
"autoprefixer": "^10.4.0",
|
|
572
|
+
"postcss": "^8.4.0",
|
|
573
|
+
"tailwindcss": "^3.4.0",
|
|
574
|
+
"tsx": "^4.7.0",
|
|
575
|
+
"typescript": "^5.0.0",
|
|
576
|
+
"vitest": "^2.0.0"
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
PKGJSON_EOF
|
|
580
|
+
|
|
581
|
+
# ── Step 10: Create Dockerfile (multi-stage Next.js standalone) ────────────────
|
|
582
|
+
progress "Creating Dockerfile..."
|
|
583
|
+
|
|
584
|
+
cat > "$PROJECT_PATH/Dockerfile" << 'DOCKER_EOF'
|
|
585
|
+
# Stage 1: Dependencies
|
|
586
|
+
FROM node:20-alpine AS deps
|
|
587
|
+
WORKDIR /app
|
|
588
|
+
|
|
589
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
590
|
+
|
|
591
|
+
COPY package.json pnpm-lock.yaml* ./
|
|
592
|
+
RUN pnpm install --frozen-lockfile || pnpm install
|
|
593
|
+
|
|
594
|
+
# Stage 2: Builder
|
|
595
|
+
FROM node:20-alpine AS builder
|
|
596
|
+
WORKDIR /app
|
|
597
|
+
|
|
598
|
+
RUN corepack enable && corepack prepare pnpm@latest --activate
|
|
599
|
+
|
|
600
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
601
|
+
COPY . .
|
|
602
|
+
|
|
603
|
+
# Build args for Next.js public env vars (baked at build time)
|
|
604
|
+
ARG NEXT_PUBLIC_RYBBIT_SITE_ID
|
|
605
|
+
ARG NEXT_PUBLIC_RYBBIT_URL
|
|
606
|
+
ENV NEXT_PUBLIC_RYBBIT_SITE_ID=$NEXT_PUBLIC_RYBBIT_SITE_ID
|
|
607
|
+
ENV NEXT_PUBLIC_RYBBIT_URL=$NEXT_PUBLIC_RYBBIT_URL
|
|
608
|
+
|
|
609
|
+
RUN pnpm build
|
|
610
|
+
|
|
611
|
+
# Stage 3: Runner
|
|
612
|
+
FROM node:20-alpine AS runner
|
|
613
|
+
WORKDIR /app
|
|
614
|
+
ENV NODE_ENV=production
|
|
615
|
+
|
|
616
|
+
RUN addgroup --system --gid 1001 appgroup && \
|
|
617
|
+
adduser --system --uid 1001 appuser
|
|
618
|
+
|
|
619
|
+
COPY --from=builder --chown=appuser:appgroup /app/.next/standalone ./
|
|
620
|
+
COPY --from=builder --chown=appuser:appgroup /app/.next/static ./.next/static
|
|
621
|
+
COPY --from=builder --chown=appuser:appgroup /app/public ./public
|
|
622
|
+
|
|
623
|
+
USER appuser
|
|
624
|
+
EXPOSE 3000
|
|
625
|
+
ENV PORT=3000
|
|
626
|
+
ENV HOSTNAME="0.0.0.0"
|
|
627
|
+
|
|
628
|
+
CMD ["node", "server.js"]
|
|
629
|
+
DOCKER_EOF
|
|
630
|
+
|
|
631
|
+
# ── Step 11: Create CI workflow ────────────────────────────────────────────────
|
|
632
|
+
progress "Creating GitHub Actions CI..."
|
|
633
|
+
|
|
634
|
+
cat > "$PROJECT_PATH/.github/workflows/ci.yml" << 'CI_EOF'
|
|
635
|
+
name: CI
|
|
636
|
+
|
|
637
|
+
on:
|
|
638
|
+
push:
|
|
639
|
+
branches: [main]
|
|
640
|
+
pull_request:
|
|
641
|
+
branches: [main]
|
|
642
|
+
|
|
643
|
+
jobs:
|
|
644
|
+
test:
|
|
645
|
+
runs-on: ubuntu-latest
|
|
646
|
+
|
|
647
|
+
steps:
|
|
648
|
+
- uses: actions/checkout@v4
|
|
649
|
+
|
|
650
|
+
- uses: pnpm/action-setup@v2
|
|
651
|
+
with:
|
|
652
|
+
version: latest
|
|
653
|
+
|
|
654
|
+
- uses: actions/setup-node@v4
|
|
655
|
+
with:
|
|
656
|
+
node-version: 20
|
|
657
|
+
cache: pnpm
|
|
658
|
+
|
|
659
|
+
- run: pnpm install --frozen-lockfile
|
|
660
|
+
|
|
661
|
+
- name: Type check
|
|
662
|
+
run: pnpm typecheck
|
|
663
|
+
|
|
664
|
+
- name: Unit tests
|
|
665
|
+
run: pnpm test:unit
|
|
666
|
+
|
|
667
|
+
- name: Install Playwright browsers
|
|
668
|
+
run: pnpm exec playwright install --with-deps chromium
|
|
669
|
+
|
|
670
|
+
- name: E2E tests
|
|
671
|
+
run: pnpm test:e2e
|
|
672
|
+
|
|
673
|
+
- name: Upload test results
|
|
674
|
+
if: failure()
|
|
675
|
+
uses: actions/upload-artifact@v4
|
|
676
|
+
with:
|
|
677
|
+
name: test-results
|
|
678
|
+
path: |
|
|
679
|
+
playwright-report/
|
|
680
|
+
test-results/
|
|
681
|
+
CI_EOF
|
|
682
|
+
|
|
683
|
+
# ── Step 12: Create CLAUDE.md (comprehensive, all rules) ──────────────────────
|
|
684
|
+
progress "Creating CLAUDE.md (all rules)..."
|
|
685
|
+
|
|
686
|
+
cat > "$PROJECT_PATH/CLAUDE.md" << 'CLAUDEMD_EOF'
|
|
687
|
+
# CLAUDE.md — Project Instructions
|
|
688
|
+
|
|
689
|
+
---
|
|
690
|
+
|
|
691
|
+
## Quick Reference — Scripts
|
|
692
|
+
|
|
693
|
+
| Command | What it does |
|
|
694
|
+
|---------|-------------|
|
|
695
|
+
| `pnpm dev` | Start dev server on port 3000 |
|
|
696
|
+
| `pnpm build` | Build for production |
|
|
697
|
+
| `pnpm start` | Run production build |
|
|
698
|
+
| `pnpm typecheck` | TypeScript type-check only |
|
|
699
|
+
| **Testing** | |
|
|
700
|
+
| `pnpm test` | Run ALL tests (unit + E2E) |
|
|
701
|
+
| `pnpm test:unit` | Unit/integration tests (Vitest) |
|
|
702
|
+
| `pnpm test:unit:watch` | Unit tests in watch mode |
|
|
703
|
+
| `pnpm test:coverage` | Unit tests with coverage |
|
|
704
|
+
| `pnpm test:e2e` | E2E tests (kills test ports first) |
|
|
705
|
+
| `pnpm test:e2e:ui` | E2E with Playwright UI |
|
|
706
|
+
| `pnpm test:e2e:headed` | E2E with visible browser |
|
|
707
|
+
| `pnpm test:kill-ports` | Kill test ports (4000, 4010, 4020) |
|
|
708
|
+
| **Database** | |
|
|
709
|
+
| `pnpm db:query <name>` | Run a dev/test database query |
|
|
710
|
+
| `pnpm db:query:list` | List all registered queries |
|
|
711
|
+
|
|
712
|
+
---
|
|
713
|
+
|
|
714
|
+
## Critical Rules
|
|
715
|
+
|
|
716
|
+
### 0. NEVER Publish Sensitive Data
|
|
717
|
+
|
|
718
|
+
- NEVER commit passwords, API keys, tokens, or secrets to git/npm/docker
|
|
719
|
+
- NEVER commit `.env` files — ALWAYS verify `.env` is in `.gitignore`
|
|
720
|
+
- Before ANY commit: verify no secrets are included
|
|
721
|
+
- NEVER output secrets in suggestions, logs, or responses
|
|
722
|
+
|
|
723
|
+
### 1. TypeScript Always
|
|
724
|
+
|
|
725
|
+
- ALWAYS use TypeScript for new files (strict mode)
|
|
726
|
+
- NEVER use `any` unless absolutely necessary and documented why
|
|
727
|
+
- When editing JavaScript files, convert to TypeScript first
|
|
728
|
+
- Types are specs — they tell you what functions accept and return
|
|
729
|
+
|
|
730
|
+
### 2. API Versioning
|
|
731
|
+
|
|
732
|
+
```
|
|
733
|
+
CORRECT: /api/v1/users
|
|
734
|
+
WRONG: /api/users
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
Every API endpoint MUST use `/api/v1/` prefix. No exceptions.
|
|
738
|
+
|
|
739
|
+
### 3. Database Access — StrictDB
|
|
740
|
+
|
|
741
|
+
**ALL database access uses StrictDB directly. No exceptions.**
|
|
742
|
+
|
|
743
|
+
- Install `strictdb` + your driver, use `StrictDB.create()` at app startup
|
|
744
|
+
- NEVER import native database drivers (`mongodb`, `pg`, etc.) directly
|
|
745
|
+
- Share a single StrictDB instance across the application
|
|
746
|
+
- All query inputs are automatically sanitized against injection
|
|
747
|
+
|
|
748
|
+
**Test queries go through `scripts/db-query.ts`:**
|
|
749
|
+
1. Create a query file in `scripts/queries/<name>.ts`
|
|
750
|
+
2. Register it in `scripts/db-query.ts`
|
|
751
|
+
3. NEVER create standalone scripts or inline queries in `src/`
|
|
752
|
+
|
|
753
|
+
### 4. Testing — Explicit Success Criteria
|
|
754
|
+
|
|
755
|
+
- ALWAYS define explicit success criteria for E2E tests
|
|
756
|
+
- "Page loads" is NOT a success criterion
|
|
757
|
+
- Every E2E test MUST verify: URL, visible elements, data displayed
|
|
758
|
+
- Minimum 3 assertions per test
|
|
759
|
+
|
|
760
|
+
```typescript
|
|
761
|
+
// CORRECT
|
|
762
|
+
await expect(page).toHaveURL('/dashboard');
|
|
763
|
+
await expect(page.locator('h1')).toContainText('Welcome');
|
|
764
|
+
await expect(page.locator('[data-testid="user"]')).toContainText('test@example.com');
|
|
765
|
+
|
|
766
|
+
// WRONG — no assertions
|
|
767
|
+
await page.goto('/dashboard');
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### 5. NEVER Hardcode Credentials
|
|
771
|
+
|
|
772
|
+
- ALWAYS use environment variables for secrets
|
|
773
|
+
- NEVER put API keys, passwords, or tokens directly in code
|
|
774
|
+
- NEVER hardcode connection strings — use environment variables from .env
|
|
775
|
+
|
|
776
|
+
### 6. ALWAYS Ask Before Deploying
|
|
777
|
+
|
|
778
|
+
- NEVER auto-deploy, even if the fix seems simple
|
|
779
|
+
- NEVER assume approval — wait for explicit "yes, deploy"
|
|
780
|
+
|
|
781
|
+
### 7. Quality Gates
|
|
782
|
+
|
|
783
|
+
- No file > 300 lines (split if larger)
|
|
784
|
+
- No function > 50 lines (extract helpers)
|
|
785
|
+
- All tests must pass before committing
|
|
786
|
+
- TypeScript must compile with no errors
|
|
787
|
+
|
|
788
|
+
### 8. Parallelize Independent Awaits
|
|
789
|
+
|
|
790
|
+
```typescript
|
|
791
|
+
// CORRECT — independent operations in parallel
|
|
792
|
+
const [users, products] = await Promise.all([getUsers(), getProducts()]);
|
|
793
|
+
|
|
794
|
+
// WRONG — sequential when independent
|
|
795
|
+
const users = await getUsers();
|
|
796
|
+
const products = await getProducts();
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
### 9. Git Workflow — NEVER Work Directly on Main
|
|
800
|
+
|
|
801
|
+
**Auto-branch hook is ON by default.** ALWAYS branch BEFORE editing any files:
|
|
802
|
+
|
|
803
|
+
```bash
|
|
804
|
+
git branch --show-current
|
|
805
|
+
# If on main → create a feature branch IMMEDIATELY:
|
|
806
|
+
git checkout -b feat/<task-name>
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
### 10. Docker Push Gate
|
|
810
|
+
|
|
811
|
+
When enabled, ANY `docker push` is BLOCKED until the image passes local verification.
|
|
812
|
+
|
|
813
|
+
---
|
|
814
|
+
|
|
815
|
+
## Service Ports (FIXED)
|
|
816
|
+
|
|
817
|
+
| Service | Dev Port | Test Port |
|
|
818
|
+
|---------|----------|-----------|
|
|
819
|
+
| Website | 3000 | 4000 |
|
|
820
|
+
| API | 3001 | 4010 |
|
|
821
|
+
| Dashboard | 3002 | 4020 |
|
|
822
|
+
|
|
823
|
+
---
|
|
824
|
+
|
|
825
|
+
## When Something Seems Wrong
|
|
826
|
+
|
|
827
|
+
- Missing UI element? → Check feature gates BEFORE assuming bug
|
|
828
|
+
- Empty data? → Check if services are running BEFORE assuming broken
|
|
829
|
+
- 404 error? → Check service separation BEFORE adding endpoint
|
|
830
|
+
- Auth failing? → Check which auth system BEFORE debugging
|
|
831
|
+
- Test failing? → Read the error message fully BEFORE changing code
|
|
832
|
+
|
|
833
|
+
---
|
|
834
|
+
|
|
835
|
+
## Project Documentation
|
|
836
|
+
|
|
837
|
+
| Document | Purpose | When to Read |
|
|
838
|
+
|----------|---------|--------------|
|
|
839
|
+
| `project-docs/ARCHITECTURE.md` | System overview & data flow | Before architectural changes |
|
|
840
|
+
| `project-docs/INFRASTRUCTURE.md` | Deployment details | Before environment changes |
|
|
841
|
+
| `project-docs/DECISIONS.md` | Architectural decisions | Before proposing alternatives |
|
|
842
|
+
|
|
843
|
+
**ALWAYS read relevant docs before making cross-service changes.**
|
|
844
|
+
|
|
845
|
+
---
|
|
846
|
+
|
|
847
|
+
## Workflow Preferences
|
|
848
|
+
|
|
849
|
+
- Quality over speed — if unsure, ask before executing
|
|
850
|
+
- Plan first, code second — use plan mode for non-trivial tasks
|
|
851
|
+
- One task, one chat — `/clear` between unrelated tasks
|
|
852
|
+
- When testing: queue observations, fix in batch (not one at a time)
|
|
853
|
+
|
|
854
|
+
---
|
|
855
|
+
|
|
856
|
+
## Naming — NEVER Rename Mid-Project
|
|
857
|
+
|
|
858
|
+
If you must rename packages, modules, or key variables:
|
|
859
|
+
|
|
860
|
+
1. Create a checklist of ALL files and references first
|
|
861
|
+
2. Use IDE semantic rename (not search-and-replace)
|
|
862
|
+
3. Full project search for old name after renaming
|
|
863
|
+
4. Check: .md files, .txt files, .env files, comments, strings, paths
|
|
864
|
+
5. Start a FRESH Claude session after renaming
|
|
865
|
+
CLAUDEMD_EOF
|
|
866
|
+
|
|
867
|
+
cat > "$PROJECT_PATH/CLAUDE.local.md" << 'LOCALMD_EOF'
|
|
868
|
+
# CLAUDE.local.md — Personal Overrides
|
|
869
|
+
|
|
870
|
+
> **This file is gitignored.** It's for YOUR personal preferences — things that shouldn't be shared with the team.
|
|
871
|
+
>
|
|
872
|
+
> **When to use this vs CLAUDE.md:**
|
|
873
|
+
> - `CLAUDE.md` = team rules (checked into git, everyone follows them)
|
|
874
|
+
> - `CLAUDE.local.md` = personal preferences (gitignored, only affects you)
|
|
875
|
+
|
|
876
|
+
---
|
|
877
|
+
|
|
878
|
+
## My Identity
|
|
879
|
+
|
|
880
|
+
- GitHub: YourUsername
|
|
881
|
+
- SSH: `git@github.com:YourUsername/<repo>.git`
|
|
882
|
+
|
|
883
|
+
## Communication Style
|
|
884
|
+
|
|
885
|
+
<!-- Uncomment the style that fits you: -->
|
|
886
|
+
<!-- - Respond concisely — I prefer terse explanations -->
|
|
887
|
+
<!-- - Be thorough — explain your reasoning in detail -->
|
|
888
|
+
<!-- - Show me the code first, explain after -->
|
|
889
|
+
|
|
890
|
+
## Commit Preferences
|
|
891
|
+
|
|
892
|
+
- When creating commits, use conventional commit format (feat:, fix:, docs:, etc.)
|
|
893
|
+
|
|
894
|
+
## Local Environment
|
|
895
|
+
|
|
896
|
+
- Node version: 20.x
|
|
897
|
+
- Package manager: pnpm
|
|
898
|
+
- OS: (your OS here)
|
|
899
|
+
LOCALMD_EOF
|
|
900
|
+
|
|
901
|
+
# ── Step 13: Create project templates + config files ──────────────────────────
|
|
902
|
+
progress "Creating project docs + config files..."
|
|
903
|
+
|
|
904
|
+
cat > "$PROJECT_PATH/project-docs/ARCHITECTURE.md" << 'ARCH_EOF'
|
|
905
|
+
# Architecture
|
|
906
|
+
|
|
907
|
+
> System overview and data flow for the project.
|
|
908
|
+
|
|
909
|
+
## Overview
|
|
910
|
+
|
|
911
|
+
<!-- Describe the high-level architecture here -->
|
|
912
|
+
|
|
913
|
+
## Components
|
|
914
|
+
|
|
915
|
+
<!-- List major components and their responsibilities -->
|
|
916
|
+
|
|
917
|
+
## Data Flow
|
|
918
|
+
|
|
919
|
+
<!-- Describe how data moves through the system -->
|
|
920
|
+
|
|
921
|
+
## Dependencies
|
|
922
|
+
|
|
923
|
+
<!-- List external services and dependencies -->
|
|
924
|
+
ARCH_EOF
|
|
925
|
+
|
|
926
|
+
cat > "$PROJECT_PATH/project-docs/INFRASTRUCTURE.md" << 'INFRA_EOF'
|
|
927
|
+
# Infrastructure
|
|
928
|
+
|
|
929
|
+
> Deployment and environment details.
|
|
930
|
+
|
|
931
|
+
## Environments
|
|
932
|
+
|
|
933
|
+
<!-- List environments: development, staging, production -->
|
|
934
|
+
|
|
935
|
+
## Deployment
|
|
936
|
+
|
|
937
|
+
<!-- Describe the deployment process -->
|
|
938
|
+
|
|
939
|
+
## Environment Variables
|
|
940
|
+
|
|
941
|
+
<!-- List required environment variables and their purpose -->
|
|
942
|
+
|
|
943
|
+
## Monitoring
|
|
944
|
+
|
|
945
|
+
<!-- Describe monitoring and alerting setup -->
|
|
946
|
+
INFRA_EOF
|
|
947
|
+
|
|
948
|
+
cat > "$PROJECT_PATH/project-docs/DECISIONS.md" << 'DEC_EOF'
|
|
949
|
+
# Architectural Decisions
|
|
950
|
+
|
|
951
|
+
> Record of key technical decisions and their rationale.
|
|
952
|
+
|
|
953
|
+
## Template
|
|
954
|
+
|
|
955
|
+
### Decision: [Title]
|
|
956
|
+
- **Date:** YYYY-MM-DD
|
|
957
|
+
- **Status:** Accepted / Superseded / Deprecated
|
|
958
|
+
- **Context:** What prompted the decision
|
|
959
|
+
- **Decision:** What was decided
|
|
960
|
+
- **Consequences:** What are the trade-offs
|
|
961
|
+
- **Alternatives considered:** What else was evaluated
|
|
962
|
+
|
|
963
|
+
---
|
|
964
|
+
|
|
965
|
+
<!-- Add decisions below -->
|
|
966
|
+
DEC_EOF
|
|
967
|
+
|
|
968
|
+
cat > "$PROJECT_PATH/tests/CHECKLIST.md" << 'CHECK_EOF'
|
|
969
|
+
# Test Checklist
|
|
970
|
+
|
|
971
|
+
> Track what needs testing and what's been verified.
|
|
972
|
+
|
|
973
|
+
## Test Coverage
|
|
974
|
+
|
|
975
|
+
| Area | Unit Tests | Integration Tests | E2E Tests | Status |
|
|
976
|
+
|------|-----------|-------------------|-----------|--------|
|
|
977
|
+
| <!-- feature --> | <!-- yes/no --> | <!-- yes/no --> | <!-- yes/no --> | <!-- pending/done --> |
|
|
978
|
+
|
|
979
|
+
## Manual Test Cases
|
|
980
|
+
|
|
981
|
+
- [ ] <!-- Describe manual test case -->
|
|
982
|
+
|
|
983
|
+
## Regression Tests
|
|
984
|
+
|
|
985
|
+
- [ ] <!-- Describe regression test case -->
|
|
986
|
+
CHECK_EOF
|
|
987
|
+
|
|
988
|
+
cat > "$PROJECT_PATH/tests/ISSUES_FOUND.md" << 'ISSUES_EOF'
|
|
989
|
+
# Issues Found During Testing
|
|
990
|
+
|
|
991
|
+
> Track bugs and issues discovered during testing sessions.
|
|
992
|
+
|
|
993
|
+
## Template
|
|
994
|
+
|
|
995
|
+
### Issue: [Title]
|
|
996
|
+
- **Found:** YYYY-MM-DD
|
|
997
|
+
- **Severity:** Critical / High / Medium / Low
|
|
998
|
+
- **Status:** Open / Fixed / Won't Fix
|
|
999
|
+
- **Description:** What happened
|
|
1000
|
+
- **Steps to reproduce:** How to trigger the issue
|
|
1001
|
+
- **Expected behavior:** What should happen
|
|
1002
|
+
- **Actual behavior:** What actually happened
|
|
1003
|
+
- **Fix:** How it was resolved (if fixed)
|
|
1004
|
+
|
|
1005
|
+
---
|
|
1006
|
+
|
|
1007
|
+
<!-- Add issues below -->
|
|
1008
|
+
ISSUES_EOF
|
|
1009
|
+
|
|
1010
|
+
# robots.txt + sitemap.xml
|
|
1011
|
+
cat > "$PROJECT_PATH/public/robots.txt" << 'ROBOTS_EOF'
|
|
1012
|
+
User-agent: *
|
|
1013
|
+
Allow: /
|
|
1014
|
+
Sitemap: https://example.com/sitemap.xml
|
|
1015
|
+
ROBOTS_EOF
|
|
1016
|
+
|
|
1017
|
+
cat > "$PROJECT_PATH/public/sitemap.xml" << 'SITEMAP_EOF'
|
|
1018
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
1019
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
1020
|
+
<url>
|
|
1021
|
+
<loc>https://example.com/</loc>
|
|
1022
|
+
<priority>1.0</priority>
|
|
1023
|
+
</url>
|
|
1024
|
+
</urlset>
|
|
1025
|
+
SITEMAP_EOF
|
|
1026
|
+
|
|
1027
|
+
# JSON-LD structured data component
|
|
1028
|
+
cat > "$PROJECT_PATH/src/app/json-ld.tsx" << 'JSONLD_EOF'
|
|
1029
|
+
export function JsonLd() {
|
|
1030
|
+
const structuredData = {
|
|
1031
|
+
'@context': 'https://schema.org',
|
|
1032
|
+
'@type': 'WebSite',
|
|
1033
|
+
name: 'My App',
|
|
1034
|
+
url: process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com',
|
|
1035
|
+
description: 'Built with Claude Code Mastery Starter Kit',
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
return (
|
|
1039
|
+
<script
|
|
1040
|
+
type="application/ld+json"
|
|
1041
|
+
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
|
|
1042
|
+
/>
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
JSONLD_EOF
|
|
1046
|
+
|
|
1047
|
+
# ── Step 14: Create .env + .gitignore + .dockerignore + README ────────────────
|
|
1048
|
+
progress "Creating .env, .gitignore, .dockerignore, README..."
|
|
1049
|
+
|
|
1050
|
+
touch "$PROJECT_PATH/.env"
|
|
1051
|
+
|
|
1052
|
+
cat > "$PROJECT_PATH/.env.example" << 'ENVEX_EOF'
|
|
1053
|
+
# Application
|
|
1054
|
+
NODE_ENV=development
|
|
1055
|
+
PORT=3000
|
|
1056
|
+
|
|
1057
|
+
# StrictDB
|
|
1058
|
+
STRICTDB_URI=mongodb+srv://user:password@cluster.mongodb.net/mydb?retryWrites=true&w=majority
|
|
1059
|
+
|
|
1060
|
+
# Rybbit Analytics
|
|
1061
|
+
NEXT_PUBLIC_RYBBIT_SITE_ID=your_rybbit_site_id
|
|
1062
|
+
NEXT_PUBLIC_RYBBIT_URL=https://app.rybbit.io
|
|
1063
|
+
|
|
1064
|
+
# Docker Hub
|
|
1065
|
+
DOCKER_HUB_USER=your_docker_username
|
|
1066
|
+
DOCKER_IMAGE_NAME=your_docker_username/your_app_name
|
|
1067
|
+
|
|
1068
|
+
# Dokploy Deployment
|
|
1069
|
+
DOKPLOY_URL=http://your-vps-ip:3000/api
|
|
1070
|
+
DOKPLOY_API_KEY=your_dokploy_api_key
|
|
1071
|
+
DOKPLOY_APP_ID=your_application_id
|
|
1072
|
+
DOKPLOY_REFRESH_TOKEN=your_webhook_refresh_token
|
|
1073
|
+
|
|
1074
|
+
# RuleCatch (optional)
|
|
1075
|
+
RULECATCH_API_KEY=dc_your_api_key_here
|
|
1076
|
+
RULECATCH_REGION=us
|
|
1077
|
+
ENVEX_EOF
|
|
1078
|
+
|
|
1079
|
+
cat > "$PROJECT_PATH/.gitignore" << 'GI_EOF'
|
|
1080
|
+
# Environment
|
|
1081
|
+
.env
|
|
1082
|
+
.env.*
|
|
1083
|
+
.env.local
|
|
1084
|
+
|
|
1085
|
+
# Dependencies
|
|
1086
|
+
node_modules/
|
|
1087
|
+
|
|
1088
|
+
# Build output
|
|
1089
|
+
.next/
|
|
1090
|
+
dist/
|
|
1091
|
+
out/
|
|
1092
|
+
|
|
1093
|
+
# Test artifacts
|
|
1094
|
+
coverage/
|
|
1095
|
+
test-results/
|
|
1096
|
+
playwright-report/
|
|
1097
|
+
|
|
1098
|
+
# IDE
|
|
1099
|
+
.idea/
|
|
1100
|
+
.vscode/
|
|
1101
|
+
*.swp
|
|
1102
|
+
*.swo
|
|
1103
|
+
|
|
1104
|
+
# OS
|
|
1105
|
+
.DS_Store
|
|
1106
|
+
Thumbs.db
|
|
1107
|
+
|
|
1108
|
+
# Claude local overrides
|
|
1109
|
+
CLAUDE.local.md
|
|
1110
|
+
|
|
1111
|
+
# Temporary AI research files
|
|
1112
|
+
_ai_temp/
|
|
1113
|
+
GI_EOF
|
|
1114
|
+
|
|
1115
|
+
cat > "$PROJECT_PATH/.dockerignore" << 'DI_EOF'
|
|
1116
|
+
.env
|
|
1117
|
+
.env.*
|
|
1118
|
+
.git/
|
|
1119
|
+
node_modules/
|
|
1120
|
+
.next/
|
|
1121
|
+
dist/
|
|
1122
|
+
coverage/
|
|
1123
|
+
test-results/
|
|
1124
|
+
playwright-report/
|
|
1125
|
+
*.md
|
|
1126
|
+
!README.md
|
|
1127
|
+
_ai_temp/
|
|
1128
|
+
DI_EOF
|
|
1129
|
+
|
|
1130
|
+
cat > "$PROJECT_PATH/README.md" << README_EOF
|
|
1131
|
+
# $PROJECT_NAME
|
|
1132
|
+
|
|
1133
|
+
> Scaffolded with [Claude Code Mastery Starter Kit](https://github.com/TheDecipherist/claude-code-mastery-project-starter-kit) (default profile)
|
|
1134
|
+
|
|
1135
|
+
## Tech Stack
|
|
1136
|
+
|
|
1137
|
+
- **Framework:** Next.js (App Router)
|
|
1138
|
+
- **Language:** TypeScript (strict mode)
|
|
1139
|
+
- **Database:** StrictDB (unified driver)
|
|
1140
|
+
- **Styling:** Tailwind CSS
|
|
1141
|
+
- **Testing:** Vitest (unit) + Playwright (E2E)
|
|
1142
|
+
- **Deployment:** Docker (multi-stage, standalone)
|
|
1143
|
+
|
|
1144
|
+
## Getting Started
|
|
1145
|
+
|
|
1146
|
+
\`\`\`bash
|
|
1147
|
+
pnpm install
|
|
1148
|
+
pnpm dev
|
|
1149
|
+
\`\`\`
|
|
1150
|
+
|
|
1151
|
+
Open [http://localhost:3000](http://localhost:3000) in your browser.
|
|
1152
|
+
|
|
1153
|
+
## Available Commands
|
|
1154
|
+
|
|
1155
|
+
Run \`/help\` in Claude Code to see all 16 available commands.
|
|
1156
|
+
|
|
1157
|
+
## Scripts
|
|
1158
|
+
|
|
1159
|
+
| Command | Description |
|
|
1160
|
+
|---------|-------------|
|
|
1161
|
+
| \`pnpm dev\` | Start dev server |
|
|
1162
|
+
| \`pnpm build\` | Build for production |
|
|
1163
|
+
| \`pnpm test\` | Run all tests |
|
|
1164
|
+
| \`pnpm test:unit\` | Unit tests |
|
|
1165
|
+
| \`pnpm test:e2e\` | E2E tests |
|
|
1166
|
+
| \`pnpm db:query <name>\` | Run a database query |
|
|
1167
|
+
| \`pnpm db:query:list\` | List available queries |
|
|
1168
|
+
|
|
1169
|
+
## Project Documentation
|
|
1170
|
+
|
|
1171
|
+
| Document | Purpose |
|
|
1172
|
+
|----------|---------|
|
|
1173
|
+
| \`project-docs/ARCHITECTURE.md\` | System overview & data flow |
|
|
1174
|
+
| \`project-docs/INFRASTRUCTURE.md\` | Deployment details |
|
|
1175
|
+
| \`project-docs/DECISIONS.md\` | Architectural decisions |
|
|
1176
|
+
README_EOF
|
|
1177
|
+
|
|
1178
|
+
# ── Step 15: Git init + pnpm install + register project ───────────────────────
|
|
1179
|
+
progress "Git init + pnpm install + registering project..."
|
|
1180
|
+
|
|
1181
|
+
git -C "$PROJECT_PATH" init -q
|
|
1182
|
+
git -C "$PROJECT_PATH" add -A
|
|
1183
|
+
git -C "$PROJECT_PATH" commit -q -m "Initial project scaffold (default profile)"
|
|
1184
|
+
|
|
1185
|
+
# Install dependencies
|
|
1186
|
+
cd "$PROJECT_PATH" && pnpm install --silent 2>/dev/null || true
|
|
1187
|
+
|
|
1188
|
+
# Register in project registry
|
|
1189
|
+
python3 << PYEOF
|
|
1190
|
+
import json, os
|
|
1191
|
+
from datetime import datetime, timezone
|
|
1192
|
+
|
|
1193
|
+
registry = "$REGISTRY"
|
|
1194
|
+
if os.path.exists(registry):
|
|
1195
|
+
with open(registry) as f:
|
|
1196
|
+
data = json.load(f)
|
|
1197
|
+
else:
|
|
1198
|
+
os.makedirs(os.path.dirname(registry), exist_ok=True)
|
|
1199
|
+
data = {"projects": []}
|
|
1200
|
+
|
|
1201
|
+
data["projects"].append({
|
|
1202
|
+
"name": "$PROJECT_NAME",
|
|
1203
|
+
"path": os.path.realpath("$PROJECT_PATH"),
|
|
1204
|
+
"profile": "default",
|
|
1205
|
+
"language": "node",
|
|
1206
|
+
"framework": "next",
|
|
1207
|
+
"database": "mongo",
|
|
1208
|
+
"createdAt": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
1209
|
+
})
|
|
1210
|
+
|
|
1211
|
+
with open(registry, "w") as f:
|
|
1212
|
+
json.dump(data, f, indent=2)
|
|
1213
|
+
f.write("\n")
|
|
1214
|
+
PYEOF
|
|
1215
|
+
|
|
1216
|
+
# ── Summary ────────────────────────────────────────────────────────────────────
|
|
1217
|
+
END_NS=$(date +%s%N)
|
|
1218
|
+
TOTAL_MS=$(( (END_NS - START_NS) / 1000000 ))
|
|
1219
|
+
FILE_COUNT=$(find "$PROJECT_PATH" -type f -not -path '*/.git/*' -not -path '*/node_modules/*' | wc -l)
|
|
1220
|
+
|
|
1221
|
+
# Format elapsed time
|
|
1222
|
+
if [ "$TOTAL_MS" -ge 1000 ]; then
|
|
1223
|
+
TOTAL_S=$((TOTAL_MS / 1000))
|
|
1224
|
+
TOTAL_FRAC=$(( (TOTAL_MS % 1000) / 100 ))
|
|
1225
|
+
TIME_STR="${TOTAL_S}.${TOTAL_FRAC}s"
|
|
1226
|
+
else
|
|
1227
|
+
TIME_STR="${TOTAL_MS}ms"
|
|
1228
|
+
fi
|
|
1229
|
+
|
|
1230
|
+
echo ""
|
|
1231
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
1232
|
+
echo " Completed in ${TIME_STR}"
|
|
1233
|
+
echo " Created at: $PROJECT_PATH"
|
|
1234
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
1235
|
+
echo ""
|
|
1236
|
+
echo " ${FILE_COUNT} files | 16 commands | 2 skills | 2 agents | 9 hooks"
|
|
1237
|
+
echo ""
|
|
1238
|
+
echo " Stack: Next.js + StrictDB + Tailwind + Docker"
|
|
1239
|
+
echo " Testing: Vitest (unit) + Playwright (E2E)"
|
|
1240
|
+
echo " CI: GitHub Actions"
|
|
1241
|
+
echo ""
|
|
1242
|
+
echo " Next steps:"
|
|
1243
|
+
echo " cd $PROJECT_PATH"
|
|
1244
|
+
echo " pnpm dev # Start dev server"
|
|
1245
|
+
echo " claude # Start Claude Code — run /help to see commands"
|
|
1246
|
+
echo ""
|
|
1247
|
+
echo " Configure environment:"
|
|
1248
|
+
echo " cp .env.example .env"
|
|
1249
|
+
echo " # Edit .env with your StrictDB URI, Rybbit ID, etc."
|
|
1250
|
+
echo " # Or run /setup in Claude for interactive configuration"
|
|
1251
|
+
echo ""
|