create-turbo-mono 1.0.7 → 1.0.9
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/settings.local.json +2 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +4 -0
- package/dist/scaffold.js.map +1 -1
- package/dist/templates/claude.d.ts +3 -0
- package/dist/templates/claude.d.ts.map +1 -0
- package/dist/templates/claude.js +318 -0
- package/dist/templates/claude.js.map +1 -0
- package/package.json +1 -1
- package/src/scaffold.ts +5 -0
- package/src/templates/claude.ts +341 -0
package/dist/scaffold.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC1E"}
|
package/dist/scaffold.js
CHANGED
|
@@ -13,6 +13,7 @@ const shared_1 = require("./templates/shared");
|
|
|
13
13
|
const docker_1 = require("./templates/docker");
|
|
14
14
|
const cicd_1 = require("./templates/cicd");
|
|
15
15
|
const docs_1 = require("./templates/docs");
|
|
16
|
+
const claude_1 = require("./templates/claude");
|
|
16
17
|
async function scaffoldProject(config) {
|
|
17
18
|
const { targetDir, backends, frontends } = config;
|
|
18
19
|
// Create directory structure
|
|
@@ -38,5 +39,8 @@ async function scaffoldProject(config) {
|
|
|
38
39
|
await (0, cicd_1.createCICDFiles)(config);
|
|
39
40
|
// Create documentation
|
|
40
41
|
await (0, docs_1.createDocumentation)(config);
|
|
42
|
+
// Create CLAUDE.md files (root + per-app + packages/) so Claude Code
|
|
43
|
+
// auto-loads project context every session.
|
|
44
|
+
await (0, claude_1.createClaudeFiles)(config);
|
|
41
45
|
}
|
|
42
46
|
//# sourceMappingURL=scaffold.js.map
|
package/dist/scaffold.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":";;;;;AAkBA,0CAqCC;AAvDD,wDAA0B;AAC1B,gDAAwB;AACxB,2CAAmD;AACnD,iDAAuD;AACvD,mDAAyD;AACzD,+CAA0D;AAC1D,+CAAuD;AACvD,2CAAmD;AACnD,2CAAuD;AACvD,+CAAuD;AAShD,KAAK,UAAU,eAAe,CAAC,MAAqB;IACzD,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAElD,6BAA6B;IAC7B,MAAM,kBAAE,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAC9B,MAAM,kBAAE,CAAC,SAAS,CAAC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAC5D,MAAM,kBAAE,CAAC,SAAS,CAAC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAC7D,MAAM,kBAAE,CAAC,SAAS,CAAC,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IAErD,kCAAkC;IAClC,MAAM,IAAA,sBAAe,EAAC,MAAM,CAAC,CAAC;IAE9B,sBAAsB;IACtB,KAAK,MAAM,WAAW,IAAI,QAAQ,EAAE,CAAC;QACnC,MAAM,IAAA,0BAAgB,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IACjD,CAAC;IAED,uBAAuB;IACvB,KAAK,MAAM,YAAY,IAAI,SAAS,EAAE,CAAC;QACrC,MAAM,IAAA,4BAAiB,EAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACnD,CAAC;IAED,yBAAyB;IACzB,MAAM,IAAA,6BAAoB,EAAC,SAAS,CAAC,CAAC;IAEtC,sBAAsB;IACtB,MAAM,IAAA,0BAAiB,EAAC,MAAM,CAAC,CAAC;IAEhC,qBAAqB;IACrB,MAAM,IAAA,sBAAe,EAAC,MAAM,CAAC,CAAC;IAE9B,uBAAuB;IACvB,MAAM,IAAA,0BAAmB,EAAC,MAAM,CAAC,CAAC;IAElC,qEAAqE;IACrE,4CAA4C;IAC5C,MAAM,IAAA,0BAAiB,EAAC,MAAM,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/templates/claude.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAc5E"}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createClaudeFiles = createClaudeFiles;
|
|
7
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
async function createClaudeFiles(config) {
|
|
10
|
+
const { targetDir, projectName, backends, frontends } = config;
|
|
11
|
+
await createRootClaudeMd(targetDir, projectName, backends, frontends);
|
|
12
|
+
for (const name of backends) {
|
|
13
|
+
await createBackendClaudeMd(targetDir, name);
|
|
14
|
+
}
|
|
15
|
+
for (const name of frontends) {
|
|
16
|
+
await createFrontendClaudeMd(targetDir, name);
|
|
17
|
+
}
|
|
18
|
+
await createSharedClaudeMd(targetDir);
|
|
19
|
+
}
|
|
20
|
+
async function createRootClaudeMd(targetDir, projectName, backends, frontends) {
|
|
21
|
+
const backendList = backends.length
|
|
22
|
+
? backends.map((n, i) => ` - \`apps/backend/${n}\` — NestJS service (port ${4000 + i})`).join('\n')
|
|
23
|
+
: ' - _(none)_';
|
|
24
|
+
const frontendList = frontends.length
|
|
25
|
+
? frontends.map((n, i) => ` - \`apps/frontend/${n}\` — Next.js app (port ${3000 + i})`).join('\n')
|
|
26
|
+
: ' - _(none)_';
|
|
27
|
+
const content = `# ${projectName}
|
|
28
|
+
|
|
29
|
+
> This file is auto-loaded by Claude Code at the start of every session.
|
|
30
|
+
> Keep it short, imperative, and pointed at where the truth lives in code.
|
|
31
|
+
> Long-form docs belong in \`docs/\` — link to them from here.
|
|
32
|
+
|
|
33
|
+
## Project overview
|
|
34
|
+
|
|
35
|
+
${projectName} is a Turborepo monorepo containing NestJS backends and Next.js frontends sharing
|
|
36
|
+
TypeScript packages, a single Prisma schema, and one Dockerfile with per-app build targets.
|
|
37
|
+
|
|
38
|
+
- Package manager: **pnpm** (workspaces). Never use \`npm\` or \`yarn\` in this repo.
|
|
39
|
+
- Build orchestrator: **Turborepo** (\`turbo.json\` defines the pipeline).
|
|
40
|
+
- Database: **PostgreSQL** via Prisma in \`packages/database\`.
|
|
41
|
+
- Cache/queues: **Redis**.
|
|
42
|
+
|
|
43
|
+
## Repo map
|
|
44
|
+
|
|
45
|
+
\`\`\`
|
|
46
|
+
${projectName}/
|
|
47
|
+
├── apps/
|
|
48
|
+
│ ├── backend/ # NestJS services
|
|
49
|
+
${backendList}
|
|
50
|
+
│ └── frontend/ # Next.js apps
|
|
51
|
+
${frontendList}
|
|
52
|
+
├── packages/
|
|
53
|
+
│ ├── database/ # Prisma schema, migrations, generated client
|
|
54
|
+
│ ├── shared-backend/ # Backend-only utilities (NestJS guards, pipes, types)
|
|
55
|
+
│ ├── shared-frontend/ # React components, hooks, client utilities
|
|
56
|
+
│ ├── shared-common/ # Isomorphic utilities (zod schemas, constants, types)
|
|
57
|
+
│ └── tsconfig/ # Shared tsconfig presets
|
|
58
|
+
├── docs/ # Human docs — see @docs/ENVIRONMENT.md
|
|
59
|
+
└── Dockerfile # Multi-stage; one target per app
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
## Common commands
|
|
63
|
+
|
|
64
|
+
Run from the repo root unless noted.
|
|
65
|
+
|
|
66
|
+
\`\`\`bash
|
|
67
|
+
pnpm install # install all workspace deps
|
|
68
|
+
pnpm dev # turbo run dev — starts every app in watch mode
|
|
69
|
+
pnpm build # turbo run build — production builds
|
|
70
|
+
pnpm type-check # turbo run type-check
|
|
71
|
+
pnpm lint # turbo run lint
|
|
72
|
+
|
|
73
|
+
pnpm db:generate # regenerate Prisma client (run after schema changes)
|
|
74
|
+
pnpm db:migrate # prisma migrate dev — create + apply a new migration
|
|
75
|
+
pnpm db:studio # open Prisma Studio
|
|
76
|
+
|
|
77
|
+
# Run a single app
|
|
78
|
+
pnpm --filter <app-name> dev
|
|
79
|
+
pnpm --filter <app-name> build
|
|
80
|
+
\`\`\`
|
|
81
|
+
|
|
82
|
+
Docker (local infra only — Postgres + Redis):
|
|
83
|
+
|
|
84
|
+
\`\`\`bash
|
|
85
|
+
docker compose -f docker-compose.local.yml up -d
|
|
86
|
+
\`\`\`
|
|
87
|
+
|
|
88
|
+
Full stack in containers:
|
|
89
|
+
|
|
90
|
+
\`\`\`bash
|
|
91
|
+
docker compose -f docker-compose.dev.yml up --build
|
|
92
|
+
\`\`\`
|
|
93
|
+
|
|
94
|
+
## Tech stack conventions
|
|
95
|
+
|
|
96
|
+
- **TypeScript strict mode** everywhere. No \`any\` without a comment explaining why.
|
|
97
|
+
- **ES modules**. Use \`import\`/\`export\`, not \`require\`.
|
|
98
|
+
- Path aliases resolve via \`@repo/*\` for workspace packages — import shared code as
|
|
99
|
+
\`import { x } from '@repo/shared-common'\`, never via relative \`../../packages/...\` paths.
|
|
100
|
+
- Validation at boundaries: **zod** schemas live in \`@repo/shared-common\` so frontend and
|
|
101
|
+
backend share one source of truth. Reuse them — do not redefine DTOs per app.
|
|
102
|
+
- Errors: throw typed errors from \`@repo/shared-backend\`. Don't throw raw \`Error\` strings
|
|
103
|
+
in HTTP paths.
|
|
104
|
+
|
|
105
|
+
## Architecture rules
|
|
106
|
+
|
|
107
|
+
1. **Database access only from backend apps.** Frontends call backend HTTP endpoints; they
|
|
108
|
+
never import \`@prisma/client\` or \`@repo/database\`.
|
|
109
|
+
2. **Schema is one file.** \`packages/database/prisma/schema.prisma\` is the only Prisma
|
|
110
|
+
schema. Do not create per-app schemas.
|
|
111
|
+
3. **No cross-app imports.** \`apps/backend/foo\` must not import from \`apps/backend/bar\`.
|
|
112
|
+
If two apps need the same code, lift it into \`packages/shared-*\`.
|
|
113
|
+
4. **\`shared-frontend\` is React-only**, \`shared-backend\` is Node-only, \`shared-common\` is
|
|
114
|
+
isomorphic (no \`fs\`, no \`window\`). Pick the right one.
|
|
115
|
+
5. Environment variables are read **once at startup** in a typed config module — not
|
|
116
|
+
sprinkled as \`process.env.X\` throughout business logic.
|
|
117
|
+
|
|
118
|
+
## Adding things
|
|
119
|
+
|
|
120
|
+
- **New backend app** → \`apps/backend/<name>/\`, add a \`package.json\` named \`@app/<name>\`,
|
|
121
|
+
then add a Docker target in \`Dockerfile\` and services in the three compose files.
|
|
122
|
+
pnpm picks it up automatically via \`pnpm-workspace.yaml\`.
|
|
123
|
+
- **New frontend app** → same idea under \`apps/frontend/<name>/\`.
|
|
124
|
+
- **New shared package** → \`packages/<name>/\`, name it \`@repo/<name>\`, export a clean
|
|
125
|
+
public API from \`src/index.ts\`.
|
|
126
|
+
- **New env var** → add to \`.env.example\` AND document in [docs/ENVIRONMENT.md](docs/ENVIRONMENT.md).
|
|
127
|
+
|
|
128
|
+
## Testing
|
|
129
|
+
|
|
130
|
+
- Unit tests live next to the code: \`foo.ts\` + \`foo.spec.ts\`.
|
|
131
|
+
- Integration tests that touch the DB use a real Postgres (the \`docker-compose.local.yml\`
|
|
132
|
+
one) — **do not mock Prisma**. Mocks have hidden divergence from real query behavior.
|
|
133
|
+
- Run a single app's tests: \`pnpm --filter <app-name> test\`.
|
|
134
|
+
|
|
135
|
+
## Git & CI
|
|
136
|
+
|
|
137
|
+
- Default branch: \`main\`. Feature branches: \`feat/<short-name>\`, \`fix/<short-name>\`.
|
|
138
|
+
- Conventional commits (\`feat:\`, \`fix:\`, \`chore:\`, \`docs:\`, \`refactor:\`).
|
|
139
|
+
- CI runs \`type-check\`, \`lint\`, \`build\` on every PR (see \`.github/workflows/\`).
|
|
140
|
+
- **Never** commit \`.env\`, \`.env.local\`, \`*.pem\`, or anything under \`packages/database/prisma/migrations\`
|
|
141
|
+
that wasn't generated by \`pnpm db:migrate\`.
|
|
142
|
+
|
|
143
|
+
## Don't
|
|
144
|
+
|
|
145
|
+
- Don't run \`npm install\` or \`yarn\` — use \`pnpm\`.
|
|
146
|
+
- Don't bypass Turbo by \`cd\`-ing into an app and running raw scripts unless debugging.
|
|
147
|
+
- Don't add a new top-level dependency to multiple apps individually — put it in the
|
|
148
|
+
appropriate \`packages/shared-*\` and re-export.
|
|
149
|
+
- Don't \`prisma db push\` against a shared/dev database — always go through \`prisma migrate\`.
|
|
150
|
+
- Don't edit generated files (\`packages/database/node_modules/.prisma\`, \`.next/\`, \`dist/\`).
|
|
151
|
+
- Don't skip git hooks (\`--no-verify\`) without an explicit reason in the commit body.
|
|
152
|
+
|
|
153
|
+
## When you finish a task
|
|
154
|
+
|
|
155
|
+
1. \`pnpm type-check && pnpm lint\` — both must pass.
|
|
156
|
+
2. If you changed the Prisma schema: \`pnpm db:generate\` and create a migration.
|
|
157
|
+
3. If you added an env var: update \`.env.example\` and \`docs/ENVIRONMENT.md\`.
|
|
158
|
+
4. If you added/removed an app: update the Dockerfile and all three compose files.
|
|
159
|
+
|
|
160
|
+
## Pointers
|
|
161
|
+
|
|
162
|
+
- Environment variables: @docs/ENVIRONMENT.md
|
|
163
|
+
- README (human-facing setup): @README.md
|
|
164
|
+
- Prisma schema: @packages/database/prisma/schema.prisma
|
|
165
|
+
- Turbo pipeline: @turbo.json
|
|
166
|
+
|
|
167
|
+
App-specific context lives in per-app \`CLAUDE.md\` files (e.g.
|
|
168
|
+
\`apps/backend/<name>/CLAUDE.md\`). Claude Code auto-discovers them when working inside
|
|
169
|
+
that subtree.
|
|
170
|
+
`;
|
|
171
|
+
await fs_extra_1.default.writeFile(path_1.default.join(targetDir, 'CLAUDE.md'), content);
|
|
172
|
+
}
|
|
173
|
+
async function createBackendClaudeMd(targetDir, name) {
|
|
174
|
+
const dir = path_1.default.join(targetDir, 'apps', 'backend', name);
|
|
175
|
+
await fs_extra_1.default.ensureDir(dir);
|
|
176
|
+
const content = `# ${name} (backend)
|
|
177
|
+
|
|
178
|
+
NestJS service. See the root [CLAUDE.md](../../../CLAUDE.md) for monorepo-wide rules;
|
|
179
|
+
this file only documents what is specific to \`${name}\`.
|
|
180
|
+
|
|
181
|
+
## Layout
|
|
182
|
+
|
|
183
|
+
\`\`\`
|
|
184
|
+
src/
|
|
185
|
+
├── main.ts # bootstrap — keep this minimal
|
|
186
|
+
├── app.module.ts # root module
|
|
187
|
+
├── modules/ # feature modules (one folder per domain concept)
|
|
188
|
+
│ └── <feature>/
|
|
189
|
+
│ ├── <feature>.controller.ts
|
|
190
|
+
│ ├── <feature>.service.ts
|
|
191
|
+
│ ├── <feature>.module.ts
|
|
192
|
+
│ └── dto/ # request/response shapes — prefer importing zod schemas
|
|
193
|
+
│ # from @repo/shared-common over redefining DTOs here
|
|
194
|
+
└── common/ # cross-cutting filters, guards, interceptors
|
|
195
|
+
\`\`\`
|
|
196
|
+
|
|
197
|
+
## Conventions
|
|
198
|
+
|
|
199
|
+
- One feature = one module. Don't put unrelated controllers in the same module.
|
|
200
|
+
- Services are injectable and stateless. Persistent state goes in Prisma or Redis.
|
|
201
|
+
- Use \`@repo/shared-backend\` guards/pipes — don't reinvent them here.
|
|
202
|
+
- Validate request bodies with the zod schemas in \`@repo/shared-common\`. Pair them with
|
|
203
|
+
the shared \`ZodValidationPipe\`.
|
|
204
|
+
- Health check at \`GET /health\` is required (the prod compose file probes it).
|
|
205
|
+
|
|
206
|
+
## Commands
|
|
207
|
+
|
|
208
|
+
\`\`\`bash
|
|
209
|
+
pnpm --filter ${name} dev # watch mode
|
|
210
|
+
pnpm --filter ${name} build
|
|
211
|
+
pnpm --filter ${name} test
|
|
212
|
+
pnpm --filter ${name} test:e2e
|
|
213
|
+
\`\`\`
|
|
214
|
+
|
|
215
|
+
## Don't
|
|
216
|
+
|
|
217
|
+
- Don't import from another backend app (\`apps/backend/<other>\`). Lift shared code into
|
|
218
|
+
\`packages/shared-backend\` instead.
|
|
219
|
+
- Don't bypass the global validation pipe — every controller input must be validated.
|
|
220
|
+
`;
|
|
221
|
+
await fs_extra_1.default.writeFile(path_1.default.join(dir, 'CLAUDE.md'), content);
|
|
222
|
+
}
|
|
223
|
+
async function createFrontendClaudeMd(targetDir, name) {
|
|
224
|
+
const dir = path_1.default.join(targetDir, 'apps', 'frontend', name);
|
|
225
|
+
await fs_extra_1.default.ensureDir(dir);
|
|
226
|
+
const content = `# ${name} (frontend)
|
|
227
|
+
|
|
228
|
+
Next.js app (App Router). See the root [CLAUDE.md](../../../CLAUDE.md) for monorepo-wide
|
|
229
|
+
rules; this file documents only what is specific to \`${name}\`.
|
|
230
|
+
|
|
231
|
+
## Layout
|
|
232
|
+
|
|
233
|
+
\`\`\`
|
|
234
|
+
app/ # App Router routes
|
|
235
|
+
layout.tsx
|
|
236
|
+
page.tsx
|
|
237
|
+
(group)/... # route groups
|
|
238
|
+
components/ # ${name}-specific components
|
|
239
|
+
ui/ # presentational, no data fetching
|
|
240
|
+
features/ # feature components that fetch / mutate
|
|
241
|
+
lib/ # ${name}-specific helpers (API client, hooks)
|
|
242
|
+
\`\`\`
|
|
243
|
+
|
|
244
|
+
Reusable cross-app components live in \`@repo/shared-frontend\`. If you find yourself
|
|
245
|
+
copying a component to another frontend app, move it there.
|
|
246
|
+
|
|
247
|
+
## Conventions
|
|
248
|
+
|
|
249
|
+
- **Server Components by default.** Add \`'use client'\` only when you need state, effects,
|
|
250
|
+
or browser APIs.
|
|
251
|
+
- Data fetching: server components fetch directly; client components use the typed API
|
|
252
|
+
client from \`@repo/shared-frontend\`.
|
|
253
|
+
- Types and zod schemas come from \`@repo/shared-common\` — do not redefine API response
|
|
254
|
+
shapes here.
|
|
255
|
+
- All env vars referenced in client code must be prefixed \`NEXT_PUBLIC_\`. Anything else is
|
|
256
|
+
server-only.
|
|
257
|
+
|
|
258
|
+
## Commands
|
|
259
|
+
|
|
260
|
+
\`\`\`bash
|
|
261
|
+
pnpm --filter ${name} dev
|
|
262
|
+
pnpm --filter ${name} build
|
|
263
|
+
pnpm --filter ${name} start # serve the production build locally
|
|
264
|
+
pnpm --filter ${name} lint
|
|
265
|
+
\`\`\`
|
|
266
|
+
|
|
267
|
+
## Don't
|
|
268
|
+
|
|
269
|
+
- Don't import server-only modules (\`fs\`, \`@repo/database\`, \`@repo/shared-backend\`) into
|
|
270
|
+
client components — Next.js will fail the build.
|
|
271
|
+
- Don't fetch in a \`useEffect\` for data that could be fetched on the server.
|
|
272
|
+
- Don't hand-roll API types. Reuse zod schemas from \`@repo/shared-common\`.
|
|
273
|
+
`;
|
|
274
|
+
await fs_extra_1.default.writeFile(path_1.default.join(dir, 'CLAUDE.md'), content);
|
|
275
|
+
}
|
|
276
|
+
async function createSharedClaudeMd(targetDir) {
|
|
277
|
+
const dir = path_1.default.join(targetDir, 'packages');
|
|
278
|
+
await fs_extra_1.default.ensureDir(dir);
|
|
279
|
+
const content = `# packages/
|
|
280
|
+
|
|
281
|
+
Shared workspace packages. See the root [CLAUDE.md](../CLAUDE.md) for monorepo rules.
|
|
282
|
+
|
|
283
|
+
## Which package does my code go in?
|
|
284
|
+
|
|
285
|
+
| Code uses... | Goes in |
|
|
286
|
+
| -------------------------------- | -------------------- |
|
|
287
|
+
| \`fs\`, \`process\`, NestJS, Prisma | \`shared-backend\` |
|
|
288
|
+
| \`react\`, \`window\`, \`document\` | \`shared-frontend\` |
|
|
289
|
+
| Pure TS, zod, no env coupling | \`shared-common\` |
|
|
290
|
+
| Prisma schema or DB client | \`database\` |
|
|
291
|
+
| tsconfig presets | \`tsconfig\` |
|
|
292
|
+
|
|
293
|
+
When in doubt, default to \`shared-common\` — it's the safest place because both sides
|
|
294
|
+
can import it.
|
|
295
|
+
|
|
296
|
+
## Conventions
|
|
297
|
+
|
|
298
|
+
- Every package exports its public API from \`src/index.ts\`. Don't let consumers
|
|
299
|
+
deep-import (\`@repo/foo/src/internal/x\`) — that's a leak.
|
|
300
|
+
- Package names are \`@repo/<folder>\`. Keep them in sync with the folder name.
|
|
301
|
+
- Adding a new package: copy the structure of an existing one, add it to
|
|
302
|
+
\`pnpm-workspace.yaml\` (already covered by the \`packages/*\` glob), run \`pnpm install\`.
|
|
303
|
+
|
|
304
|
+
## Database package
|
|
305
|
+
|
|
306
|
+
\`packages/database\` owns the Prisma schema and the generated client. After editing
|
|
307
|
+
\`schema.prisma\`:
|
|
308
|
+
|
|
309
|
+
\`\`\`bash
|
|
310
|
+
pnpm db:generate # regenerate the client (required before type-check passes)
|
|
311
|
+
pnpm db:migrate # create + apply a migration in dev
|
|
312
|
+
\`\`\`
|
|
313
|
+
|
|
314
|
+
Never run \`prisma db push\` against a shared database — it skips the migration history.
|
|
315
|
+
`;
|
|
316
|
+
await fs_extra_1.default.writeFile(path_1.default.join(dir, 'CLAUDE.md'), content);
|
|
317
|
+
}
|
|
318
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/templates/claude.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/scaffold.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { createSharedPackages } from './templates/shared';
|
|
|
7
7
|
import { createDockerFiles } from './templates/docker';
|
|
8
8
|
import { createCICDFiles } from './templates/cicd';
|
|
9
9
|
import { createDocumentation } from './templates/docs';
|
|
10
|
+
import { createClaudeFiles } from './templates/claude';
|
|
10
11
|
|
|
11
12
|
export interface ProjectConfig {
|
|
12
13
|
projectName: string;
|
|
@@ -48,4 +49,8 @@ export async function scaffoldProject(config: ProjectConfig): Promise<void> {
|
|
|
48
49
|
|
|
49
50
|
// Create documentation
|
|
50
51
|
await createDocumentation(config);
|
|
52
|
+
|
|
53
|
+
// Create CLAUDE.md files (root + per-app + packages/) so Claude Code
|
|
54
|
+
// auto-loads project context every session.
|
|
55
|
+
await createClaudeFiles(config);
|
|
51
56
|
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { ProjectConfig } from '../scaffold';
|
|
4
|
+
|
|
5
|
+
export async function createClaudeFiles(config: ProjectConfig): Promise<void> {
|
|
6
|
+
const { targetDir, projectName, backends, frontends } = config;
|
|
7
|
+
|
|
8
|
+
await createRootClaudeMd(targetDir, projectName, backends, frontends);
|
|
9
|
+
|
|
10
|
+
for (const name of backends) {
|
|
11
|
+
await createBackendClaudeMd(targetDir, name);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
for (const name of frontends) {
|
|
15
|
+
await createFrontendClaudeMd(targetDir, name);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
await createSharedClaudeMd(targetDir);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function createRootClaudeMd(
|
|
22
|
+
targetDir: string,
|
|
23
|
+
projectName: string,
|
|
24
|
+
backends: string[],
|
|
25
|
+
frontends: string[]
|
|
26
|
+
): Promise<void> {
|
|
27
|
+
const backendList = backends.length
|
|
28
|
+
? backends.map((n, i) => ` - \`apps/backend/${n}\` — NestJS service (port ${4000 + i})`).join('\n')
|
|
29
|
+
: ' - _(none)_';
|
|
30
|
+
|
|
31
|
+
const frontendList = frontends.length
|
|
32
|
+
? frontends.map((n, i) => ` - \`apps/frontend/${n}\` — Next.js app (port ${3000 + i})`).join('\n')
|
|
33
|
+
: ' - _(none)_';
|
|
34
|
+
|
|
35
|
+
const content = `# ${projectName}
|
|
36
|
+
|
|
37
|
+
> This file is auto-loaded by Claude Code at the start of every session.
|
|
38
|
+
> Keep it short, imperative, and pointed at where the truth lives in code.
|
|
39
|
+
> Long-form docs belong in \`docs/\` — link to them from here.
|
|
40
|
+
|
|
41
|
+
## Project overview
|
|
42
|
+
|
|
43
|
+
${projectName} is a Turborepo monorepo containing NestJS backends and Next.js frontends sharing
|
|
44
|
+
TypeScript packages, a single Prisma schema, and one Dockerfile with per-app build targets.
|
|
45
|
+
|
|
46
|
+
- Package manager: **pnpm** (workspaces). Never use \`npm\` or \`yarn\` in this repo.
|
|
47
|
+
- Build orchestrator: **Turborepo** (\`turbo.json\` defines the pipeline).
|
|
48
|
+
- Database: **PostgreSQL** via Prisma in \`packages/database\`.
|
|
49
|
+
- Cache/queues: **Redis**.
|
|
50
|
+
|
|
51
|
+
## Repo map
|
|
52
|
+
|
|
53
|
+
\`\`\`
|
|
54
|
+
${projectName}/
|
|
55
|
+
├── apps/
|
|
56
|
+
│ ├── backend/ # NestJS services
|
|
57
|
+
${backendList}
|
|
58
|
+
│ └── frontend/ # Next.js apps
|
|
59
|
+
${frontendList}
|
|
60
|
+
├── packages/
|
|
61
|
+
│ ├── database/ # Prisma schema, migrations, generated client
|
|
62
|
+
│ ├── shared-backend/ # Backend-only utilities (NestJS guards, pipes, types)
|
|
63
|
+
│ ├── shared-frontend/ # React components, hooks, client utilities
|
|
64
|
+
│ ├── shared-common/ # Isomorphic utilities (zod schemas, constants, types)
|
|
65
|
+
│ └── tsconfig/ # Shared tsconfig presets
|
|
66
|
+
├── docs/ # Human docs — see @docs/ENVIRONMENT.md
|
|
67
|
+
└── Dockerfile # Multi-stage; one target per app
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
## Common commands
|
|
71
|
+
|
|
72
|
+
Run from the repo root unless noted.
|
|
73
|
+
|
|
74
|
+
\`\`\`bash
|
|
75
|
+
pnpm install # install all workspace deps
|
|
76
|
+
pnpm dev # turbo run dev — starts every app in watch mode
|
|
77
|
+
pnpm build # turbo run build — production builds
|
|
78
|
+
pnpm type-check # turbo run type-check
|
|
79
|
+
pnpm lint # turbo run lint
|
|
80
|
+
|
|
81
|
+
pnpm db:generate # regenerate Prisma client (run after schema changes)
|
|
82
|
+
pnpm db:migrate # prisma migrate dev — create + apply a new migration
|
|
83
|
+
pnpm db:studio # open Prisma Studio
|
|
84
|
+
|
|
85
|
+
# Run a single app
|
|
86
|
+
pnpm --filter <app-name> dev
|
|
87
|
+
pnpm --filter <app-name> build
|
|
88
|
+
\`\`\`
|
|
89
|
+
|
|
90
|
+
Docker (local infra only — Postgres + Redis):
|
|
91
|
+
|
|
92
|
+
\`\`\`bash
|
|
93
|
+
docker compose -f docker-compose.local.yml up -d
|
|
94
|
+
\`\`\`
|
|
95
|
+
|
|
96
|
+
Full stack in containers:
|
|
97
|
+
|
|
98
|
+
\`\`\`bash
|
|
99
|
+
docker compose -f docker-compose.dev.yml up --build
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
## Tech stack conventions
|
|
103
|
+
|
|
104
|
+
- **TypeScript strict mode** everywhere. No \`any\` without a comment explaining why.
|
|
105
|
+
- **ES modules**. Use \`import\`/\`export\`, not \`require\`.
|
|
106
|
+
- Path aliases resolve via \`@repo/*\` for workspace packages — import shared code as
|
|
107
|
+
\`import { x } from '@repo/shared-common'\`, never via relative \`../../packages/...\` paths.
|
|
108
|
+
- Validation at boundaries: **zod** schemas live in \`@repo/shared-common\` so frontend and
|
|
109
|
+
backend share one source of truth. Reuse them — do not redefine DTOs per app.
|
|
110
|
+
- Errors: throw typed errors from \`@repo/shared-backend\`. Don't throw raw \`Error\` strings
|
|
111
|
+
in HTTP paths.
|
|
112
|
+
|
|
113
|
+
## Architecture rules
|
|
114
|
+
|
|
115
|
+
1. **Database access only from backend apps.** Frontends call backend HTTP endpoints; they
|
|
116
|
+
never import \`@prisma/client\` or \`@repo/database\`.
|
|
117
|
+
2. **Schema is one file.** \`packages/database/prisma/schema.prisma\` is the only Prisma
|
|
118
|
+
schema. Do not create per-app schemas.
|
|
119
|
+
3. **No cross-app imports.** \`apps/backend/foo\` must not import from \`apps/backend/bar\`.
|
|
120
|
+
If two apps need the same code, lift it into \`packages/shared-*\`.
|
|
121
|
+
4. **\`shared-frontend\` is React-only**, \`shared-backend\` is Node-only, \`shared-common\` is
|
|
122
|
+
isomorphic (no \`fs\`, no \`window\`). Pick the right one.
|
|
123
|
+
5. Environment variables are read **once at startup** in a typed config module — not
|
|
124
|
+
sprinkled as \`process.env.X\` throughout business logic.
|
|
125
|
+
|
|
126
|
+
## Adding things
|
|
127
|
+
|
|
128
|
+
- **New backend app** → \`apps/backend/<name>/\`, add a \`package.json\` named \`@app/<name>\`,
|
|
129
|
+
then add a Docker target in \`Dockerfile\` and services in the three compose files.
|
|
130
|
+
pnpm picks it up automatically via \`pnpm-workspace.yaml\`.
|
|
131
|
+
- **New frontend app** → same idea under \`apps/frontend/<name>/\`.
|
|
132
|
+
- **New shared package** → \`packages/<name>/\`, name it \`@repo/<name>\`, export a clean
|
|
133
|
+
public API from \`src/index.ts\`.
|
|
134
|
+
- **New env var** → add to \`.env.example\` AND document in [docs/ENVIRONMENT.md](docs/ENVIRONMENT.md).
|
|
135
|
+
|
|
136
|
+
## Testing
|
|
137
|
+
|
|
138
|
+
- Unit tests live next to the code: \`foo.ts\` + \`foo.spec.ts\`.
|
|
139
|
+
- Integration tests that touch the DB use a real Postgres (the \`docker-compose.local.yml\`
|
|
140
|
+
one) — **do not mock Prisma**. Mocks have hidden divergence from real query behavior.
|
|
141
|
+
- Run a single app's tests: \`pnpm --filter <app-name> test\`.
|
|
142
|
+
|
|
143
|
+
## Git & CI
|
|
144
|
+
|
|
145
|
+
- Default branch: \`main\`. Feature branches: \`feat/<short-name>\`, \`fix/<short-name>\`.
|
|
146
|
+
- Conventional commits (\`feat:\`, \`fix:\`, \`chore:\`, \`docs:\`, \`refactor:\`).
|
|
147
|
+
- CI runs \`type-check\`, \`lint\`, \`build\` on every PR (see \`.github/workflows/\`).
|
|
148
|
+
- **Never** commit \`.env\`, \`.env.local\`, \`*.pem\`, or anything under \`packages/database/prisma/migrations\`
|
|
149
|
+
that wasn't generated by \`pnpm db:migrate\`.
|
|
150
|
+
|
|
151
|
+
## Don't
|
|
152
|
+
|
|
153
|
+
- Don't run \`npm install\` or \`yarn\` — use \`pnpm\`.
|
|
154
|
+
- Don't bypass Turbo by \`cd\`-ing into an app and running raw scripts unless debugging.
|
|
155
|
+
- Don't add a new top-level dependency to multiple apps individually — put it in the
|
|
156
|
+
appropriate \`packages/shared-*\` and re-export.
|
|
157
|
+
- Don't \`prisma db push\` against a shared/dev database — always go through \`prisma migrate\`.
|
|
158
|
+
- Don't edit generated files (\`packages/database/node_modules/.prisma\`, \`.next/\`, \`dist/\`).
|
|
159
|
+
- Don't skip git hooks (\`--no-verify\`) without an explicit reason in the commit body.
|
|
160
|
+
|
|
161
|
+
## When you finish a task
|
|
162
|
+
|
|
163
|
+
1. \`pnpm type-check && pnpm lint\` — both must pass.
|
|
164
|
+
2. If you changed the Prisma schema: \`pnpm db:generate\` and create a migration.
|
|
165
|
+
3. If you added an env var: update \`.env.example\` and \`docs/ENVIRONMENT.md\`.
|
|
166
|
+
4. If you added/removed an app: update the Dockerfile and all three compose files.
|
|
167
|
+
|
|
168
|
+
## Pointers
|
|
169
|
+
|
|
170
|
+
- Environment variables: @docs/ENVIRONMENT.md
|
|
171
|
+
- README (human-facing setup): @README.md
|
|
172
|
+
- Prisma schema: @packages/database/prisma/schema.prisma
|
|
173
|
+
- Turbo pipeline: @turbo.json
|
|
174
|
+
|
|
175
|
+
App-specific context lives in per-app \`CLAUDE.md\` files (e.g.
|
|
176
|
+
\`apps/backend/<name>/CLAUDE.md\`). Claude Code auto-discovers them when working inside
|
|
177
|
+
that subtree.
|
|
178
|
+
`;
|
|
179
|
+
|
|
180
|
+
await fs.writeFile(path.join(targetDir, 'CLAUDE.md'), content);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function createBackendClaudeMd(
|
|
184
|
+
targetDir: string,
|
|
185
|
+
name: string
|
|
186
|
+
): Promise<void> {
|
|
187
|
+
const dir = path.join(targetDir, 'apps', 'backend', name);
|
|
188
|
+
await fs.ensureDir(dir);
|
|
189
|
+
|
|
190
|
+
const content = `# ${name} (backend)
|
|
191
|
+
|
|
192
|
+
NestJS service. See the root [CLAUDE.md](../../../CLAUDE.md) for monorepo-wide rules;
|
|
193
|
+
this file only documents what is specific to \`${name}\`.
|
|
194
|
+
|
|
195
|
+
## Layout
|
|
196
|
+
|
|
197
|
+
\`\`\`
|
|
198
|
+
src/
|
|
199
|
+
├── main.ts # bootstrap — keep this minimal
|
|
200
|
+
├── app.module.ts # root module
|
|
201
|
+
├── modules/ # feature modules (one folder per domain concept)
|
|
202
|
+
│ └── <feature>/
|
|
203
|
+
│ ├── <feature>.controller.ts
|
|
204
|
+
│ ├── <feature>.service.ts
|
|
205
|
+
│ ├── <feature>.module.ts
|
|
206
|
+
│ └── dto/ # request/response shapes — prefer importing zod schemas
|
|
207
|
+
│ # from @repo/shared-common over redefining DTOs here
|
|
208
|
+
└── common/ # cross-cutting filters, guards, interceptors
|
|
209
|
+
\`\`\`
|
|
210
|
+
|
|
211
|
+
## Conventions
|
|
212
|
+
|
|
213
|
+
- One feature = one module. Don't put unrelated controllers in the same module.
|
|
214
|
+
- Services are injectable and stateless. Persistent state goes in Prisma or Redis.
|
|
215
|
+
- Use \`@repo/shared-backend\` guards/pipes — don't reinvent them here.
|
|
216
|
+
- Validate request bodies with the zod schemas in \`@repo/shared-common\`. Pair them with
|
|
217
|
+
the shared \`ZodValidationPipe\`.
|
|
218
|
+
- Health check at \`GET /health\` is required (the prod compose file probes it).
|
|
219
|
+
|
|
220
|
+
## Commands
|
|
221
|
+
|
|
222
|
+
\`\`\`bash
|
|
223
|
+
pnpm --filter ${name} dev # watch mode
|
|
224
|
+
pnpm --filter ${name} build
|
|
225
|
+
pnpm --filter ${name} test
|
|
226
|
+
pnpm --filter ${name} test:e2e
|
|
227
|
+
\`\`\`
|
|
228
|
+
|
|
229
|
+
## Don't
|
|
230
|
+
|
|
231
|
+
- Don't import from another backend app (\`apps/backend/<other>\`). Lift shared code into
|
|
232
|
+
\`packages/shared-backend\` instead.
|
|
233
|
+
- Don't bypass the global validation pipe — every controller input must be validated.
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
await fs.writeFile(path.join(dir, 'CLAUDE.md'), content);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function createFrontendClaudeMd(
|
|
240
|
+
targetDir: string,
|
|
241
|
+
name: string
|
|
242
|
+
): Promise<void> {
|
|
243
|
+
const dir = path.join(targetDir, 'apps', 'frontend', name);
|
|
244
|
+
await fs.ensureDir(dir);
|
|
245
|
+
|
|
246
|
+
const content = `# ${name} (frontend)
|
|
247
|
+
|
|
248
|
+
Next.js app (App Router). See the root [CLAUDE.md](../../../CLAUDE.md) for monorepo-wide
|
|
249
|
+
rules; this file documents only what is specific to \`${name}\`.
|
|
250
|
+
|
|
251
|
+
## Layout
|
|
252
|
+
|
|
253
|
+
\`\`\`
|
|
254
|
+
app/ # App Router routes
|
|
255
|
+
layout.tsx
|
|
256
|
+
page.tsx
|
|
257
|
+
(group)/... # route groups
|
|
258
|
+
components/ # ${name}-specific components
|
|
259
|
+
ui/ # presentational, no data fetching
|
|
260
|
+
features/ # feature components that fetch / mutate
|
|
261
|
+
lib/ # ${name}-specific helpers (API client, hooks)
|
|
262
|
+
\`\`\`
|
|
263
|
+
|
|
264
|
+
Reusable cross-app components live in \`@repo/shared-frontend\`. If you find yourself
|
|
265
|
+
copying a component to another frontend app, move it there.
|
|
266
|
+
|
|
267
|
+
## Conventions
|
|
268
|
+
|
|
269
|
+
- **Server Components by default.** Add \`'use client'\` only when you need state, effects,
|
|
270
|
+
or browser APIs.
|
|
271
|
+
- Data fetching: server components fetch directly; client components use the typed API
|
|
272
|
+
client from \`@repo/shared-frontend\`.
|
|
273
|
+
- Types and zod schemas come from \`@repo/shared-common\` — do not redefine API response
|
|
274
|
+
shapes here.
|
|
275
|
+
- All env vars referenced in client code must be prefixed \`NEXT_PUBLIC_\`. Anything else is
|
|
276
|
+
server-only.
|
|
277
|
+
|
|
278
|
+
## Commands
|
|
279
|
+
|
|
280
|
+
\`\`\`bash
|
|
281
|
+
pnpm --filter ${name} dev
|
|
282
|
+
pnpm --filter ${name} build
|
|
283
|
+
pnpm --filter ${name} start # serve the production build locally
|
|
284
|
+
pnpm --filter ${name} lint
|
|
285
|
+
\`\`\`
|
|
286
|
+
|
|
287
|
+
## Don't
|
|
288
|
+
|
|
289
|
+
- Don't import server-only modules (\`fs\`, \`@repo/database\`, \`@repo/shared-backend\`) into
|
|
290
|
+
client components — Next.js will fail the build.
|
|
291
|
+
- Don't fetch in a \`useEffect\` for data that could be fetched on the server.
|
|
292
|
+
- Don't hand-roll API types. Reuse zod schemas from \`@repo/shared-common\`.
|
|
293
|
+
`;
|
|
294
|
+
|
|
295
|
+
await fs.writeFile(path.join(dir, 'CLAUDE.md'), content);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function createSharedClaudeMd(targetDir: string): Promise<void> {
|
|
299
|
+
const dir = path.join(targetDir, 'packages');
|
|
300
|
+
await fs.ensureDir(dir);
|
|
301
|
+
|
|
302
|
+
const content = `# packages/
|
|
303
|
+
|
|
304
|
+
Shared workspace packages. See the root [CLAUDE.md](../CLAUDE.md) for monorepo rules.
|
|
305
|
+
|
|
306
|
+
## Which package does my code go in?
|
|
307
|
+
|
|
308
|
+
| Code uses... | Goes in |
|
|
309
|
+
| -------------------------------- | -------------------- |
|
|
310
|
+
| \`fs\`, \`process\`, NestJS, Prisma | \`shared-backend\` |
|
|
311
|
+
| \`react\`, \`window\`, \`document\` | \`shared-frontend\` |
|
|
312
|
+
| Pure TS, zod, no env coupling | \`shared-common\` |
|
|
313
|
+
| Prisma schema or DB client | \`database\` |
|
|
314
|
+
| tsconfig presets | \`tsconfig\` |
|
|
315
|
+
|
|
316
|
+
When in doubt, default to \`shared-common\` — it's the safest place because both sides
|
|
317
|
+
can import it.
|
|
318
|
+
|
|
319
|
+
## Conventions
|
|
320
|
+
|
|
321
|
+
- Every package exports its public API from \`src/index.ts\`. Don't let consumers
|
|
322
|
+
deep-import (\`@repo/foo/src/internal/x\`) — that's a leak.
|
|
323
|
+
- Package names are \`@repo/<folder>\`. Keep them in sync with the folder name.
|
|
324
|
+
- Adding a new package: copy the structure of an existing one, add it to
|
|
325
|
+
\`pnpm-workspace.yaml\` (already covered by the \`packages/*\` glob), run \`pnpm install\`.
|
|
326
|
+
|
|
327
|
+
## Database package
|
|
328
|
+
|
|
329
|
+
\`packages/database\` owns the Prisma schema and the generated client. After editing
|
|
330
|
+
\`schema.prisma\`:
|
|
331
|
+
|
|
332
|
+
\`\`\`bash
|
|
333
|
+
pnpm db:generate # regenerate the client (required before type-check passes)
|
|
334
|
+
pnpm db:migrate # create + apply a migration in dev
|
|
335
|
+
\`\`\`
|
|
336
|
+
|
|
337
|
+
Never run \`prisma db push\` against a shared database — it skips the migration history.
|
|
338
|
+
`;
|
|
339
|
+
|
|
340
|
+
await fs.writeFile(path.join(dir, 'CLAUDE.md'), content);
|
|
341
|
+
}
|