docguard-cli 0.28.0 → 0.30.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.
Files changed (69) hide show
  1. package/README.es.md +102 -0
  2. package/README.md +80 -32
  3. package/README.pt-BR.md +101 -0
  4. package/STANDARD.md +20 -10
  5. package/cli/commands/agents.mjs +149 -0
  6. package/cli/commands/diff.mjs +6 -15
  7. package/cli/commands/generate.mjs +14 -1001
  8. package/cli/commands/guard.mjs +136 -8
  9. package/cli/commands/llms.mjs +67 -5
  10. package/cli/commands/mcp.mjs +263 -0
  11. package/cli/commands/memory.mjs +115 -0
  12. package/cli/commands/score.mjs +76 -12
  13. package/cli/commands/trace.mjs +364 -1
  14. package/cli/commands/verify.mjs +93 -6
  15. package/cli/docguard.mjs +42 -5
  16. package/cli/findings.mjs +511 -0
  17. package/cli/scanners/agent-readability.mjs +202 -0
  18. package/cli/scanners/instruction-audit.mjs +320 -0
  19. package/cli/scanners/semantic-claims.mjs +7 -1
  20. package/cli/scanners/speckit.mjs +443 -28
  21. package/cli/shared-ignore.mjs +148 -16
  22. package/cli/shared.mjs +45 -1
  23. package/cli/validators/api-surface.mjs +113 -26
  24. package/cli/validators/architecture.mjs +66 -43
  25. package/cli/validators/canonical-sync.mjs +59 -28
  26. package/cli/validators/changelog.mjs +41 -17
  27. package/cli/validators/cross-reference.mjs +28 -11
  28. package/cli/validators/doc-quality.mjs +78 -44
  29. package/cli/validators/docs-coverage.mjs +90 -63
  30. package/cli/validators/docs-diff.mjs +63 -64
  31. package/cli/validators/docs-sync.mjs +48 -33
  32. package/cli/validators/drift.mjs +40 -34
  33. package/cli/validators/environment.mjs +67 -27
  34. package/cli/validators/freshness.mjs +12 -5
  35. package/cli/validators/generated-staleness.mjs +26 -10
  36. package/cli/validators/metadata-sync.mjs +28 -25
  37. package/cli/validators/metrics-consistency.mjs +89 -47
  38. package/cli/validators/schema-sync.mjs +37 -32
  39. package/cli/validators/security.mjs +7 -20
  40. package/cli/validators/spec-kit.mjs +3 -0
  41. package/cli/validators/structure.mjs +58 -23
  42. package/cli/validators/surface-sync.mjs +34 -15
  43. package/cli/validators/test-spec.mjs +87 -29
  44. package/cli/validators/todo-tracking.mjs +83 -74
  45. package/cli/validators/traceability.mjs +67 -39
  46. package/cli/writers/doc-generators.mjs +853 -0
  47. package/cli/writers/generate-io.mjs +142 -0
  48. package/cli/writers/sarif.mjs +129 -0
  49. package/commands/docguard.fix.md +56 -53
  50. package/commands/docguard.guard.md +53 -47
  51. package/commands/docguard.review.md +49 -31
  52. package/docs/ai-integration.md +133 -134
  53. package/docs/commands.md +49 -3
  54. package/docs/configuration.md +38 -0
  55. package/docs/faq.md +15 -0
  56. package/extensions/spec-kit-docguard/extension.yml +1 -1
  57. package/extensions/spec-kit-docguard/skills/docguard-fix/SKILL.md +2 -2
  58. package/extensions/spec-kit-docguard/skills/docguard-guard/SKILL.md +2 -2
  59. package/extensions/spec-kit-docguard/skills/docguard-review/SKILL.md +2 -2
  60. package/extensions/spec-kit-docguard/skills/docguard-score/SKILL.md +2 -2
  61. package/extensions/spec-kit-docguard/skills/docguard-sync/SKILL.md +2 -2
  62. package/package.json +2 -1
  63. package/schemas/docguard-config.schema.json +28 -0
  64. package/templates/ci/gitlab-component.yml +90 -0
  65. package/templates/commands/docguard.fix.md +33 -10
  66. package/templates/commands/docguard.guard.md +40 -26
  67. package/templates/commands/docguard.init.md +23 -11
  68. package/templates/commands/docguard.review.md +25 -8
  69. package/templates/commands/docguard.update.md +14 -4
@@ -0,0 +1,853 @@
1
+ /**
2
+ * Document Generators — the seven doc emitters behind `docguard generate`:
3
+ * ARCHITECTURE, API-REFERENCE, DATA-MODEL, ENVIRONMENT, TEST-SPEC, SECURITY,
4
+ * plus the root files (AGENTS.md, CHANGELOG.md, DRIFT-LOG.md).
5
+ *
6
+ * v0.29 split: extracted verbatim from cli/commands/generate.mjs — pure code
7
+ * motion, zero behavior change.
8
+ */
9
+
10
+ import { existsSync } from 'node:fs';
11
+ import { resolve, basename, extname } from 'node:path';
12
+ import { c } from '../shared.mjs';
13
+ import { generateERDiagram } from '../scanners/schemas.mjs';
14
+ import { safeWrite, appendStandardsCitation } from './generate-io.mjs';
15
+
16
+ // ── Document Generators ────────────────────────────────────────────────────
17
+
18
+ export function generateArchitecture(dir, config, stack, scan, flags, docTools) {
19
+ const path = resolve(dir, 'docs-canonical/ARCHITECTURE.md');
20
+ if (existsSync(path) && !flags.force) {
21
+ console.log(` ${c.dim}⏭️ ARCHITECTURE.md (exists)${c.reset}`);
22
+ return false;
23
+ }
24
+
25
+ const techRows = Object.entries(stack)
26
+ .filter(([, v]) => v)
27
+ .map(([k, v]) => `| ${k.charAt(0).toUpperCase() + k.slice(1)} | ${v} | | |`)
28
+ .join('\n');
29
+
30
+ const componentRows = [];
31
+ if (scan.routes.length > 0) componentRows.push(`| API Routes | HTTP request handling | ${scan.routes.length > 3 ? scan.routes.slice(0, 3).join(', ') + '...' : scan.routes.join(', ')} | |`);
32
+ if (scan.services.length > 0) componentRows.push(`| Services | Business logic | ${scan.services.length > 3 ? scan.services.slice(0, 3).join(', ') + '...' : scan.services.join(', ')} | |`);
33
+ if (scan.models.length > 0) componentRows.push(`| Models | Data entities | ${scan.models.length > 3 ? scan.models.slice(0, 3).join(', ') + '...' : scan.models.join(', ')} | |`);
34
+ if (scan.components.length > 0) componentRows.push(`| UI Components | Frontend components | ${scan.components.length} files | |`);
35
+ if (scan.middlewares.length > 0) componentRows.push(`| Middleware | Request processing | ${scan.middlewares.join(', ')} | |`);
36
+
37
+ // Storybook integration
38
+ if (docTools?.storybook?.found) {
39
+ componentRows.push(`| Storybook | UI component docs | .storybook/ (${docTools.storybook.storyCount || '?'} stories) | |`);
40
+ }
41
+
42
+ // Doc tools section — always include DocGuard since it generated these docs
43
+ const docToolRows = ['| DocGuard | `.docguard.json` | Active |'];
44
+ if (docTools?._detected?.length > 0) {
45
+ for (const tool of docTools._detected) {
46
+ const info = docTools[tool];
47
+ docToolRows.push(`| ${tool} | ${info.config || info.path || info.middleware || 'detected'} | Active |`);
48
+ }
49
+ }
50
+
51
+ const content = `# Architecture
52
+
53
+ <!-- docguard:version 0.1.0 -->
54
+ <!-- docguard:status draft -->
55
+ <!-- docguard:last-reviewed ${new Date().toISOString().split('T')[0]} -->
56
+ <!-- docguard:generated true -->
57
+ <!-- docguard:standards arc42, C4 -->
58
+
59
+ > **Auto-generated by DocGuard.** Review and refine this document.
60
+ > Follows [arc42](https://arc42.org) structure and [C4 Model](https://c4model.com) diagrams.
61
+
62
+ | Metadata | Value |
63
+ |----------|-------|
64
+ | **Status** | ![Status](https://img.shields.io/badge/status-draft-yellow) |
65
+ | **Version** | \`0.1.0\` |
66
+ | **Last Updated** | ${new Date().toISOString().split('T')[0]} |
67
+ | **Project Size** | ${scan.totalFiles} files, ~${Math.round(scan.totalLines / 1000)}K lines |
68
+
69
+ ---
70
+
71
+ ## 1. Introduction & Goals
72
+ <!-- arc42: §1 — Introduction and Goals -->
73
+
74
+ <!-- TBD: Describe what this system does, who it's for, and key quality goals -->
75
+ ${config.projectName} is a ${stack.framework || stack.language || 'software'} application.
76
+
77
+ ### Quality Goals
78
+
79
+ | Priority | Quality Goal | Scenario |
80
+ |----------|-------------|----------|
81
+ | 1 | <!-- e.g. Performance --> | <!-- e.g. Response time < 200ms --> |
82
+ | 2 | <!-- e.g. Security --> | <!-- e.g. All endpoints authenticated --> |
83
+ | 3 | <!-- e.g. Maintainability --> | <!-- e.g. New feature in < 1 day --> |
84
+
85
+ ## 2. Constraints
86
+ <!-- arc42: §2 — Constraints -->
87
+
88
+ | Type | Constraint | Background |
89
+ |------|-----------|------------|
90
+ | Technical | ${stack.language || 'TBD'} | Primary language |
91
+ | Technical | ${stack.framework || 'TBD'} | Framework |
92
+ | Infrastructure | ${stack.hosting || 'TBD'} | Hosting provider |
93
+
94
+ ## 3. Context & Scope
95
+ <!-- arc42: §3 — Context and Scope (C4 Level 1: System Context) -->
96
+
97
+ \\\`\\\`\\\`mermaid
98
+ graph TD
99
+ U[Users/Clients] --> S[${config.projectName}]
100
+ S --> DB[(${stack.database || 'Database'})]
101
+ S --> EXT[External Services]
102
+ \\\`\\\`\\\`
103
+
104
+ ## 4. Solution Strategy
105
+ <!-- arc42: §4 — Solution Strategy -->
106
+
107
+ See \\\`docs-canonical/ADR.md\\\` for architecture decision records.
108
+
109
+ ## 5. Building Block View
110
+ <!-- arc42: §5 — Building Block View (C4 Level 2: Container) -->
111
+
112
+ | Component | Responsibility | Location | Tests |
113
+ |-----------|---------------|----------|-------|
114
+ ${componentRows.join('\\n') || '| <!-- Add components --> | | | |'}
115
+
116
+ \\\`\\\`\\\`mermaid
117
+ graph TD
118
+ A[Client] --> B[${stack.framework || 'API'}]
119
+ B --> C[Services]
120
+ C --> D[${stack.database || 'Database'}]
121
+ ${scan.middlewares.length > 0 ? 'A --> M[Middleware] --> B' : ''}
122
+ ${scan.components.length > 0 ? 'A --> UI[UI Components]' : ''}
123
+ \\\`\\\`\\\`
124
+
125
+ ## 6. Runtime View
126
+ <!-- arc42: §6 — Runtime View -->
127
+
128
+ \\\`\\\`\\\`mermaid
129
+ sequenceDiagram
130
+ participant C as Client
131
+ participant A as ${stack.framework || 'API'}
132
+ participant S as Service
133
+ participant D as ${stack.database || 'DB'}
134
+ C->>A: Request
135
+ A->>S: Process
136
+ S->>D: Query
137
+ D-->>S: Result
138
+ S-->>A: Response
139
+ A-->>C: JSON
140
+ \\\`\\\`\\\`
141
+
142
+ ## 7. Deployment View
143
+ <!-- arc42: §7 — Deployment View -->
144
+
145
+ See \\\`docs-canonical/DEPLOYMENT.md\\\` for details.
146
+
147
+ | Environment | Infrastructure | URL |
148
+ |-------------|---------------|-----|
149
+ | Development | localhost | http://localhost:3000 |
150
+ | Staging | ${stack.hosting || 'TBD'} | <!-- TBD --> |
151
+ | Production | ${stack.hosting || 'TBD'} | <!-- TBD --> |
152
+
153
+ ## 8. Crosscutting Concepts
154
+ <!-- arc42: §8 — Crosscutting Concepts -->
155
+
156
+ ### Tech Stack
157
+
158
+ | Category | Technology | Version | License |
159
+ |----------|-----------|---------|---------|
160
+ ${techRows || '| <!-- Add technologies --> | | | |'}
161
+ ${docToolRows.length > 0 ? `
162
+ ### Documentation Tools
163
+
164
+ | Tool | Config | Status |
165
+ |------|--------|--------|
166
+ ${docToolRows.join('\\n')}
167
+ ` : ''}
168
+
169
+ ### Layer Boundaries
170
+
171
+ | Layer | Can Import From | Cannot Import From |
172
+ |-------|----------------|-------------------|
173
+ ${scan.routes.length > 0 ? '| Routes/Handlers | Services, Middleware | Models (direct) |' : ''}
174
+ ${scan.services.length > 0 ? '| Services | Repositories, Utils | Routes |' : ''}
175
+ ${scan.models.length > 0 ? '| Models/Repositories | Utils | Services, Routes |' : ''}
176
+
177
+ ## 9. Architecture Decisions
178
+ <!-- arc42: §9 — Architecture Decisions -->
179
+
180
+ See \\\`docs-canonical/ADR.md\\\` for the full decision log.
181
+
182
+ ## 10. Quality Requirements
183
+ <!-- arc42: §10 — Quality Requirements -->
184
+
185
+ See \\\`docs-canonical/TEST-SPEC.md\\\` for test requirements and coverage targets.
186
+
187
+ ## 11. Risks & Technical Debt
188
+ <!-- arc42: §11 — Risk Assessment and Technical Debt -->
189
+
190
+ See \\\`DRIFT-LOG.md\\\` for documented deviations from canonical specs.
191
+ See \\\`docs-canonical/KNOWN-GOTCHAS.md\\\` for known issues.
192
+
193
+ ## 12. Glossary
194
+ <!-- arc42: §12 — Glossary -->
195
+
196
+ | Term | Definition |
197
+ |------|-----------|
198
+ | CDD | Canonical-Driven Development — documentation as the source of truth |
199
+ | Canonical Doc | A specification document that defines system behavior |
200
+ | Drift | Conscious deviation from canonical documentation |
201
+
202
+ ---
203
+
204
+ ## Revision History
205
+
206
+ | Version | Date | Author | Changes |
207
+ |---------|------|--------|---------|
208
+ | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (arc42 + C4 aligned) |
209
+ `;
210
+
211
+ safeWrite(path, appendStandardsCitation(content, 'ARCHITECTURE.md'), 'utf-8');
212
+ console.log(` ${c.green}✅ ARCHITECTURE.md${c.reset} (arc42 §1-§12, ${componentRows.length} components, ${Object.values(stack).filter(Boolean).length} tech)`);
213
+ return true;
214
+ }
215
+
216
+ // ── API Reference Generator (NEW — from deep route scanning) ───────────────
217
+
218
+ export function generateApiReference(dir, config, stack, deepRoutes, flags) {
219
+ const path = resolve(dir, 'docs-canonical/API-REFERENCE.md');
220
+ if (existsSync(path) && !flags.force) {
221
+ console.log(` ${c.dim}⏭️ API-REFERENCE.md (exists)${c.reset}`);
222
+ return false;
223
+ }
224
+
225
+ // Group routes by resource (first path segment after /api/)
226
+ const groups = {};
227
+ for (const route of deepRoutes) {
228
+ const parts = route.path.split('/').filter(Boolean);
229
+ const resource = parts[1] || parts[0] || 'root';
230
+ if (!groups[resource]) groups[resource] = [];
231
+ groups[resource].push(route);
232
+ }
233
+
234
+ // Build endpoint table
235
+ const endpointRows = deepRoutes
236
+ .sort((a, b) => a.path.localeCompare(b.path) || a.method.localeCompare(b.method))
237
+ .map(r => `| \`${r.method}\` | \`${r.path}\` | ${r.handler || '—'} | ${r.auth ? '🔒' : '🔓'} | ${r.description || '—'} |`)
238
+ .join('\n');
239
+
240
+ // Build per-resource sections
241
+ const resourceSections = Object.entries(groups)
242
+ .sort(([a], [b]) => a.localeCompare(b))
243
+ .map(([resource, routes]) => {
244
+ const routeDetails = routes.map(r => `#### ${r.method} \`${r.path}\`
245
+
246
+ > Source: \`${r.file}\`${r.source ? ` (${r.source})` : ''}
247
+
248
+ - **Auth:** ${r.auth ? 'Required' : 'None'}
249
+ - **Handler:** ${r.handler || '—'}
250
+ ${r.description ? `- **Description:** ${r.description}` : ''}
251
+
252
+ | Parameter | In | Type | Required | Description |
253
+ |-----------|-----|------|:--------:|-------------|
254
+ | <!-- TBD --> | | | | |
255
+
256
+ | Status | Response |
257
+ |--------|----------|
258
+ | 200 | Success |
259
+ | 400 | Bad Request |
260
+ | 401 | Unauthorized |
261
+ `).join('\n');
262
+
263
+ return `### ${resource.charAt(0).toUpperCase() + resource.slice(1)}
264
+
265
+ ${routeDetails}`;
266
+ }).join('\n---\n\n');
267
+
268
+ const content = `# API Reference
269
+
270
+ <!-- docguard:version 0.1.0 -->
271
+ <!-- docguard:status draft -->
272
+ <!-- docguard:last-reviewed ${new Date().toISOString().split('T')[0]} -->
273
+ <!-- docguard:generated true -->
274
+
275
+ > **Auto-generated by DocGuard.** Review and refine this document.
276
+
277
+ | Metadata | Value |
278
+ |----------|-------|
279
+ | **Status** | ![Status](https://img.shields.io/badge/status-draft-yellow) |
280
+ | **Base URL** | \`http://localhost:3000\` |
281
+ | **Auth** | <!-- TBD: Describe auth mechanism --> |
282
+ | **Total Endpoints** | ${deepRoutes.length} |
283
+ | **Source** | ${deepRoutes[0]?.source || 'code scan'} |
284
+
285
+ ---
286
+
287
+ ## Endpoints Summary
288
+
289
+ | Method | Path | Handler | Auth | Description |
290
+ |--------|------|---------|:----:|-------------|
291
+ ${endpointRows}
292
+
293
+ ---
294
+
295
+ ## Endpoint Details
296
+
297
+ ${resourceSections}
298
+
299
+ ---
300
+
301
+ ## Revision History
302
+
303
+ | Version | Date | Author | Changes |
304
+ |---------|------|--------|---------|
305
+ | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${deepRoutes.length} endpoints from ${deepRoutes[0]?.source || 'code'}) |
306
+ `;
307
+
308
+ safeWrite(path, appendStandardsCitation(content, 'API-REFERENCE.md'), 'utf-8');
309
+ console.log(` ${c.green}✅ API-REFERENCE.md${c.reset} (${deepRoutes.length} endpoints, ${Object.keys(groups).length} resources)`);
310
+ return true;
311
+ }
312
+
313
+ // ── Enhanced Data Model Generator ──────────────────────────────────────────
314
+
315
+ export function generateDataModel(dir, config, stack, scan, flags, deepSchemas) {
316
+ const path = resolve(dir, 'docs-canonical/DATA-MODEL.md');
317
+ if (existsSync(path) && !flags.force) {
318
+ console.log(` ${c.dim}⏭️ DATA-MODEL.md (exists)${c.reset}`);
319
+ return false;
320
+ }
321
+
322
+ // Use deep schemas if available, fallback to basic scan
323
+ let entities = [];
324
+ let relationships = [];
325
+ let schemaSource = 'file scan';
326
+
327
+ if (deepSchemas && deepSchemas.entities.length > 0) {
328
+ entities = deepSchemas.entities;
329
+ relationships = deepSchemas.relationships;
330
+ schemaSource = deepSchemas.source;
331
+ } else {
332
+ // Fallback: basic entity detection from file names
333
+ for (const modelFile of scan.models) {
334
+ const name = basename(modelFile, extname(modelFile));
335
+ if (name !== 'index' && name !== 'schema') {
336
+ entities.push({
337
+ name: name.charAt(0).toUpperCase() + name.slice(1),
338
+ fields: [],
339
+ file: modelFile,
340
+ source: 'file',
341
+ });
342
+ }
343
+ }
344
+ }
345
+
346
+ // Build entity summary table
347
+ const entityRows = entities
348
+ .filter(e => e.source !== 'prisma-enum')
349
+ .map(e => {
350
+ const pk = e.fields?.find(f => f.primaryKey);
351
+ return `| ${e.name} | ${stack.database || 'TBD'} | ${pk ? pk.name : e.name.toLowerCase() + 'Id'} | ${e.file || '—'} | ${e.fields?.length || 0} fields |`;
352
+ }).join('\n');
353
+
354
+ // Build detailed entity sections
355
+ const entitySections = entities
356
+ .filter(e => e.source !== 'prisma-enum')
357
+ .map(e => {
358
+ if (!e.fields || e.fields.length === 0) {
359
+ return `### ${e.name}
360
+
361
+ > Source: \`${e.file || 'unknown'}\`
362
+
363
+ | Field | Type | Required | Default | Constraints | Description |
364
+ |-------|------|----------|---------|-------------|-------------|
365
+ | <!-- TBD: Fill in fields --> | | | | | |
366
+ `;
367
+ }
368
+ const fieldRows = e.fields.map(f =>
369
+ `| ${f.name} | ${f.type} | ${f.required ? '✓' : '✗'} | ${f.default || '—'} | ${f.primaryKey ? 'PK' : ''}${f.unique ? ' UK' : ''} | ${f.description || ''} |`
370
+ ).join('\n');
371
+
372
+ return `### ${e.name}
373
+
374
+ > Source: \`${e.file || 'unknown'}\` (${e.source || 'detected'})
375
+
376
+ | Field | Type | Required | Default | Constraints | Description |
377
+ |-------|------|:--------:|---------|-------------|-------------|
378
+ ${fieldRows}
379
+ `;
380
+ }).join('\n');
381
+
382
+ // Build enum sections (if Prisma enums found)
383
+ const enums = entities.filter(e => e.source === 'prisma-enum');
384
+ const enumSection = enums.length > 0 ? `## Enums
385
+
386
+ ${enums.map(e => `### ${e.name}
387
+
388
+ | Value |
389
+ |-------|
390
+ ${e.fields.map(f => `| ${f.name} |`).join('\n')}
391
+ `).join('\n')}` : '';
392
+
393
+ // Build relationship table
394
+ const relRows = relationships.length > 0
395
+ ? relationships.map(r => `| ${r.from} | ${r.to} | ${r.type} | ${r.field} | — |`).join('\n')
396
+ : '| <!-- No relationships detected --> | | | | |';
397
+
398
+ // Generate mermaid ER diagram
399
+ const erDiagram = generateERDiagram(entities, relationships);
400
+
401
+ const content = `# Data Model
402
+
403
+ <!-- docguard:version 0.1.0 -->
404
+ <!-- docguard:status draft -->
405
+ <!-- docguard:last-reviewed ${new Date().toISOString().split('T')[0]} -->
406
+ <!-- docguard:generated true -->
407
+
408
+ > **Auto-generated by DocGuard.** Review and refine this document.
409
+
410
+ | Metadata | Value |
411
+ |----------|-------|
412
+ | **Status** | ![Status](https://img.shields.io/badge/status-draft-yellow) |
413
+ | **Version** | \`0.1.0\` |
414
+ | **Database** | ${stack.database || 'TBD'} |
415
+ | **ORM** | ${stack.orm || 'None detected'} |
416
+ | **Schema Source** | ${schemaSource} |
417
+ | **Entities** | ${entities.filter(e => e.source !== 'prisma-enum').length} |
418
+ | **Relationships** | ${relationships.length} |
419
+
420
+ ---
421
+
422
+ ## Entity Summary
423
+
424
+ | Entity | Storage | Primary Key | Source | Fields |
425
+ |--------|---------|-------------|--------|--------|
426
+ ${entityRows || '| <!-- No models detected --> | | | | |'}
427
+
428
+ ---
429
+
430
+ ## Entity Details
431
+
432
+ ${entitySections}
433
+ ${enumSection}
434
+
435
+ ## Relationships
436
+
437
+ | From | To | Type | FK/Reference | Cascade |
438
+ |------|-----|------|-------------|---------|
439
+ ${relRows}
440
+ ${erDiagram ? `
441
+ ## Entity-Relationship Diagram
442
+
443
+ \`\`\`mermaid
444
+ ${erDiagram}
445
+ \`\`\`
446
+ ` : ''}
447
+
448
+ ## Indexes
449
+
450
+ | Table | Index Name | Fields | Type | Purpose |
451
+ |-------|-----------|--------|------|---------|
452
+ | <!-- TBD: Document indexes --> | | | | |
453
+
454
+ ---
455
+
456
+ ## Revision History
457
+
458
+ | Version | Date | Author | Changes |
459
+ |---------|------|--------|---------|
460
+ | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${entities.length} entities, ${relationships.length} relationships from ${schemaSource}) |
461
+ `;
462
+
463
+ safeWrite(path, appendStandardsCitation(content, 'DATA-MODEL.md'), 'utf-8');
464
+ console.log(` ${c.green}✅ DATA-MODEL.md${c.reset} (${entities.length} entities, ${relationships.length} relationships from ${schemaSource})`);
465
+ return true;
466
+ }
467
+
468
+ export function generateEnvironment(dir, config, stack, scan, flags) {
469
+ const path = resolve(dir, 'docs-canonical/ENVIRONMENT.md');
470
+ if (existsSync(path) && !flags.force) {
471
+ console.log(` ${c.dim}⏭️ ENVIRONMENT.md (exists)${c.reset}`);
472
+ return false;
473
+ }
474
+
475
+ const envVarRows = scan.envVars.map(v =>
476
+ `| \`${v.name}\` | ${categorizeEnvVar(v.name)} | Yes | \`${v.example}\` | |`
477
+ ).join('\n');
478
+
479
+ const content = `# Environment
480
+
481
+ <!-- docguard:version 0.1.0 -->
482
+ <!-- docguard:status draft -->
483
+ <!-- docguard:last-reviewed ${new Date().toISOString().split('T')[0]} -->
484
+ <!-- docguard:generated true -->
485
+
486
+ > **Auto-generated by DocGuard.** Review and refine this document.
487
+
488
+ | Metadata | Value |
489
+ |----------|-------|
490
+ | **Status** | ![Status](https://img.shields.io/badge/status-draft-yellow) |
491
+ | **Version** | \`0.1.0\` |
492
+
493
+ ---
494
+
495
+ ## Prerequisites
496
+
497
+ | Tool | Version | Installation |
498
+ |------|---------|-------------|
499
+ ${stack.language ? `| ${stack.language.split(' ')[0]} | ${stack.language.split(' ')[1] || 'latest'} | |` : ''}
500
+ ${stack.framework ? `| ${stack.framework.split(' ')[0]} | ${stack.framework.split(' ')[1] || 'latest'} | |` : ''}
501
+ ${stack.database ? `| ${stack.database} | latest | |` : ''}
502
+
503
+ ## Environment Variables
504
+
505
+ | Variable | Category | Required | Example | Description |
506
+ |----------|----------|:--------:|---------|-------------|
507
+ ${envVarRows || '| <!-- No .env.example found --> | | | | |'}
508
+
509
+ ## Setup Steps
510
+
511
+ 1. Clone the repository
512
+ 2. Install dependencies: \`${existsSync(resolve(dir, 'pnpm-lock.yaml')) ? 'pnpm install' : 'npm install'}\`
513
+ 3. Copy environment file: \`cp .env.example .env.local\`
514
+ 4. Fill in environment variables
515
+ 5. Start development server: \`${existsSync(resolve(dir, 'pnpm-lock.yaml')) ? 'pnpm' : 'npm'} run dev\`
516
+
517
+ ---
518
+
519
+ ## Revision History
520
+
521
+ | Version | Date | Author | Changes |
522
+ |---------|------|--------|---------|
523
+ | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${scan.envVars.length} env vars found) |
524
+ `;
525
+
526
+ safeWrite(path, appendStandardsCitation(content, 'ENVIRONMENT.md'), 'utf-8');
527
+ console.log(` ${c.green}✅ ENVIRONMENT.md${c.reset} (${scan.envVars.length} env vars detected)`);
528
+ return true;
529
+ }
530
+
531
+ export function generateTestSpec(dir, config, stack, scan, flags) {
532
+ const path = resolve(dir, 'docs-canonical/TEST-SPEC.md');
533
+ if (existsSync(path) && !flags.force) {
534
+ console.log(` ${c.dim}⏭️ TEST-SPEC.md (exists)${c.reset}`);
535
+ return false;
536
+ }
537
+
538
+ // Build service-to-test map
539
+ const serviceMap = [];
540
+ for (const svc of scan.services) {
541
+ const svcName = basename(svc, extname(svc));
542
+ const matchingTest = scan.tests.find(t =>
543
+ t.includes(svcName) || t.includes(svcName.replace('.', '.test.'))
544
+ );
545
+ serviceMap.push({
546
+ source: svc,
547
+ test: matchingTest || '—',
548
+ status: matchingTest ? '✅' : '❌',
549
+ });
550
+ }
551
+
552
+ const serviceRows = serviceMap.map(s =>
553
+ `| \`${s.source}\` | \`${s.test}\` | — | ${s.status} |`
554
+ ).join('\n');
555
+
556
+ const content = `# Test Specification
557
+
558
+ <!-- docguard:version 0.1.0 -->
559
+ <!-- docguard:status draft -->
560
+ <!-- docguard:last-reviewed ${new Date().toISOString().split('T')[0]} -->
561
+ <!-- docguard:generated true -->
562
+
563
+ > **Auto-generated by DocGuard.** Review and refine this document.
564
+
565
+ | Metadata | Value |
566
+ |----------|-------|
567
+ | **Status** | ![Status](https://img.shields.io/badge/status-draft-yellow) |
568
+ | **Test Framework** | ${stack.testing || 'Not detected'} |
569
+ | **Test Files Found** | ${scan.tests.length} |
570
+
571
+ ---
572
+
573
+ ## Test Categories
574
+
575
+ | Category | Framework | Location | Run Command |
576
+ |----------|-----------|----------|-------------|
577
+ | Unit | ${stack.testing || 'TBD'} | tests/unit/ | \`npm test\` |
578
+ | Integration | ${stack.testing || 'TBD'} | tests/integration/ | \`npm run test:integration\` |
579
+ | E2E | Playwright | tests/e2e/ | \`npm run test:e2e\` |
580
+
581
+ ## Coverage Rules
582
+
583
+ | Metric | Target | Current |
584
+ |--------|:------:|:-------:|
585
+ | Line Coverage | 80% | <!-- TBD --> |
586
+ | Branch Coverage | 70% | <!-- TBD --> |
587
+ | Function Coverage | 80% | <!-- TBD --> |
588
+
589
+ ## Service-to-Test Map
590
+
591
+ | Source File | Unit Test | Integration Test | Status |
592
+ |------------|-----------|-----------------|:------:|
593
+ ${serviceRows || '| <!-- No services found --> | | | |'}
594
+
595
+ ## Critical User Journeys
596
+
597
+ | # | Journey | Test File | Status |
598
+ |---|---------|-----------|:------:|
599
+ | 1 | <!-- e.g. User Registration --> | <!-- test file --> | ❌ |
600
+ | 2 | <!-- e.g. Login Flow --> | | ❌ |
601
+
602
+ ---
603
+
604
+ ## Revision History
605
+
606
+ | Version | Date | Author | Changes |
607
+ |---------|------|--------|---------|
608
+ | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${scan.tests.length} test files, ${serviceMap.filter(s => s.status === '✅').length}/${serviceMap.length} mapped) |
609
+ `;
610
+
611
+ safeWrite(path, appendStandardsCitation(content, 'TEST-SPEC.md'), 'utf-8');
612
+ console.log(` ${c.green}✅ TEST-SPEC.md${c.reset} (${scan.tests.length} tests, ${serviceMap.filter(s => s.status === '✅').length}/${serviceMap.length} services mapped)`);
613
+ return true;
614
+ }
615
+
616
+ export function generateSecurity(dir, config, stack, scan, flags) {
617
+ const path = resolve(dir, 'docs-canonical/SECURITY.md');
618
+ if (existsSync(path) && !flags.force) {
619
+ console.log(` ${c.dim}⏭️ SECURITY.md (exists)${c.reset}`);
620
+ return false;
621
+ }
622
+
623
+ const content = `# Security
624
+
625
+ <!-- docguard:version 0.1.0 -->
626
+ <!-- docguard:status draft -->
627
+ <!-- docguard:last-reviewed ${new Date().toISOString().split('T')[0]} -->
628
+ <!-- docguard:generated true -->
629
+
630
+ > **Auto-generated by DocGuard.** Review and refine this document.
631
+
632
+ | Metadata | Value |
633
+ |----------|-------|
634
+ | **Status** | ![Status](https://img.shields.io/badge/status-draft-yellow) |
635
+
636
+ ---
637
+
638
+ ## Authentication
639
+
640
+ | Method | Provider | Token Type | Expiry |
641
+ |--------|---------|-----------|--------|
642
+ | ${stack.auth || '<!-- TBD -->'} | | | |
643
+
644
+ ## Authorization
645
+
646
+ | Role | Permissions | Notes |
647
+ |------|-----------|-------|
648
+ | <!-- e.g. admin --> | <!-- All --> | |
649
+ | <!-- e.g. user --> | <!-- Read/Write --> | |
650
+
651
+ ## Secrets Management
652
+
653
+ | Secret | Storage | Rotation | Access |
654
+ |--------|---------|----------|--------|
655
+ ${scan.envVars.filter(v => isSecretVar(v.name)).map(v =>
656
+ `| \`${v.name}\` | Environment Variable | <!-- TBD --> | Application |`
657
+ ).join('\n') || '| <!-- TBD --> | | | |'}
658
+
659
+ ## Security Rules
660
+
661
+ - [ ] All secrets stored in environment variables (never in code)
662
+ - [ ] \`.env\` is in \`.gitignore\`
663
+ - [ ] API endpoints require authentication
664
+ - [ ] Input validation on all user inputs
665
+ - [ ] HTTPS enforced in production
666
+ - [ ] CORS configured appropriately
667
+
668
+ ---
669
+
670
+ ## Revision History
671
+
672
+ | Version | Date | Author | Changes |
673
+ |---------|------|--------|---------|
674
+ | 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated |
675
+ `;
676
+
677
+ safeWrite(path, appendStandardsCitation(content, 'SECURITY.md'), 'utf-8');
678
+ console.log(` ${c.green}✅ SECURITY.md${c.reset} (auth: ${stack.auth || 'not detected'})`);
679
+ return true;
680
+ }
681
+
682
+ export function generateRootFiles(dir, config, stack, scan, flags, docTools) {
683
+ let created = 0;
684
+ let skipped = 0;
685
+
686
+ // AGENTS.md (AGENTS.md Standard compliant)
687
+ const agentsPath = resolve(dir, 'AGENTS.md');
688
+ if (!existsSync(agentsPath) || flags.force) {
689
+ const content = `# AI Agent Instructions — ${config.projectName}
690
+
691
+ <!-- Standard: https://agents.md -->
692
+ <!-- Generated by DocGuard — AGENTS.md standard compliant -->
693
+
694
+ > This project follows **Canonical-Driven Development (CDD)**.
695
+ > Documentation is the source of truth. Read before coding.
696
+
697
+ ## Workflow
698
+
699
+ 1. **Read** \`docs-canonical/\` before suggesting changes
700
+ 2. **Check** existing patterns in the codebase
701
+ 3. **Run** \`npx docguard-cli diagnose\` to see what needs fixing
702
+ 4. **Confirm** your approach before writing code
703
+ 5. **Implement** matching existing code style
704
+ 6. **Log** any deviations in \`DRIFT-LOG.md\` with \`// DRIFT: reason\`
705
+ 7. **Verify** with \`npx docguard-cli guard\` — all checks must pass
706
+
707
+ ## Project Stack
708
+
709
+ ${Object.entries(stack).filter(([, v]) => v).map(([k, v]) => `- **${k}**: ${v}`).join('\n')}
710
+
711
+ ## Key Files
712
+
713
+ | File | Purpose |
714
+ |------|---------|
715
+ | \`docs-canonical/ARCHITECTURE.md\` | System design (arc42 aligned) |
716
+ | \`docs-canonical/API-REFERENCE.md\` | API endpoint documentation |
717
+ | \`docs-canonical/DATA-MODEL.md\` | Database schemas & entities |
718
+ | \`docs-canonical/SECURITY.md\` | Auth & secrets |
719
+ | \`docs-canonical/TEST-SPEC.md\` | Test requirements |
720
+ | \`docs-canonical/ENVIRONMENT.md\` | Environment setup |
721
+ | \`AGENTS.md\` | AI agent instructions (this file) |
722
+ | \`CHANGELOG.md\` | Change tracking |
723
+ | \`DRIFT-LOG.md\` | Documented deviations |
724
+
725
+ ## Permissions & Guardrails
726
+
727
+ > **IMPORTANT:** These limits apply to all AI agents working on this project.
728
+
729
+ ### Allowed
730
+
731
+ - Read any file in the repository
732
+ - Modify files within \`src/\`, \`tests/\`, and \`docs-canonical/\`
733
+ - Run test commands (\`npm test\`, \`npx docguard-cli guard\`)
734
+ - Create new files in appropriate directories
735
+
736
+ ### Not Allowed
737
+
738
+ - Modify \`.env\` files or secrets
739
+ - Push commits or create releases without explicit approval
740
+ - Delete or rename canonical documentation files
741
+ - Bypass DocGuard checks (\`docguard guard\` must pass)
742
+ - Install new dependencies without approval
743
+
744
+ ### Safety Rules
745
+
746
+ - Never hardcode secrets, tokens, or API keys
747
+ - Always validate inputs before processing
748
+ - Never expose internal paths or stack traces to users
749
+ - Run \`npx docguard-cli guard\` before every commit
750
+
751
+ ## Monorepo Support
752
+
753
+ <!-- If this is a monorepo, nested AGENTS.md files in subdirectories
754
+ override these instructions for their scope. -->
755
+
756
+ | Scope | AGENTS.md Location |
757
+ |-------|-------------------|
758
+ | Root (default) | \`./AGENTS.md\` |
759
+ | <!-- e.g. packages/api --> | <!-- packages/api/AGENTS.md --> |
760
+
761
+ ## DocGuard Commands
762
+
763
+ \`\`\`bash
764
+ npx docguard-cli guard # Validate compliance
765
+ npx docguard-cli diagnose # Identify issues + AI fix prompts
766
+ npx docguard-cli fix --doc ARCH # Fix specific document
767
+ npx docguard-cli score # CDD maturity score (0-100)
768
+ npx docguard-cli generate # Generate docs from code
769
+ \`\`\`
770
+
771
+ ### AI Agent Workflow (IMPORTANT)
772
+
773
+ 1. **Before work**: Run \`npx docguard-cli guard\` — understand compliance state
774
+ 2. **After changes**: Run \`npx docguard-cli diagnose\` — get fix instructions
775
+ 3. **Fix issues**: Each issue has an \`ai_instruction\` — follow it exactly
776
+ 4. **Verify**: Run \`npx docguard-cli guard\` again — must pass before commit
777
+ 5. **Update CHANGELOG**: All changes need a changelog entry
778
+
779
+ ## Rules
780
+
781
+ - Never commit without updating CHANGELOG.md
782
+ - If code deviates from docs, add \`// DRIFT: reason\`
783
+ - Security rules in SECURITY.md are mandatory
784
+ - Test requirements in TEST-SPEC.md must be met
785
+ - Documentation changes must pass \`docguard guard\`
786
+ `;
787
+ safeWrite(agentsPath, content);
788
+ console.log(` ${c.green}✅ AGENTS.md${c.reset} (AGENTS.md standard compliant)`);
789
+ created++;
790
+ } else {
791
+ console.log(` ${c.dim}⏭️ AGENTS.md (exists)${c.reset}`);
792
+ skipped++;
793
+ }
794
+
795
+ // CHANGELOG.md
796
+ const changelogPath = resolve(dir, 'CHANGELOG.md');
797
+ if (!existsSync(changelogPath) || flags.force) {
798
+ const content = `# Changelog
799
+
800
+ All notable changes to this project will be documented in this file.
801
+
802
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
803
+
804
+ ## [Unreleased]
805
+
806
+ ### Added
807
+ - CDD documentation via DocGuard generate
808
+ `;
809
+ safeWrite(changelogPath, content);
810
+ console.log(` ${c.green}✅ CHANGELOG.md${c.reset}`);
811
+ created++;
812
+ } else {
813
+ console.log(` ${c.dim}⏭️ CHANGELOG.md (exists)${c.reset}`);
814
+ skipped++;
815
+ }
816
+
817
+ // DRIFT-LOG.md
818
+ const driftPath = resolve(dir, 'DRIFT-LOG.md');
819
+ if (!existsSync(driftPath) || flags.force) {
820
+ const content = `# Drift Log
821
+
822
+ > Documents conscious deviations from canonical specifications.
823
+ > Every \`// DRIFT: reason\` in code must have a corresponding entry here.
824
+
825
+ | Date | File | Canonical Doc | Drift Description | Severity | Resolution |
826
+ |------|------|---------------|-------------------|----------|------------|
827
+ | | | | | | |
828
+ `;
829
+ safeWrite(driftPath, content);
830
+ console.log(` ${c.green}✅ DRIFT-LOG.md${c.reset}`);
831
+ created++;
832
+ } else {
833
+ console.log(` ${c.dim}⏭️ DRIFT-LOG.md (exists)${c.reset}`);
834
+ skipped++;
835
+ }
836
+
837
+ return { created, skipped };
838
+ }
839
+
840
+ // ── Utility Functions ──────────────────────────────────────────────────────
841
+
842
+ function categorizeEnvVar(name) {
843
+ if (name.includes('SECRET') || name.includes('KEY') || name.includes('TOKEN') || name.includes('PASSWORD')) return '🔐 Secret';
844
+ if (name.includes('DATABASE') || name.includes('DB_') || name.includes('REDIS')) return '🗃️ Database';
845
+ if (name.includes('AUTH') || name.includes('JWT') || name.includes('SESSION')) return '🔒 Auth';
846
+ if (name.includes('AWS') || name.includes('CLOUD') || name.includes('S3')) return '☁️ Cloud';
847
+ if (name.includes('URL') || name.includes('HOST') || name.includes('PORT')) return '🌐 Network';
848
+ return '⚙️ Config';
849
+ }
850
+
851
+ function isSecretVar(name) {
852
+ return name.includes('SECRET') || name.includes('KEY') || name.includes('TOKEN') || name.includes('PASSWORD');
853
+ }