create-sdd-project 0.3.0 → 0.3.3

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/lib/generator.js CHANGED
@@ -50,7 +50,22 @@ function generate(config) {
50
50
  removeBackendFiles(dest, config);
51
51
  }
52
52
 
53
- // 7. Remove AI tool config if single tool selected
53
+ // 7. Adapt agent/skill content for non-default backend stacks
54
+ if (config.projectType !== 'frontend') {
55
+ const bp = config.backendPreset || BACKEND_STACKS[0];
56
+ if (bp.orm && bp.orm !== 'Prisma') {
57
+ step(`Adapting agents for ${bp.orm}`);
58
+ adaptAgentsForStack(dest, bp, config);
59
+ }
60
+ }
61
+
62
+ // 7b. Clean documentation-standards and agent content based on project type
63
+ adaptDocStandards(dest, config);
64
+ if (config.projectType !== 'fullstack') {
65
+ adaptAgentContent(dest, config);
66
+ }
67
+
68
+ // 8. Remove AI tool config if single tool selected
54
69
  if (config.aiTools === 'claude') {
55
70
  step('Removing Gemini config (Claude only)');
56
71
  fs.rmSync(path.join(dest, '.gemini'), { recursive: true, force: true });
@@ -289,4 +304,127 @@ function collectNotes(config) {
289
304
  return notes;
290
305
  }
291
306
 
307
+ function adaptAgentsForStack(dest, bPreset, config) {
308
+ const ormReplacements = [
309
+ ['Prisma ORM, and PostgreSQL', `${bPreset.orm}${bPreset.db ? `, and ${bPreset.db}` : ''}`],
310
+ ['Repository implementations (Prisma)', `Repository implementations (${bPreset.orm})`],
311
+ ];
312
+
313
+ const toolDirs = [];
314
+ if (config.aiTools !== 'gemini') toolDirs.push('.claude');
315
+ if (config.aiTools !== 'claude') toolDirs.push('.gemini');
316
+
317
+ for (const dir of toolDirs) {
318
+ replaceInFile(path.join(dest, dir, 'agents', 'backend-developer.md'), ormReplacements);
319
+ replaceInFile(path.join(dest, dir, 'agents', 'backend-planner.md'), ormReplacements);
320
+ }
321
+ }
322
+
323
+ function adaptDocStandards(dest, config) {
324
+ const docStdPath = path.join(dest, 'ai-specs', 'specs', 'documentation-standards.mdc');
325
+ if (!fs.existsSync(docStdPath)) return;
326
+
327
+ const replacements = [];
328
+ if (config.projectType === 'backend') {
329
+ replacements.push([/\| `ai-specs\/specs\/frontend-standards\.mdc` \|[^\n]*\n/, '']);
330
+ replacements.push([/\| `docs\/specs\/ui-components\.md` \|[^\n]*\n/, '']);
331
+ replacements.push([/ - UI component changes → `docs\/specs\/ui-components\.md`\n/, '']);
332
+ } else if (config.projectType === 'frontend') {
333
+ replacements.push([/\| `ai-specs\/specs\/backend-standards\.mdc` \|[^\n]*\n/, '']);
334
+ replacements.push([/\| `docs\/specs\/api-spec\.yaml` \|[^\n]*\n/, '']);
335
+ }
336
+
337
+ if (replacements.length > 0) {
338
+ replaceInFile(docStdPath, replacements);
339
+ }
340
+ }
341
+
342
+ function adaptAgentContent(dest, config) {
343
+ const toolDirs = [];
344
+ if (config.aiTools !== 'gemini') toolDirs.push('.claude');
345
+ if (config.aiTools !== 'claude') toolDirs.push('.gemini');
346
+
347
+ if (config.projectType === 'backend') {
348
+ for (const dir of toolDirs) {
349
+ // spec-creator: remove Frontend Specifications section and UI output format
350
+ replaceInFile(path.join(dest, dir, 'agents', 'spec-creator.md'), [
351
+ [/### Frontend Specifications\n(?:- [^\n]*\n)+\n/, ''],
352
+ [/### For UI Changes\n```markdown\n(?:[^\n]*\n)*?```\n\n/, ''],
353
+ ['Data Model Changes, UI Changes, Edge Cases', 'Data Model Changes, Edge Cases'],
354
+ ]);
355
+ // production-code-validator: remove ui-components line
356
+ replaceInFile(path.join(dest, dir, 'agents', 'production-code-validator.md'), [
357
+ [/- Components exported\/used that are NOT listed in `docs\/specs\/ui-components\.md`\n/, ''],
358
+ ]);
359
+ // code-review-specialist: backend-only refs
360
+ replaceInFile(path.join(dest, dir, 'agents', 'code-review-specialist.md'), [
361
+ ['(`backend-standards.mdc` / `frontend-standards.mdc`)', '(`backend-standards.mdc`)'],
362
+ ['(`api-spec.yaml`, `ui-components.md`)', '(`api-spec.yaml`)'],
363
+ ]);
364
+ // qa-engineer: remove frontend refs
365
+ replaceInFile(path.join(dest, dir, 'agents', 'qa-engineer.md'), [
366
+ ['(`api-spec.yaml`, `ui-components.md`)', '(`api-spec.yaml`)'],
367
+ [/- Frontend: `cd frontend && npm test`\n/, ''],
368
+ [/- \*\*Frontend\*\*: Write tests for error states[^\n]*\n/, ''],
369
+ ]);
370
+ }
371
+ } else if (config.projectType === 'frontend') {
372
+ for (const dir of toolDirs) {
373
+ replaceInFile(path.join(dest, dir, 'agents', 'spec-creator.md'), [
374
+ [/### Backend Specifications\n(?:- [^\n]*\n)+\n/, ''],
375
+ [/### For API Changes\n```yaml\n(?:[^\n]*\n)*?```\n\n/, ''],
376
+ ]);
377
+ replaceInFile(path.join(dest, dir, 'agents', 'code-review-specialist.md'), [
378
+ ['(`backend-standards.mdc` / `frontend-standards.mdc`)', '(`frontend-standards.mdc`)'],
379
+ ['(`api-spec.yaml`, `ui-components.md`)', '(`ui-components.md`)'],
380
+ ]);
381
+ replaceInFile(path.join(dest, dir, 'agents', 'qa-engineer.md'), [
382
+ ['(`api-spec.yaml`, `ui-components.md`)', '(`ui-components.md`)'],
383
+ [/- Backend: `cd backend && npm test`\n/, ''],
384
+ [/- \*\*Backend\*\*: Write tests for error paths[^\n]*\n/, ''],
385
+ ]);
386
+ }
387
+ }
388
+
389
+ // Skills and templates: same cleanup
390
+ if (config.projectType === 'backend') {
391
+ for (const dir of toolDirs) {
392
+ replaceInFile(path.join(dest, dir, 'agents', 'spec-creator.md'), [
393
+ [/\(api-spec\.yaml, ui-components\.md\)/, '(api-spec.yaml)'],
394
+ ]);
395
+ replaceInFile(path.join(dest, dir, 'agents', 'production-code-validator.md'), [
396
+ [/,? components not in ui-components\.md/, ''],
397
+ ]);
398
+ replaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'SKILL.md'), [
399
+ [/,? `ui-components\.md`\)/, ')'],
400
+ [/- UI components → `docs\/specs\/ui-components\.md` \(MANDATORY\)\n/, ''],
401
+ ]);
402
+ replaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'references', 'ticket-template.md'), [
403
+ [/### UI Changes \(if applicable\)\n\n\[Components to add\/modify\. Reference `docs\/specs\/ui-components\.md`\.\]\n\n/, ''],
404
+ [' / `ui-components.md`', ''],
405
+ ]);
406
+ replaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'references', 'pr-template.md'), [
407
+ [' / ui-components.md', ''],
408
+ ]);
409
+ }
410
+ } else if (config.projectType === 'frontend') {
411
+ for (const dir of toolDirs) {
412
+ replaceInFile(path.join(dest, dir, 'agents', 'spec-creator.md'), [
413
+ [/\(api-spec\.yaml, ui-components\.md\)/, '(ui-components.md)'],
414
+ ]);
415
+ replaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'SKILL.md'), [
416
+ [/`api-spec\.yaml`,? /, ''],
417
+ [/- API endpoints → `docs\/specs\/api-spec\.yaml` \(MANDATORY\)\n/, ''],
418
+ ]);
419
+ replaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'references', 'ticket-template.md'), [
420
+ [/### API Changes \(if applicable\)\n\n\[Endpoints to add\/modify\. Reference[^\]]*\]\n\n/, ''],
421
+ ['`api-spec.yaml` / ', ''],
422
+ ]);
423
+ replaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'references', 'pr-template.md'), [
424
+ ['api-spec.yaml / ', ''],
425
+ ]);
426
+ }
427
+ }
428
+ }
429
+
292
430
  module.exports = { generate };
@@ -35,12 +35,15 @@ function generateInit(config) {
35
35
  step('Creating ai-specs/specs/ (4 standards files)');
36
36
  ensureDir(path.join(dest, 'ai-specs', 'specs'));
37
37
 
38
- // base-standards and documentation-standards: copy as-is
39
- copyFileIfNotExists(
40
- path.join(templateDir, 'ai-specs', 'specs', 'base-standards.mdc'),
41
- path.join(dest, 'ai-specs', 'specs', 'base-standards.mdc'),
42
- skipped
43
- );
38
+ // base-standards: adapt validation references based on detected stack
39
+ const baseStdPath = path.join(dest, 'ai-specs', 'specs', 'base-standards.mdc');
40
+ if (!fs.existsSync(baseStdPath)) {
41
+ const baseTemplate = fs.readFileSync(path.join(templateDir, 'ai-specs', 'specs', 'base-standards.mdc'), 'utf8');
42
+ const adaptedBase = adaptBaseStandards(baseTemplate, scan, config);
43
+ fs.writeFileSync(baseStdPath, adaptedBase, 'utf8');
44
+ } else {
45
+ skipped.push('ai-specs/specs/base-standards.mdc');
46
+ }
44
47
  copyFileIfNotExists(
45
48
  path.join(templateDir, 'ai-specs', 'specs', 'documentation-standards.mdc'),
46
49
  path.join(dest, 'ai-specs', 'specs', 'documentation-standards.mdc'),
@@ -174,6 +177,9 @@ function generateInit(config) {
174
177
  removeAgentFiles(dest, BACKEND_AGENTS, config);
175
178
  }
176
179
 
180
+ // 7b. Adapt agent/skill content to match detected stack
181
+ adaptCopiedFiles(dest, scan, config);
182
+
177
183
  // 8. Append to .gitignore
178
184
  appendGitignore(dest, skipped);
179
185
 
@@ -288,8 +294,288 @@ function safeDelete(filePath) {
288
294
  try { fs.unlinkSync(filePath); } catch { /* ignore */ }
289
295
  }
290
296
 
297
+ // --- Adapt Copied Agent/Skill Files ---
298
+
299
+ function replaceInCopiedFile(dest, relativePath, replacements) {
300
+ const filePath = path.join(dest, relativePath);
301
+ if (!fs.existsSync(filePath)) return;
302
+ let content = fs.readFileSync(filePath, 'utf8');
303
+ for (const [from, to] of replacements) {
304
+ content = content.replaceAll(from, to);
305
+ }
306
+ fs.writeFileSync(filePath, content, 'utf8');
307
+ }
308
+
309
+ function regexReplaceInFile(filePath, replacements) {
310
+ if (!fs.existsSync(filePath)) return;
311
+ let content = fs.readFileSync(filePath, 'utf8');
312
+ for (const [from, to] of replacements) {
313
+ if (from instanceof RegExp) {
314
+ content = content.replace(from, to);
315
+ } else {
316
+ content = content.replaceAll(from, to);
317
+ }
318
+ }
319
+ fs.writeFileSync(filePath, content, 'utf8');
320
+ }
321
+
322
+ function adaptCopiedFiles(dest, scan, config) {
323
+ const orm = scan.backend.orm || 'your ORM';
324
+ const db = scan.backend.db || 'your database';
325
+
326
+ // Common Zod → generic validation replacements (all agents + skills)
327
+ // Order: most specific first, catch-all last
328
+ const zodReplacements = [
329
+ ['Zod data schemas', 'validation schemas'],
330
+ ['Zod schemas', 'validation schemas'],
331
+ ];
332
+
333
+ // ORM/DB replacements for backend agents
334
+ const ormReplacements = scan.backend.orm && scan.backend.orm !== 'Prisma'
335
+ ? [
336
+ ['Prisma ORM, and PostgreSQL', `${orm}${db !== 'your database' ? `, and ${db}` : ''}`],
337
+ ['Repository implementations (Prisma)', `Repository implementations (${orm})`],
338
+ ]
339
+ : [];
340
+
341
+ // Apply to all AI tool directories
342
+ const toolDirs = [];
343
+ if (config.aiTools !== 'gemini') toolDirs.push('.claude');
344
+ if (config.aiTools !== 'claude') toolDirs.push('.gemini');
345
+
346
+ for (const dir of toolDirs) {
347
+ // Backend agents: Zod + ORM replacements
348
+ if (scan.backend.validation !== 'Zod') {
349
+ const backendAgentReplacements = [...zodReplacements, ...ormReplacements];
350
+ replaceInCopiedFile(dest, `${dir}/agents/backend-developer.md`, backendAgentReplacements);
351
+ replaceInCopiedFile(dest, `${dir}/agents/backend-planner.md`, backendAgentReplacements);
352
+ } else if (ormReplacements.length > 0) {
353
+ // Zod detected but different ORM — only ORM replacements
354
+ replaceInCopiedFile(dest, `${dir}/agents/backend-developer.md`, ormReplacements);
355
+ replaceInCopiedFile(dest, `${dir}/agents/backend-planner.md`, ormReplacements);
356
+ }
357
+
358
+ // Multi-purpose agents: Zod replacements only
359
+ if (scan.backend.validation !== 'Zod') {
360
+ replaceInCopiedFile(dest, `${dir}/agents/spec-creator.md`, zodReplacements);
361
+ replaceInCopiedFile(dest, `${dir}/agents/production-code-validator.md`, zodReplacements);
362
+ replaceInCopiedFile(dest, `${dir}/agents/database-architect.md`, zodReplacements);
363
+ }
364
+
365
+ // Skills: Zod replacements
366
+ if (scan.backend.validation !== 'Zod') {
367
+ replaceInCopiedFile(dest, `${dir}/skills/development-workflow/SKILL.md`, zodReplacements);
368
+ replaceInCopiedFile(dest, `${dir}/skills/development-workflow/references/ticket-template.md`, zodReplacements);
369
+ }
370
+ }
371
+
372
+ // Architecture adaptation: DDD-specific content in backend agents
373
+ const arch = scan.srcStructure ? scan.srcStructure.pattern : 'ddd';
374
+ if (arch !== 'ddd') {
375
+ for (const dir of toolDirs) {
376
+ // backend-planner: adapt header, exploration paths, implementation order, rules
377
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'backend-planner.md'), [
378
+ // Header: remove DDD reference, use generic
379
+ ['specializing in Domain-Driven Design (DDD) layered architecture with deep knowledge of',
380
+ 'specializing in layered architecture with deep knowledge of'],
381
+ // Exploration paths: replace DDD-specific with generic
382
+ [/4\. Read `shared\/src\/schemas\/` \(if exists\) for current .* data schemas\n/, ''],
383
+ [/5\. Explore `backend\/src\/domain\/` for existing entities and errors\n/,
384
+ '5. Explore the codebase for existing patterns, layer structure, and reusable code\n'],
385
+ [/6\. Explore `backend\/src\/application\/services\/` for existing services\n/, ''],
386
+ [/7\. Explore `backend\/src\/application\/validators\/` for existing validators\n/, ''],
387
+ [/8\. Explore `backend\/src\/infrastructure\/` for existing repositories\n/, ''],
388
+ // Implementation Order
389
+ ['following DDD layer order: Domain > Application > Infrastructure > Presentation > Tests',
390
+ 'following the layer order defined in backend-standards.mdc'],
391
+ // Rules
392
+ ['Follow DDD layer separation: Domain > Application > Infrastructure > Presentation',
393
+ 'Follow the layer separation defined in backend-standards.mdc'],
394
+ ]);
395
+ // backend-developer: adapt frontmatter, header, exploration, implementation order, rules
396
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'backend-developer.md'), [
397
+ ['follows DDD layered architecture',
398
+ 'follows layered architecture'],
399
+ ['specializing in Domain-Driven Design (DDD) with',
400
+ 'specializing in layered architecture with'],
401
+ [/4\. Read `shared\/src\/schemas\/` \(if exists\) for current .* data schemas\n/, ''],
402
+ // Implementation Order section: replace DDD layers with generic guidance
403
+ ['Follow the DDD layer order from the plan:',
404
+ 'Follow the layer order from the plan (see backend-standards.mdc for project layers):'],
405
+ [/1\. \*\*Domain Layer\*\*: Entities, value objects, repository interfaces, domain errors\n/,
406
+ '1. **Data Layer**: Models, database operations, data access\n'],
407
+ [/2\. \*\*Application Layer\*\*: Services, validators, DTOs\n/,
408
+ '2. **Business Logic Layer**: Controllers, services, external integrations\n'],
409
+ [/3\. \*\*Infrastructure Layer\*\*: Repository implementations \([^)]*\), external integrations\n/,
410
+ '3. **Presentation Layer**: Routes, handlers, middleware\n'],
411
+ [/4\. \*\*Presentation Layer\*\*: Controllers, routes, middleware\n/,
412
+ '4. **Integration Layer**: Wiring, configuration, server registration\n'],
413
+ // Rules
414
+ ['**ALWAYS** follow DDD layer separation',
415
+ '**ALWAYS** follow the layer separation defined in backend-standards.mdc'],
416
+ ['**ALWAYS** handle errors with custom domain error classes',
417
+ '**ALWAYS** handle errors following the patterns in backend-standards.mdc'],
418
+ // Documentation
419
+ [/- \*\*MANDATORY\*\*: If modifying a DB schema → update .* schemas in `shared\/src\/schemas\/` BEFORE continuing\n/, ''],
420
+ ]);
421
+ }
422
+ }
423
+
424
+ // Agent content: remove frontend/backend-specific references for single-stack projects
425
+ if (config.projectType === 'backend') {
426
+ for (const dir of toolDirs) {
427
+ // spec-creator: remove Frontend Specifications section and UI output format
428
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'spec-creator.md'), [
429
+ [/### Frontend Specifications\n(?:- [^\n]*\n)+\n/, ''],
430
+ [/### For UI Changes\n```markdown\n(?:[^\n]*\n)*?```\n\n/, ''],
431
+ ['Data Model Changes, UI Changes, Edge Cases', 'Data Model Changes, Edge Cases'],
432
+ ]);
433
+ // production-code-validator: remove ui-components line from Spec Drift
434
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'production-code-validator.md'), [
435
+ [/- Components exported\/used that are NOT listed in `docs\/specs\/ui-components\.md`\n/, ''],
436
+ ]);
437
+ // code-review-specialist: backend-only standards ref, remove ui-components
438
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'code-review-specialist.md'), [
439
+ ['(`backend-standards.mdc` / `frontend-standards.mdc`)', '(`backend-standards.mdc`)'],
440
+ ['(`api-spec.yaml`, `ui-components.md`)', '(`api-spec.yaml`)'],
441
+ ]);
442
+ // qa-engineer: remove ui-components from specs, remove frontend test command
443
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'qa-engineer.md'), [
444
+ ['(`api-spec.yaml`, `ui-components.md`)', '(`api-spec.yaml`)'],
445
+ [/- Frontend: `cd frontend && npm test`\n/, ''],
446
+ [/- \*\*Frontend\*\*: Write tests for error states[^\n]*\n/, ''],
447
+ ]);
448
+ }
449
+ } else if (config.projectType === 'frontend') {
450
+ for (const dir of toolDirs) {
451
+ // spec-creator: remove Backend Specifications section
452
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'spec-creator.md'), [
453
+ [/### Backend Specifications\n(?:- [^\n]*\n)+\n/, ''],
454
+ [/### For API Changes\n```yaml\n(?:[^\n]*\n)*?```\n\n/, ''],
455
+ ]);
456
+ // code-review-specialist: frontend-only standards ref
457
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'code-review-specialist.md'), [
458
+ ['(`backend-standards.mdc` / `frontend-standards.mdc`)', '(`frontend-standards.mdc`)'],
459
+ ['(`api-spec.yaml`, `ui-components.md`)', '(`ui-components.md`)'],
460
+ ]);
461
+ // qa-engineer: remove api-spec from specs, remove backend test command
462
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'qa-engineer.md'), [
463
+ ['(`api-spec.yaml`, `ui-components.md`)', '(`ui-components.md`)'],
464
+ [/- Backend: `cd backend && npm test`\n/, ''],
465
+ [/- \*\*Backend\*\*: Write tests for error paths[^\n]*\n/, ''],
466
+ ]);
467
+ }
468
+ }
469
+
470
+ // Skills and templates: remove frontend/backend-specific references
471
+ if (config.projectType === 'backend') {
472
+ for (const dir of toolDirs) {
473
+ // Gemini agents have different text patterns
474
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'spec-creator.md'), [
475
+ [/\(api-spec\.yaml, ui-components\.md\)/, '(api-spec.yaml)'],
476
+ ['Data Model Changes, UI Changes, Edge Cases', 'Data Model Changes, Edge Cases'],
477
+ ]);
478
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'production-code-validator.md'), [
479
+ [/,? components not in ui-components\.md/, ''],
480
+ ]);
481
+ // SKILL.md: remove ui-components references
482
+ regexReplaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'SKILL.md'), [
483
+ [/,? `ui-components\.md`\)/, ')'],
484
+ [/- UI components → `docs\/specs\/ui-components\.md` \(MANDATORY\)\n/, ''],
485
+ ]);
486
+ // ticket-template: remove UI Changes section, ui-components from checklists
487
+ regexReplaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'references', 'ticket-template.md'), [
488
+ [/### UI Changes \(if applicable\)\n\n\[Components to add\/modify\. Reference `docs\/specs\/ui-components\.md`\.\]\n\n/, ''],
489
+ [' / `ui-components.md`', ''],
490
+ ]);
491
+ // pr-template: remove ui-components from checklist
492
+ regexReplaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'references', 'pr-template.md'), [
493
+ [' / ui-components.md', ''],
494
+ ]);
495
+ }
496
+ } else if (config.projectType === 'frontend') {
497
+ for (const dir of toolDirs) {
498
+ regexReplaceInFile(path.join(dest, dir, 'agents', 'spec-creator.md'), [
499
+ [/\(api-spec\.yaml, ui-components\.md\)/, '(ui-components.md)'],
500
+ ]);
501
+ regexReplaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'SKILL.md'), [
502
+ [/`api-spec\.yaml`,? /, ''],
503
+ [/- API endpoints → `docs\/specs\/api-spec\.yaml` \(MANDATORY\)\n/, ''],
504
+ ]);
505
+ regexReplaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'references', 'ticket-template.md'), [
506
+ [/### API Changes \(if applicable\)\n\n\[Endpoints to add\/modify\. Reference[^\]]*\]\n\n/, ''],
507
+ ['`api-spec.yaml` / ', ''],
508
+ ]);
509
+ regexReplaceInFile(path.join(dest, dir, 'skills', 'development-workflow', 'references', 'pr-template.md'), [
510
+ ['api-spec.yaml / ', ''],
511
+ ]);
512
+ }
513
+ }
514
+
515
+ // documentation-standards.mdc: remove irrelevant rows based on project type
516
+ const docStdPath = path.join(dest, 'ai-specs', 'specs', 'documentation-standards.mdc');
517
+ if (fs.existsSync(docStdPath)) {
518
+ let content = fs.readFileSync(docStdPath, 'utf8');
519
+ if (config.projectType === 'backend') {
520
+ content = content.replace(/\| `ai-specs\/specs\/frontend-standards\.mdc` \|[^\n]*\n/, '');
521
+ content = content.replace(/\| `docs\/specs\/ui-components\.md` \|[^\n]*\n/, '');
522
+ content = content.replace(/ - UI component changes → `docs\/specs\/ui-components\.md`\n/, '');
523
+ } else if (config.projectType === 'frontend') {
524
+ content = content.replace(/\| `ai-specs\/specs\/backend-standards\.mdc` \|[^\n]*\n/, '');
525
+ content = content.replace(/\| `docs\/specs\/api-spec\.yaml` \|[^\n]*\n/, '');
526
+ }
527
+ fs.writeFileSync(docStdPath, content, 'utf8');
528
+ }
529
+ }
530
+
291
531
  // --- Standards Adaptation ---
292
532
 
533
+ function adaptBaseStandards(template, scan, config) {
534
+ let content = template;
535
+
536
+ // Adapt validation recommendation based on detected library
537
+ const val = scan.backend.validation;
538
+ if (val && val !== 'Zod') {
539
+ content = content.replace(
540
+ 'Use runtime validation (Zod recommended).',
541
+ `Use runtime validation (${val}).`
542
+ );
543
+ } else if (!val) {
544
+ content = content.replace(
545
+ 'Use runtime validation (Zod recommended).',
546
+ 'Use runtime validation at system boundaries.'
547
+ );
548
+ }
549
+
550
+ // Adapt Shared Types Strategy section based on project structure
551
+ const hasSharedTypes = scan.frontend.detected && scan.backend.detected;
552
+ if (!hasSharedTypes) {
553
+ // Backend-only or frontend-only: remove entire Shared Types section
554
+ content = content.replace(
555
+ /## 5\. Shared Types Strategy\n\n<!-- CONFIG:.*?-->\n\n[\s\S]*?(?=\n## 6\.)/,
556
+ '## 5. Shared Types Strategy\n\n_Not applicable — single-stack project._\n\n'
557
+ );
558
+ } else if (val !== 'Zod') {
559
+ // Fullstack but not using Zod (or no validation detected): generalize the section
560
+ content = content.replace(
561
+ /## 5\. Shared Types Strategy\n\n<!-- CONFIG:.*?-->\n\nFor projects with backend \+ frontend, use a shared workspace for Zod schemas:\n\n```\n[\s\S]*?```\n\n\*\*Rules:\*\*\n[\s\S]*?(?=\n## 6\.)/,
562
+ `## 5. Shared Types Strategy\n\n<!-- CONFIG: Remove this section if your project has no shared types -->\n\nFor projects with backend + frontend, use a shared workspace for types:\n\n\`\`\`\nproject/\n├── shared/ ← @project/shared (npm workspace)\n│ └── src/types/ ← Shared TypeScript types\n├── backend/ ← imports @project/shared\n└── frontend/ ← imports @project/shared\n\`\`\`\n\n**Rules:**\n- Define shared types in the shared workspace\n- Both apps import from shared — never duplicate type definitions\n- Update shared types FIRST — both apps get changes automatically\n- Wire with npm workspaces + \`tsconfig.json\` paths\n\n`
563
+ );
564
+ }
565
+ // If Zod detected + fullstack: keep as-is (template default is correct)
566
+
567
+ // Remove frontend/backend references for single-stack projects
568
+ if (config.projectType === 'backend') {
569
+ content = content.replace(/ - \*\*Frontend\*\*: Update `docs\/specs\/ui-components\.md` first\.\n/, '');
570
+ content = content.replace('`backend-standards.mdc` / `frontend-standards.mdc`', '`backend-standards.mdc`');
571
+ } else if (config.projectType === 'frontend') {
572
+ content = content.replace(/ - \*\*Backend\*\*: Update `docs\/specs\/api-spec\.yaml` first\.\n/, '');
573
+ content = content.replace('`backend-standards.mdc` / `frontend-standards.mdc`', '`frontend-standards.mdc`');
574
+ }
575
+
576
+ return content;
577
+ }
578
+
293
579
  function adaptBackendStandards(template, scan) {
294
580
  let content = template;
295
581
 
@@ -367,6 +653,11 @@ function adaptBackendStandards(template, scan) {
367
653
  /## Database Patterns\n\n[\s\S]*?(?=\n## )/,
368
654
  `## Database Patterns\n\n<!-- TODO: Add database access patterns for your project. -->\n`
369
655
  );
656
+ } else if (scan.backend.orm === 'Mongoose') {
657
+ content = content.replace(
658
+ /## Database Patterns\n\n[\s\S]*?(?=\n## )/,
659
+ `## Database Patterns\n\n### Mongoose Best Practices\n- Define schemas in dedicated model files\n- Use TypeScript interfaces alongside Mongoose schemas for type safety\n- Use \`.lean()\` for read-only queries (returns plain objects, better performance)\n- Use \`.select()\` to limit returned fields on large documents\n- Add indexes for frequently queried fields (\`{ index: true }\` or compound \`schema.index()\`)\n- Use \`.populate()\` sparingly — avoid deep nesting, prefer manual joins for complex queries\n- Use middleware (pre/post hooks) for cross-cutting concerns (timestamps, audit logs)\n- Use transactions (\`session\`) for multi-document operations that must be atomic\n- Always handle \`CastError\` (invalid ObjectId) at the controller/middleware level\n\n### Schema Pattern\n\`\`\`typescript\nimport mongoose, { Schema, Document } from 'mongoose';\n\nexport interface IUser extends Document {\n email: string;\n name: string;\n role: 'user' | 'admin';\n}\n\nconst UserSchema = new Schema<IUser>({\n email: { type: String, required: true, unique: true, index: true },\n name: { type: String, required: true },\n role: { type: String, enum: ['user', 'admin'], default: 'user' },\n}, { timestamps: true });\n\nexport const User = mongoose.model<IUser>('User', UserSchema);\n\`\`\`\n\n`
660
+ );
370
661
  } else if (scan.backend.orm !== 'Prisma') {
371
662
  content = content.replace(
372
663
  /## Database Patterns\n\n[\s\S]*?(?=\n## )/,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-sdd-project",
3
- "version": "0.3.0",
3
+ "version": "0.3.3",
4
4
  "description": "Create a new SDD DevFlow project with AI-assisted development workflow",
5
5
  "bin": {
6
6
  "create-sdd-project": "bin/cli.js"
@@ -11,7 +11,7 @@
11
11
  project/
12
12
  ├── backend/ ← Backend (has its own package.json)
13
13
  ├── frontend/ ← Frontend (has its own package.json)
14
- ├── shared/ ← Shared Zod schemas (optional — see base-standards.mdc § Shared Types)
14
+ ├── shared/ ← Shared type schemas (optional — see base-standards.mdc § Shared Types)
15
15
  └── docs/ ← Documentation (no package.json)
16
16
  ```
17
17