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.
- package/README.es.md +102 -0
- package/README.md +80 -32
- package/README.pt-BR.md +101 -0
- package/STANDARD.md +20 -10
- package/cli/commands/agents.mjs +149 -0
- package/cli/commands/diff.mjs +6 -15
- package/cli/commands/generate.mjs +14 -1001
- package/cli/commands/guard.mjs +136 -8
- package/cli/commands/llms.mjs +67 -5
- package/cli/commands/mcp.mjs +263 -0
- package/cli/commands/memory.mjs +115 -0
- package/cli/commands/score.mjs +76 -12
- package/cli/commands/trace.mjs +364 -1
- package/cli/commands/verify.mjs +93 -6
- package/cli/docguard.mjs +42 -5
- package/cli/findings.mjs +511 -0
- package/cli/scanners/agent-readability.mjs +202 -0
- package/cli/scanners/instruction-audit.mjs +320 -0
- package/cli/scanners/semantic-claims.mjs +7 -1
- package/cli/scanners/speckit.mjs +443 -28
- package/cli/shared-ignore.mjs +148 -16
- package/cli/shared.mjs +45 -1
- package/cli/validators/api-surface.mjs +113 -26
- package/cli/validators/architecture.mjs +66 -43
- package/cli/validators/canonical-sync.mjs +59 -28
- package/cli/validators/changelog.mjs +41 -17
- package/cli/validators/cross-reference.mjs +28 -11
- package/cli/validators/doc-quality.mjs +78 -44
- package/cli/validators/docs-coverage.mjs +90 -63
- package/cli/validators/docs-diff.mjs +63 -64
- package/cli/validators/docs-sync.mjs +48 -33
- package/cli/validators/drift.mjs +40 -34
- package/cli/validators/environment.mjs +67 -27
- package/cli/validators/freshness.mjs +12 -5
- package/cli/validators/generated-staleness.mjs +26 -10
- package/cli/validators/metadata-sync.mjs +28 -25
- package/cli/validators/metrics-consistency.mjs +89 -47
- package/cli/validators/schema-sync.mjs +37 -32
- package/cli/validators/security.mjs +7 -20
- package/cli/validators/spec-kit.mjs +3 -0
- package/cli/validators/structure.mjs +58 -23
- package/cli/validators/surface-sync.mjs +34 -15
- package/cli/validators/test-spec.mjs +87 -29
- package/cli/validators/todo-tracking.mjs +83 -74
- package/cli/validators/traceability.mjs +67 -39
- package/cli/writers/doc-generators.mjs +853 -0
- package/cli/writers/generate-io.mjs +142 -0
- package/cli/writers/sarif.mjs +129 -0
- package/commands/docguard.fix.md +56 -53
- package/commands/docguard.guard.md +53 -47
- package/commands/docguard.review.md +49 -31
- package/docs/ai-integration.md +133 -134
- package/docs/commands.md +49 -3
- package/docs/configuration.md +38 -0
- package/docs/faq.md +15 -0
- package/extensions/spec-kit-docguard/extension.yml +1 -1
- package/extensions/spec-kit-docguard/skills/docguard-fix/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-guard/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-review/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-score/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-sync/SKILL.md +2 -2
- package/package.json +2 -1
- package/schemas/docguard-config.schema.json +28 -0
- package/templates/ci/gitlab-component.yml +90 -0
- package/templates/commands/docguard.fix.md +33 -10
- package/templates/commands/docguard.guard.md +40 -26
- package/templates/commands/docguard.init.md +23 -11
- package/templates/commands/docguard.review.md +25 -8
- 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** |  |
|
|
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** |  |
|
|
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** |  |
|
|
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** |  |
|
|
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** |  |
|
|
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** |  |
|
|
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
|
+
}
|