docguard-cli 0.5.1 → 0.6.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.
@@ -6,8 +6,11 @@
6
6
  */
7
7
 
8
8
  import { existsSync, readFileSync, writeFileSync, readdirSync, statSync, mkdirSync } from 'node:fs';
9
- import { resolve, join, extname, basename, relative } from 'node:path';
9
+ import { resolve, join, extname, basename, relative, dirname } from 'node:path';
10
10
  import { c } from '../docguard.mjs';
11
+ import { detectDocTools } from '../scanners/doc-tools.mjs';
12
+ import { scanRoutesDeep } from '../scanners/routes.mjs';
13
+ import { scanSchemasDeep, generateERDiagram } from '../scanners/schemas.mjs';
11
14
 
12
15
  const IGNORE_DIRS = new Set([
13
16
  'node_modules', '.git', '.next', 'dist', 'build', 'coverage',
@@ -33,10 +36,38 @@ export function runGenerate(projectDir, config, flags) {
33
36
  }
34
37
  console.log('');
35
38
 
36
- // ── 2. Scan Project Structure ──
39
+ // ── 2. Detect Existing Doc Tools ──
40
+ const docTools = detectDocTools(projectDir);
41
+ if (docTools._detected.length > 0) {
42
+ console.log(` ${c.bold}Detected Documentation Tools:${c.reset}`);
43
+ for (const tool of docTools._detected) {
44
+ const info = docTools[tool];
45
+ const details = info.config || info.path || info.middleware || '';
46
+ let extra = '';
47
+ if (tool === 'openapi' && info.endpoints) extra = ` — ${info.endpoints.length} endpoints, ${info.schemas?.length || 0} schemas`;
48
+ if (tool === 'storybook' && info.storyCount) extra = ` — ${info.storyCount} stories`;
49
+ console.log(` ${c.cyan}${tool}:${c.reset} ${details}${extra}`);
50
+ }
51
+ console.log('');
52
+ }
53
+
54
+ // ── 3. Scan Project Structure ──
37
55
  const scan = scanProject(projectDir);
38
56
 
39
- // ── 3. Generate Documents ──
57
+ // ── 4. Deep Scan Routes ──
58
+ const deepRoutes = scanRoutesDeep(projectDir, stack, docTools);
59
+ if (deepRoutes.length > 0) {
60
+ console.log(` ${c.bold}Route Scanning:${c.reset} ${deepRoutes.length} endpoints found (source: ${deepRoutes[0]?.source || 'code'})`);
61
+ }
62
+
63
+ // ── 5. Deep Scan Schemas ──
64
+ const deepSchemas = scanSchemasDeep(projectDir, stack, docTools);
65
+ if (deepSchemas.entities.length > 0) {
66
+ console.log(` ${c.bold}Schema Scanning:${c.reset} ${deepSchemas.entities.length} entities, ${deepSchemas.relationships.length} relationships (source: ${deepSchemas.source})`);
67
+ }
68
+ console.log('');
69
+
70
+ // ── 6. Generate Documents ──
40
71
  const docsDir = resolve(projectDir, 'docs-canonical');
41
72
  if (!existsSync(docsDir)) {
42
73
  mkdirSync(docsDir, { recursive: true });
@@ -45,12 +76,18 @@ export function runGenerate(projectDir, config, flags) {
45
76
  let created = 0;
46
77
  let skipped = 0;
47
78
 
48
- // Generate ARCHITECTURE.md
49
- const archResult = generateArchitecture(projectDir, config, stack, scan, flags);
79
+ // Generate ARCHITECTURE.md (arc42-aligned)
80
+ const archResult = generateArchitecture(projectDir, config, stack, scan, flags, docTools);
50
81
  if (archResult) { created++; } else { skipped++; }
51
82
 
52
- // Generate DATA-MODEL.md
53
- const dataResult = generateDataModel(projectDir, config, stack, scan, flags);
83
+ // Generate API-REFERENCE.md (NEW — from deep route scanning)
84
+ if (deepRoutes.length > 0) {
85
+ const apiResult = generateApiReference(projectDir, config, stack, deepRoutes, flags);
86
+ if (apiResult) { created++; } else { skipped++; }
87
+ }
88
+
89
+ // Generate DATA-MODEL.md (enhanced with deep schema scanning)
90
+ const dataResult = generateDataModel(projectDir, config, stack, scan, flags, deepSchemas);
54
91
  if (dataResult) { created++; } else { skipped++; }
55
92
 
56
93
  // Generate ENVIRONMENT.md
@@ -65,13 +102,16 @@ export function runGenerate(projectDir, config, flags) {
65
102
  const secResult = generateSecurity(projectDir, config, stack, scan, flags);
66
103
  if (secResult) { created++; } else { skipped++; }
67
104
 
68
- // Generate root files
69
- const rootResults = generateRootFiles(projectDir, config, stack, scan, flags);
105
+ // Generate root files (AGENTS.md, CHANGELOG, DRIFT-LOG)
106
+ const rootResults = generateRootFiles(projectDir, config, stack, scan, flags, docTools);
70
107
  created += rootResults.created;
71
108
  skipped += rootResults.skipped;
72
109
 
73
110
  console.log(`\n${c.bold} ─────────────────────────────────────${c.reset}`);
74
111
  console.log(` ${c.green}Generated: ${created}${c.reset} Skipped: ${skipped} (already exist)`);
112
+ if (docTools._detected.length > 0) {
113
+ console.log(` ${c.dim}Leveraged: ${docTools._detected.join(', ')} (existing tools detected)${c.reset}`);
114
+ }
75
115
  console.log(`\n ${c.yellow}${c.bold}⚠️ Review all generated docs!${c.reset}`);
76
116
  console.log(` ${c.dim}Generated docs are a starting point — review and refine them.${c.reset}`);
77
117
  console.log(` ${c.dim}Run ${c.cyan}docguard score${c.dim} to check your CDD maturity.${c.reset}\n`);
@@ -278,7 +318,7 @@ function countFilesAndLines(dir, scan) {
278
318
 
279
319
  // ── Document Generators ────────────────────────────────────────────────────
280
320
 
281
- function generateArchitecture(dir, config, stack, scan, flags) {
321
+ function generateArchitecture(dir, config, stack, scan, flags, docTools) {
282
322
  const path = resolve(dir, 'docs-canonical/ARCHITECTURE.md');
283
323
  if (existsSync(path) && !flags.force) {
284
324
  console.log(` ${c.dim}⏭️ ARCHITECTURE.md (exists)${c.reset}`);
@@ -297,14 +337,30 @@ function generateArchitecture(dir, config, stack, scan, flags) {
297
337
  if (scan.components.length > 0) componentRows.push(`| UI Components | Frontend components | ${scan.components.length} files | |`);
298
338
  if (scan.middlewares.length > 0) componentRows.push(`| Middleware | Request processing | ${scan.middlewares.join(', ')} | |`);
299
339
 
340
+ // Storybook integration
341
+ if (docTools?.storybook?.found) {
342
+ componentRows.push(`| Storybook | UI component docs | .storybook/ (${docTools.storybook.storyCount || '?'} stories) | |`);
343
+ }
344
+
345
+ // Doc tools section
346
+ const docToolRows = [];
347
+ if (docTools?._detected?.length > 0) {
348
+ for (const tool of docTools._detected) {
349
+ const info = docTools[tool];
350
+ docToolRows.push(`| ${tool} | ${info.config || info.path || info.middleware || 'detected'} | Active |`);
351
+ }
352
+ }
353
+
300
354
  const content = `# Architecture
301
355
 
302
356
  <!-- docguard:version 0.1.0 -->
303
357
  <!-- docguard:status draft -->
304
358
  <!-- docguard:last-reviewed ${new Date().toISOString().split('T')[0]} -->
305
359
  <!-- docguard:generated true -->
360
+ <!-- docguard:standards arc42, C4 -->
306
361
 
307
362
  > **Auto-generated by DocGuard.** Review and refine this document.
363
+ > Follows [arc42](https://arc42.org) structure and [C4 Model](https://c4model.com) diagrams.
308
364
 
309
365
  | Metadata | Value |
310
366
  |----------|-------|
@@ -315,26 +371,105 @@ function generateArchitecture(dir, config, stack, scan, flags) {
315
371
 
316
372
  ---
317
373
 
318
- ## System Overview
374
+ ## 1. Introduction & Goals
375
+ <!-- arc42: §1 — Introduction and Goals -->
319
376
 
320
- <!-- TODO: Describe what this system does and who it's for -->
377
+ <!-- TODO: Describe what this system does, who it's for, and key quality goals -->
321
378
  ${config.projectName} is a ${stack.framework || stack.language || 'software'} application.
322
379
 
323
- ## Component Map
380
+ ### Quality Goals
381
+
382
+ | Priority | Quality Goal | Scenario |
383
+ |----------|-------------|----------|
384
+ | 1 | <!-- e.g. Performance --> | <!-- e.g. Response time < 200ms --> |
385
+ | 2 | <!-- e.g. Security --> | <!-- e.g. All endpoints authenticated --> |
386
+ | 3 | <!-- e.g. Maintainability --> | <!-- e.g. New feature in < 1 day --> |
387
+
388
+ ## 2. Constraints
389
+ <!-- arc42: §2 — Constraints -->
390
+
391
+ | Type | Constraint | Background |
392
+ |------|-----------|------------|
393
+ | Technical | ${stack.language || 'TBD'} | Primary language |
394
+ | Technical | ${stack.framework || 'TBD'} | Framework |
395
+ | Infrastructure | ${stack.hosting || 'TBD'} | Hosting provider |
396
+
397
+ ## 3. Context & Scope
398
+ <!-- arc42: §3 — Context and Scope (C4 Level 1: System Context) -->
399
+
400
+ \\\`\\\`\\\`mermaid
401
+ graph TD
402
+ U[Users/Clients] --> S[${config.projectName}]
403
+ S --> DB[(${stack.database || 'Database'})]
404
+ S --> EXT[External Services]
405
+ \\\`\\\`\\\`
406
+
407
+ ## 4. Solution Strategy
408
+ <!-- arc42: §4 — Solution Strategy -->
409
+
410
+ See \\\`docs-canonical/ADR.md\\\` for architecture decision records.
411
+
412
+ ## 5. Building Block View
413
+ <!-- arc42: §5 — Building Block View (C4 Level 2: Container) -->
324
414
 
325
415
  | Component | Responsibility | Location | Tests |
326
416
  |-----------|---------------|----------|-------|
327
- ${componentRows.join('\n') || '| <!-- Add components --> | | | |'}
417
+ ${componentRows.join('\\n') || '| <!-- Add components --> | | | |'}
328
418
 
329
- ## Tech Stack
419
+ \\\`\\\`\\\`mermaid
420
+ graph TD
421
+ A[Client] --> B[${stack.framework || 'API'}]
422
+ B --> C[Services]
423
+ C --> D[${stack.database || 'Database'}]
424
+ ${scan.middlewares.length > 0 ? 'A --> M[Middleware] --> B' : ''}
425
+ ${scan.components.length > 0 ? 'A --> UI[UI Components]' : ''}
426
+ \\\`\\\`\\\`
427
+
428
+ ## 6. Runtime View
429
+ <!-- arc42: §6 — Runtime View -->
430
+
431
+ \\\`\\\`\\\`mermaid
432
+ sequenceDiagram
433
+ participant C as Client
434
+ participant A as ${stack.framework || 'API'}
435
+ participant S as Service
436
+ participant D as ${stack.database || 'DB'}
437
+ C->>A: Request
438
+ A->>S: Process
439
+ S->>D: Query
440
+ D-->>S: Result
441
+ S-->>A: Response
442
+ A-->>C: JSON
443
+ \\\`\\\`\\\`
444
+
445
+ ## 7. Deployment View
446
+ <!-- arc42: §7 — Deployment View -->
447
+
448
+ See \\\`docs-canonical/DEPLOYMENT.md\\\` for details.
449
+
450
+ | Environment | Infrastructure | URL |
451
+ |-------------|---------------|-----|
452
+ | Development | localhost | http://localhost:3000 |
453
+ | Staging | ${stack.hosting || 'TBD'} | <!-- TODO --> |
454
+ | Production | ${stack.hosting || 'TBD'} | <!-- TODO --> |
455
+
456
+ ## 8. Crosscutting Concepts
457
+ <!-- arc42: §8 — Crosscutting Concepts -->
458
+
459
+ ### Tech Stack
330
460
 
331
461
  | Category | Technology | Version | License |
332
462
  |----------|-----------|---------|---------|
333
463
  ${techRows || '| <!-- Add technologies --> | | | |'}
464
+ ${docToolRows.length > 0 ? `
465
+ ### Documentation Tools
334
466
 
335
- ## Layer Boundaries
467
+ | Tool | Config | Status |
468
+ |------|--------|--------|
469
+ ${docToolRows.join('\\n')}
470
+ ` : ''}
336
471
 
337
- <!-- TODO: Define which layers can import from which -->
472
+ ### Layer Boundaries
338
473
 
339
474
  | Layer | Can Import From | Cannot Import From |
340
475
  |-------|----------------|-------------------|
@@ -342,14 +477,127 @@ ${scan.routes.length > 0 ? '| Routes/Handlers | Services, Middleware | Models (d
342
477
  ${scan.services.length > 0 ? '| Services | Repositories, Utils | Routes |' : ''}
343
478
  ${scan.models.length > 0 ? '| Models/Repositories | Utils | Services, Routes |' : ''}
344
479
 
345
- ## Diagrams
480
+ ## 9. Architecture Decisions
481
+ <!-- arc42: §9 — Architecture Decisions -->
346
482
 
347
- \`\`\`mermaid
348
- graph TD
349
- A[Client] --> B[${stack.framework || 'API'}]
350
- B --> C[Services]
351
- C --> D[${stack.database || 'Database'}]
352
- \`\`\`
483
+ See \\\`docs-canonical/ADR.md\\\` for the full decision log.
484
+
485
+ ## 10. Quality Requirements
486
+ <!-- arc42: §10 — Quality Requirements -->
487
+
488
+ See \\\`docs-canonical/TEST-SPEC.md\\\` for test requirements and coverage targets.
489
+
490
+ ## 11. Risks & Technical Debt
491
+ <!-- arc42: §11 — Risk Assessment and Technical Debt -->
492
+
493
+ See \\\`DRIFT-LOG.md\\\` for documented deviations from canonical specs.
494
+ See \\\`docs-canonical/KNOWN-GOTCHAS.md\\\` for known issues.
495
+
496
+ ## 12. Glossary
497
+ <!-- arc42: §12 — Glossary -->
498
+
499
+ | Term | Definition |
500
+ |------|-----------|
501
+ | CDD | Canonical-Driven Development — documentation as the source of truth |
502
+ | Canonical Doc | A specification document that defines system behavior |
503
+ | Drift | Conscious deviation from canonical documentation |
504
+
505
+ ---
506
+
507
+ ## Revision History
508
+
509
+ | Version | Date | Author | Changes |
510
+ |---------|------|--------|---------|
511
+ | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (arc42 + C4 aligned) |
512
+ `;
513
+
514
+ writeFileSync(path, content, 'utf-8');
515
+ console.log(` ${c.green}✅ ARCHITECTURE.md${c.reset} (arc42 §1-§12, ${componentRows.length} components, ${Object.values(stack).filter(Boolean).length} tech)`);
516
+ return true;
517
+ }
518
+
519
+ // ── API Reference Generator (NEW — from deep route scanning) ───────────────
520
+
521
+ function generateApiReference(dir, config, stack, deepRoutes, flags) {
522
+ const path = resolve(dir, 'docs-canonical/API-REFERENCE.md');
523
+ if (existsSync(path) && !flags.force) {
524
+ console.log(` ${c.dim}⏭️ API-REFERENCE.md (exists)${c.reset}`);
525
+ return false;
526
+ }
527
+
528
+ // Group routes by resource (first path segment after /api/)
529
+ const groups = {};
530
+ for (const route of deepRoutes) {
531
+ const parts = route.path.split('/').filter(Boolean);
532
+ const resource = parts[1] || parts[0] || 'root';
533
+ if (!groups[resource]) groups[resource] = [];
534
+ groups[resource].push(route);
535
+ }
536
+
537
+ // Build endpoint table
538
+ const endpointRows = deepRoutes
539
+ .sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method))
540
+ .map(r => `| \`${r.method}\` | \`${r.path}\` | ${r.handler || '—'} | ${r.auth ? '🔒' : '🔓'} | ${r.description || '—'} |`)
541
+ .join('\n');
542
+
543
+ // Build per-resource sections
544
+ const resourceSections = Object.entries(groups)
545
+ .sort(([a], [b]) => a.localeCompare(b))
546
+ .map(([resource, routes]) => {
547
+ const routeDetails = routes.map(r => `#### ${r.method} \`${r.path}\`
548
+
549
+ > Source: \`${r.file}\`${r.source ? ` (${r.source})` : ''}
550
+
551
+ - **Auth:** ${r.auth ? 'Required' : 'None'}
552
+ - **Handler:** ${r.handler || '—'}
553
+ ${r.description ? `- **Description:** ${r.description}` : ''}
554
+
555
+ | Parameter | In | Type | Required | Description |
556
+ |-----------|-----|------|:--------:|-------------|
557
+ | <!-- TODO --> | | | | |
558
+
559
+ | Status | Response |
560
+ |--------|----------|
561
+ | 200 | Success |
562
+ | 400 | Bad Request |
563
+ | 401 | Unauthorized |
564
+ `).join('\n');
565
+
566
+ return `### ${resource.charAt(0).toUpperCase() + resource.slice(1)}
567
+
568
+ ${routeDetails}`;
569
+ }).join('\n---\n\n');
570
+
571
+ const content = `# API Reference
572
+
573
+ <!-- docguard:version 0.1.0 -->
574
+ <!-- docguard:status draft -->
575
+ <!-- docguard:last-reviewed ${new Date().toISOString().split('T')[0]} -->
576
+ <!-- docguard:generated true -->
577
+
578
+ > **Auto-generated by DocGuard.** Review and refine this document.
579
+
580
+ | Metadata | Value |
581
+ |----------|-------|
582
+ | **Status** | ![Status](https://img.shields.io/badge/status-draft-yellow) |
583
+ | **Base URL** | \`http://localhost:3000\` |
584
+ | **Auth** | <!-- TODO: Describe auth mechanism --> |
585
+ | **Total Endpoints** | ${deepRoutes.length} |
586
+ | **Source** | ${deepRoutes[0]?.source || 'code scan'} |
587
+
588
+ ---
589
+
590
+ ## Endpoints Summary
591
+
592
+ | Method | Path | Handler | Auth | Description |
593
+ |--------|------|---------|:----:|-------------|
594
+ ${endpointRows}
595
+
596
+ ---
597
+
598
+ ## Endpoint Details
599
+
600
+ ${resourceSections}
353
601
 
354
602
  ---
355
603
 
@@ -357,49 +605,101 @@ graph TD
357
605
 
358
606
  | Version | Date | Author | Changes |
359
607
  |---------|------|--------|---------|
360
- | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated from codebase scan |
608
+ | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${deepRoutes.length} endpoints from ${deepRoutes[0]?.source || 'code'}) |
361
609
  `;
362
610
 
363
611
  writeFileSync(path, content, 'utf-8');
364
- console.log(` ${c.green}✅ ARCHITECTURE.md${c.reset} (${componentRows.length} components, ${Object.values(stack).filter(Boolean).length} tech)`);
612
+ console.log(` ${c.green}✅ API-REFERENCE.md${c.reset} (${deepRoutes.length} endpoints, ${Object.keys(groups).length} resources)`);
365
613
  return true;
366
614
  }
367
615
 
368
- function generateDataModel(dir, config, stack, scan, flags) {
616
+ // ── Enhanced Data Model Generator ──────────────────────────────────────────
617
+
618
+ function generateDataModel(dir, config, stack, scan, flags, deepSchemas) {
369
619
  const path = resolve(dir, 'docs-canonical/DATA-MODEL.md');
370
620
  if (existsSync(path) && !flags.force) {
371
621
  console.log(` ${c.dim}⏭️ DATA-MODEL.md (exists)${c.reset}`);
372
622
  return false;
373
623
  }
374
624
 
375
- // Parse model files for entity names
376
- const entities = [];
377
- for (const modelFile of scan.models) {
378
- const name = basename(modelFile, extname(modelFile));
379
- if (name !== 'index' && name !== 'schema') {
380
- entities.push({
381
- name: name.charAt(0).toUpperCase() + name.slice(1),
382
- file: modelFile,
383
- });
384
- }
385
- }
625
+ // Use deep schemas if available, fallback to basic scan
626
+ let entities = [];
627
+ let relationships = [];
628
+ let schemaSource = 'file scan';
386
629
 
387
- // Check for Prisma schema
388
- const prismaPath = resolve(dir, 'prisma/schema.prisma');
389
- if (existsSync(prismaPath)) {
390
- const prismaContent = readFileSync(prismaPath, 'utf-8');
391
- const modelRegex = /model\s+(\w+)\s*\{/g;
392
- let match;
393
- while ((match = modelRegex.exec(prismaContent)) !== null) {
394
- if (!entities.find(e => e.name.toLowerCase() === match[1].toLowerCase())) {
395
- entities.push({ name: match[1], file: 'prisma/schema.prisma' });
630
+ if (deepSchemas && deepSchemas.entities.length > 0) {
631
+ entities = deepSchemas.entities;
632
+ relationships = deepSchemas.relationships;
633
+ schemaSource = deepSchemas.source;
634
+ } else {
635
+ // Fallback: basic entity detection from file names
636
+ for (const modelFile of scan.models) {
637
+ const name = basename(modelFile, extname(modelFile));
638
+ if (name !== 'index' && name !== 'schema') {
639
+ entities.push({
640
+ name: name.charAt(0).toUpperCase() + name.slice(1),
641
+ fields: [],
642
+ file: modelFile,
643
+ source: 'file',
644
+ });
396
645
  }
397
646
  }
398
647
  }
399
648
 
400
- const entityRows = entities.map(e =>
401
- `| ${e.name} | ${stack.database || 'TBD'} | ${e.name.toLowerCase()}Id | See \`${e.file}\` |`
402
- ).join('\n');
649
+ // Build entity summary table
650
+ const entityRows = entities
651
+ .filter(e => e.source !== 'prisma-enum')
652
+ .map(e => {
653
+ const pk = e.fields?.find(f => f.primaryKey);
654
+ return `| ${e.name} | ${stack.database || 'TBD'} | ${pk ? pk.name : e.name.toLowerCase() + 'Id'} | ${e.file || '—'} | ${e.fields?.length || 0} fields |`;
655
+ }).join('\n');
656
+
657
+ // Build detailed entity sections
658
+ const entitySections = entities
659
+ .filter(e => e.source !== 'prisma-enum')
660
+ .map(e => {
661
+ if (!e.fields || e.fields.length === 0) {
662
+ return `### ${e.name}
663
+
664
+ > Source: \`${e.file || 'unknown'}\`
665
+
666
+ | Field | Type | Required | Default | Constraints | Description |
667
+ |-------|------|----------|---------|-------------|-------------|
668
+ | <!-- TODO: Fill in fields --> | | | | | |
669
+ `;
670
+ }
671
+ const fieldRows = e.fields.map(f =>
672
+ `| ${f.name} | ${f.type} | ${f.required ? '✓' : '✗'} | ${f.default || '—'} | ${f.primaryKey ? 'PK' : ''}${f.unique ? ' UK' : ''} | ${f.description || ''} |`
673
+ ).join('\n');
674
+
675
+ return `### ${e.name}
676
+
677
+ > Source: \`${e.file || 'unknown'}\` (${e.source || 'detected'})
678
+
679
+ | Field | Type | Required | Default | Constraints | Description |
680
+ |-------|------|:--------:|---------|-------------|-------------|
681
+ ${fieldRows}
682
+ `;
683
+ }).join('\n');
684
+
685
+ // Build enum sections (if Prisma enums found)
686
+ const enums = entities.filter(e => e.source === 'prisma-enum');
687
+ const enumSection = enums.length > 0 ? `## Enums
688
+
689
+ ${enums.map(e => `### ${e.name}
690
+
691
+ | Value |
692
+ |-------|
693
+ ${e.fields.map(f => `| ${f.name} |`).join('\n')}
694
+ `).join('\n')}` : '';
695
+
696
+ // Build relationship table
697
+ const relRows = relationships.length > 0
698
+ ? relationships.map(r => `| ${r.from} | ${r.to} | ${r.type} | ${r.field} | — |`).join('\n')
699
+ : '| <!-- No relationships detected --> | | | | |';
700
+
701
+ // Generate mermaid ER diagram
702
+ const erDiagram = generateERDiagram(entities, relationships);
403
703
 
404
704
  const content = `# Data Model
405
705
 
@@ -416,35 +716,43 @@ function generateDataModel(dir, config, stack, scan, flags) {
416
716
  | **Version** | \`0.1.0\` |
417
717
  | **Database** | ${stack.database || 'TBD'} |
418
718
  | **ORM** | ${stack.orm || 'None detected'} |
719
+ | **Schema Source** | ${schemaSource} |
720
+ | **Entities** | ${entities.filter(e => e.source !== 'prisma-enum').length} |
721
+ | **Relationships** | ${relationships.length} |
419
722
 
420
723
  ---
421
724
 
422
- ## Entities
725
+ ## Entity Summary
423
726
 
424
- | Entity | Storage | Primary Key | Description |
425
- |--------|---------|-------------|-------------|
426
- ${entityRows || '| <!-- No models detected --> | | | |'}
727
+ | Entity | Storage | Primary Key | Source | Fields |
728
+ |--------|---------|-------------|--------|--------|
729
+ ${entityRows || '| <!-- No models detected --> | | | | |'}
427
730
 
428
- ${entities.map(e => `### ${e.name}
731
+ ---
429
732
 
430
- > Source: \`${e.file}\`
733
+ ## Entity Details
431
734
 
432
- | Field | Type | Required | Default | Constraints | Description |
433
- |-------|------|----------|---------|-------------|-------------|
434
- | <!-- TODO: Fill in fields --> | | | | | |
435
- `).join('\n')}
735
+ ${entitySections}
736
+ ${enumSection}
436
737
 
437
738
  ## Relationships
438
739
 
439
740
  | From | To | Type | FK/Reference | Cascade |
440
741
  |------|-----|------|-------------|---------|
441
- | <!-- TODO --> | | | | |
742
+ ${relRows}
743
+ ${erDiagram ? `
744
+ ## Entity-Relationship Diagram
745
+
746
+ \`\`\`mermaid
747
+ ${erDiagram}
748
+ \`\`\`
749
+ ` : ''}
442
750
 
443
751
  ## Indexes
444
752
 
445
753
  | Table | Index Name | Fields | Type | Purpose |
446
754
  |-------|-----------|--------|------|---------|
447
- | <!-- TODO --> | | | | |
755
+ | <!-- TODO: Document indexes --> | | | | |
448
756
 
449
757
  ---
450
758
 
@@ -452,11 +760,11 @@ ${entities.map(e => `### ${e.name}
452
760
 
453
761
  | Version | Date | Author | Changes |
454
762
  |---------|------|--------|---------|
455
- | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${entities.length} entities found) |
763
+ | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${entities.length} entities, ${relationships.length} relationships from ${schemaSource}) |
456
764
  `;
457
765
 
458
766
  writeFileSync(path, content, 'utf-8');
459
- console.log(` ${c.green}✅ DATA-MODEL.md${c.reset} (${entities.length} entities detected)`);
767
+ console.log(` ${c.green}✅ DATA-MODEL.md${c.reset} (${entities.length} entities, ${relationships.length} relationships from ${schemaSource})`);
460
768
  return true;
461
769
  }
462
770
 
@@ -674,15 +982,18 @@ ${scan.envVars.filter(v => isSecretVar(v.name)).map(v =>
674
982
  return true;
675
983
  }
676
984
 
677
- function generateRootFiles(dir, config, stack, scan, flags) {
985
+ function generateRootFiles(dir, config, stack, scan, flags, docTools) {
678
986
  let created = 0;
679
987
  let skipped = 0;
680
988
 
681
- // AGENTS.md
989
+ // AGENTS.md (AGENTS.md Standard compliant)
682
990
  const agentsPath = resolve(dir, 'AGENTS.md');
683
991
  if (!existsSync(agentsPath) || flags.force) {
684
992
  const content = `# AI Agent Instructions — ${config.projectName}
685
993
 
994
+ <!-- Standard: https://agents.md -->
995
+ <!-- Generated by DocGuard — AGENTS.md standard compliant -->
996
+
686
997
  > This project follows **Canonical-Driven Development (CDD)**.
687
998
  > Documentation is the source of truth. Read before coding.
688
999
 
@@ -690,10 +1001,11 @@ function generateRootFiles(dir, config, stack, scan, flags) {
690
1001
 
691
1002
  1. **Read** \`docs-canonical/\` before suggesting changes
692
1003
  2. **Check** existing patterns in the codebase
693
- 3. **Confirm** your approach before writing code
694
- 4. **Implement** matching existing code style
695
- 5. **Log** any deviations in \`DRIFT-LOG.md\` with \`// DRIFT: reason\`
696
- 6. **Run DocGuard** after changes \`npx docguard guard\`
1004
+ 3. **Run** \`npx docguard-cli diagnose\` to see what needs fixing
1005
+ 4. **Confirm** your approach before writing code
1006
+ 5. **Implement** matching existing code style
1007
+ 6. **Log** any deviations in \`DRIFT-LOG.md\` with \`// DRIFT: reason\`
1008
+ 7. **Verify** with \`npx docguard-cli guard\` — all checks must pass
697
1009
 
698
1010
  ## Project Stack
699
1011
 
@@ -703,30 +1015,68 @@ ${Object.entries(stack).filter(([, v]) => v).map(([k, v]) => `- **${k}**: ${v}`)
703
1015
 
704
1016
  | File | Purpose |
705
1017
  |------|---------|
706
- | \`docs-canonical/ARCHITECTURE.md\` | System design |
707
- | \`docs-canonical/DATA-MODEL.md\` | Database schemas |
1018
+ | \`docs-canonical/ARCHITECTURE.md\` | System design (arc42 aligned) |
1019
+ | \`docs-canonical/API-REFERENCE.md\` | API endpoint documentation |
1020
+ | \`docs-canonical/DATA-MODEL.md\` | Database schemas & entities |
708
1021
  | \`docs-canonical/SECURITY.md\` | Auth & secrets |
709
1022
  | \`docs-canonical/TEST-SPEC.md\` | Test requirements |
710
1023
  | \`docs-canonical/ENVIRONMENT.md\` | Environment setup |
1024
+ | \`AGENTS.md\` | AI agent instructions (this file) |
711
1025
  | \`CHANGELOG.md\` | Change tracking |
712
1026
  | \`DRIFT-LOG.md\` | Documented deviations |
713
1027
 
714
- ## DocGuard Documentation Enforcement
1028
+ ## Permissions & Guardrails
1029
+
1030
+ > **IMPORTANT:** These limits apply to all AI agents working on this project.
1031
+
1032
+ ### Allowed
1033
+
1034
+ - Read any file in the repository
1035
+ - Modify files within \`src/\`, \`tests/\`, and \`docs-canonical/\`
1036
+ - Run test commands (\`npm test\`, \`npx docguard-cli guard\`)
1037
+ - Create new files in appropriate directories
1038
+
1039
+ ### Not Allowed
1040
+
1041
+ - Modify \`.env\` files or secrets
1042
+ - Push commits or create releases without explicit approval
1043
+ - Delete or rename canonical documentation files
1044
+ - Bypass DocGuard checks (\`docguard guard\` must pass)
1045
+ - Install new dependencies without approval
1046
+
1047
+ ### Safety Rules
1048
+
1049
+ - Never hardcode secrets, tokens, or API keys
1050
+ - Always validate inputs before processing
1051
+ - Never expose internal paths or stack traces to users
1052
+ - Run \`npx docguard-cli guard\` before every commit
1053
+
1054
+ ## Monorepo Support
1055
+
1056
+ <!-- If this is a monorepo, nested AGENTS.md files in subdirectories
1057
+ override these instructions for their scope. -->
1058
+
1059
+ | Scope | AGENTS.md Location |
1060
+ |-------|-------------------|
1061
+ | Root (default) | \`./AGENTS.md\` |
1062
+ | <!-- e.g. packages/api --> | <!-- packages/api/AGENTS.md --> |
1063
+
1064
+ ## DocGuard Commands
715
1065
 
716
1066
  \`\`\`bash
717
- npx docguard guard # Validate compliance
718
- npx docguard fix # Find issues with fix instructions
719
- npx docguard fix --format prompt # AI-ready fix prompt
720
- npx docguard fix --auto # Auto-fix missing files
721
- npx docguard score # CDD maturity score
1067
+ npx docguard-cli guard # Validate compliance
1068
+ npx docguard-cli diagnose # Identify issues + AI fix prompts
1069
+ npx docguard-cli fix --doc ARCH # Fix specific document
1070
+ npx docguard-cli score # CDD maturity score (0-100)
1071
+ npx docguard-cli generate # Generate docs from code
722
1072
  \`\`\`
723
1073
 
724
1074
  ### AI Agent Workflow (IMPORTANT)
725
1075
 
726
- 1. **Before work**: Run \`npx docguard guard\` — understand compliance state
727
- 2. **After changes**: Run \`npx docguard fix --format prompt\` — get fix instructions
1076
+ 1. **Before work**: Run \`npx docguard-cli guard\` — understand compliance state
1077
+ 2. **After changes**: Run \`npx docguard-cli diagnose\` — get fix instructions
728
1078
  3. **Fix issues**: Each issue has an \`ai_instruction\` — follow it exactly
729
- 4. **Verify**: Run \`npx docguard guard\` again — must pass before commit
1079
+ 4. **Verify**: Run \`npx docguard-cli guard\` again — must pass before commit
730
1080
  5. **Update CHANGELOG**: All changes need a changelog entry
731
1081
 
732
1082
  ## Rules
@@ -738,7 +1088,7 @@ npx docguard score # CDD maturity score
738
1088
  - Documentation changes must pass \`docguard guard\`
739
1089
  `;
740
1090
  writeFileSync(agentsPath, content, 'utf-8');
741
- console.log(` ${c.green}✅ AGENTS.md${c.reset}`);
1091
+ console.log(` ${c.green}✅ AGENTS.md${c.reset} (AGENTS.md standard compliant)`);
742
1092
  created++;
743
1093
  } else {
744
1094
  console.log(` ${c.dim}⏭️ AGENTS.md (exists)${c.reset}`);