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