docguard-cli 0.5.1

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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/PHILOSOPHY.md +150 -0
  3. package/README.md +309 -0
  4. package/STANDARD.md +751 -0
  5. package/cli/commands/agents.mjs +221 -0
  6. package/cli/commands/audit.mjs +92 -0
  7. package/cli/commands/badge.mjs +72 -0
  8. package/cli/commands/ci.mjs +80 -0
  9. package/cli/commands/diagnose.mjs +273 -0
  10. package/cli/commands/diff.mjs +360 -0
  11. package/cli/commands/fix.mjs +610 -0
  12. package/cli/commands/generate.mjs +842 -0
  13. package/cli/commands/guard.mjs +158 -0
  14. package/cli/commands/hooks.mjs +227 -0
  15. package/cli/commands/init.mjs +249 -0
  16. package/cli/commands/score.mjs +396 -0
  17. package/cli/commands/watch.mjs +143 -0
  18. package/cli/docguard.mjs +458 -0
  19. package/cli/validators/architecture.mjs +380 -0
  20. package/cli/validators/changelog.mjs +39 -0
  21. package/cli/validators/docs-sync.mjs +110 -0
  22. package/cli/validators/drift.mjs +101 -0
  23. package/cli/validators/environment.mjs +70 -0
  24. package/cli/validators/freshness.mjs +224 -0
  25. package/cli/validators/security.mjs +101 -0
  26. package/cli/validators/structure.mjs +88 -0
  27. package/cli/validators/test-spec.mjs +115 -0
  28. package/docs/ai-integration.md +179 -0
  29. package/docs/commands.md +239 -0
  30. package/docs/configuration.md +96 -0
  31. package/docs/faq.md +155 -0
  32. package/docs/installation.md +81 -0
  33. package/docs/profiles.md +103 -0
  34. package/docs/quickstart.md +79 -0
  35. package/package.json +57 -0
  36. package/templates/ADR.md.template +64 -0
  37. package/templates/AGENTS.md.template +88 -0
  38. package/templates/ARCHITECTURE.md.template +78 -0
  39. package/templates/CHANGELOG.md.template +16 -0
  40. package/templates/CURRENT-STATE.md.template +64 -0
  41. package/templates/DATA-MODEL.md.template +66 -0
  42. package/templates/DEPLOYMENT.md.template +66 -0
  43. package/templates/DRIFT-LOG.md.template +18 -0
  44. package/templates/ENVIRONMENT.md.template +43 -0
  45. package/templates/KNOWN-GOTCHAS.md.template +69 -0
  46. package/templates/ROADMAP.md.template +82 -0
  47. package/templates/RUNBOOKS.md.template +115 -0
  48. package/templates/SECURITY.md.template +42 -0
  49. package/templates/TEST-SPEC.md.template +55 -0
  50. package/templates/TROUBLESHOOTING.md.template +96 -0
  51. package/templates/VENDOR-BUGS.md.template +74 -0
  52. package/templates/ci/github-actions.yml +39 -0
  53. package/templates/commands/docguard.fix.md +65 -0
  54. package/templates/commands/docguard.guard.md +40 -0
  55. package/templates/commands/docguard.init.md +62 -0
  56. package/templates/commands/docguard.review.md +44 -0
  57. package/templates/commands/docguard.update.md +44 -0
@@ -0,0 +1,610 @@
1
+ /**
2
+ * Fix Command — The AI Orchestrator
3
+ *
4
+ * The CLI does NOT write documentation content.
5
+ * It FLAGS what needs work and generates intelligent prompts
6
+ * that tell the AI exactly what to research and write.
7
+ *
8
+ * Output modes:
9
+ * --format text Human-readable issue list (default)
10
+ * --format json Machine-readable for VS Code / CI
11
+ * --format prompt Full AI-ready prompt with codebase research instructions
12
+ * --doc <name> Generate deep prompt for a specific document
13
+ * --auto Create skeleton files (NOT content) via init
14
+ */
15
+
16
+ import { existsSync, readFileSync, mkdirSync } from 'node:fs';
17
+ import { resolve, basename } from 'node:path';
18
+ import { execSync } from 'node:child_process';
19
+ import { c } from '../docguard.mjs';
20
+
21
+ // ── Document Quality Definitions ───────────────────────────────────────────
22
+ // What each doc SHOULD contain, and what to look for in the codebase
23
+
24
+ const DOC_EXPECTATIONS = {
25
+ 'docs-canonical/ARCHITECTURE.md': {
26
+ label: 'Architecture',
27
+ purpose: 'Define the system design: layers, components, boundaries, and data flow',
28
+ qualitySignals: [
29
+ 'Has a system overview (not a TODO placeholder)',
30
+ 'Lists actual components/modules with responsibilities',
31
+ 'Defines layer boundaries and allowed imports',
32
+ 'Includes a data flow or request lifecycle description',
33
+ 'References real file paths or directories',
34
+ ],
35
+ aiResearchInstructions: `
36
+ RESEARCH STEPS:
37
+ 1. Read package.json for project name, description, dependencies, and scripts
38
+ 2. List the top-level directory structure (ls -la, focus on src/, lib/, cli/, app/, etc.)
39
+ 3. Identify the entry point(s) — check "main", "bin", or "exports" in package.json
40
+ 4. For each major directory, read 2-3 representative files to understand its purpose
41
+ 5. Map the import graph — which modules import which
42
+ 6. Identify external dependencies and what role they play
43
+
44
+ WRITE THE DOCUMENT:
45
+ - System Overview: 2-3 sentences on what this project does and who uses it
46
+ - Component Map: Table of each module/directory with its responsibility
47
+ - Layer Boundaries: Which layers can import from which (draw import rules)
48
+ - Data Flow: How a request/command flows through the system
49
+ - Key Design Decisions: Why the architecture is the way it is
50
+ - Technology Choices: List frameworks/tools and why they were chosen
51
+
52
+ FORMAT: Use the docguard metadata header (version, status, last-reviewed).
53
+ IMPORTANT: Use REAL file paths, REAL module names, REAL dependency names. No placeholders.`,
54
+ },
55
+
56
+ 'docs-canonical/DATA-MODEL.md': {
57
+ label: 'Data Model',
58
+ purpose: 'Document all data structures, schemas, database tables, and relationships',
59
+ qualitySignals: [
60
+ 'Lists actual database tables/collections or data structures',
61
+ 'Shows field names, types, and constraints',
62
+ 'Documents relationships (foreign keys, references)',
63
+ 'Includes indexes if applicable',
64
+ 'No TODO or example placeholders in table rows',
65
+ ],
66
+ aiResearchInstructions: `
67
+ RESEARCH STEPS:
68
+ 1. Search for schema definitions: grep -r "Schema\\|model\\|Table\\|Entity\\|interface\\|type " src/ lib/ --include="*.ts" --include="*.js" --include="*.mjs"
69
+ 2. Look for database config: grep -r "database\\|sequelize\\|prisma\\|mongoose\\|drizzle\\|knex" package.json
70
+ 3. Check for migration files in migrations/, db/, prisma/, etc.
71
+ 4. Look for TypeScript interfaces/types that define data shapes
72
+ 5. Check for Zod schemas, JSON schemas, or validation files
73
+ 6. If no database: document the config file format (.docguard.json, etc.)
74
+
75
+ WRITE THE DOCUMENT:
76
+ - If database project: Document each table with columns, types, constraints, indexes, relationships
77
+ - If config-driven: Document each config file format with all fields, types, defaults, and validation rules
78
+ - If API project: Document request/response shapes
79
+ - If CLI project: Document any configuration formats, file formats the tool reads/writes
80
+
81
+ FORMAT: Use markdown tables. Include field name, type, required/optional, default, description.
82
+ IMPORTANT: Research the actual codebase. Do NOT use placeholder values.`,
83
+ },
84
+
85
+ 'docs-canonical/SECURITY.md': {
86
+ label: 'Security',
87
+ purpose: 'Document authentication, authorization, secrets management, and security boundaries',
88
+ qualitySignals: [
89
+ 'Documents actual auth mechanism (or explicitly states "no auth needed")',
90
+ 'Lists secrets/credentials and where they are stored',
91
+ 'Describes RBAC/permissions if applicable',
92
+ 'Has a threat model or security boundaries section',
93
+ 'References .gitignore patterns for sensitive files',
94
+ ],
95
+ aiResearchInstructions: `
96
+ RESEARCH STEPS:
97
+ 1. Check .gitignore for security-related patterns (.env, secrets, keys, credentials)
98
+ 2. Search for auth: grep -r "auth\\|token\\|jwt\\|session\\|password\\|secret\\|apiKey\\|API_KEY" src/ lib/ --include="*.ts" --include="*.js" --include="*.mjs"
99
+ 3. Check package.json for auth-related dependencies (passport, jwt, bcrypt, etc.)
100
+ 4. Look for middleware or guards that enforce permissions
101
+ 5. Check for .env files (but DON'T include secret values — only variable names)
102
+ 6. Look for CORS configuration, rate limiting, input validation
103
+
104
+ WRITE THE DOCUMENT:
105
+ - Auth Mechanism: What auth does this project use? (OAuth, JWT, API key, none, etc.)
106
+ - Secrets Inventory: List all secrets/env vars needed (names only, never values)
107
+ - Secrets Storage: Where are secrets stored? (.env, Vault, AWS Secrets Manager, etc.)
108
+ - Permissions/RBAC: What roles exist? What can each role do?
109
+ - Security Boundaries: What is trusted vs untrusted input?
110
+ - .gitignore Audit: Confirm sensitive files are excluded from version control
111
+ - If CLI tool with no auth: State explicitly "No authentication required — this is a local CLI tool"
112
+
113
+ IMPORTANT: Be specific to THIS project. Don't add generic security boilerplate for features the project doesn't have.`,
114
+ },
115
+
116
+ 'docs-canonical/TEST-SPEC.md': {
117
+ label: 'Test Spec',
118
+ purpose: 'Document test strategy, coverage requirements, and critical test scenarios',
119
+ qualitySignals: [
120
+ 'References actual test files and test frameworks',
121
+ 'Lists critical flows that must be tested',
122
+ 'Documents test commands (npm test, etc.)',
123
+ 'Has coverage thresholds or quality gates',
124
+ 'No generic placeholder test scenarios',
125
+ ],
126
+ aiResearchInstructions: `
127
+ RESEARCH STEPS:
128
+ 1. Read package.json "scripts" for test commands
129
+ 2. Find test files: find . -name "*.test.*" -o -name "*.spec.*" -o -name "__tests__" | head -20
130
+ 3. Read the test configuration (jest.config, vitest.config, .mocharc, etc.)
131
+ 4. Read 2-3 test files to understand the testing patterns used
132
+ 5. Check for E2E test setup (playwright, cypress, puppeteer configs)
133
+ 6. Look for CI config that runs tests (.github/workflows/)
134
+
135
+ WRITE THE DOCUMENT:
136
+ - Test Framework: What testing tool is used and why
137
+ - Test Structure: Where tests live, naming conventions
138
+ - Test Commands: Exact commands to run unit, integration, E2E tests
139
+ - Critical Flows: List the 5-10 most important things that MUST be tested
140
+ - Coverage: Current coverage and target thresholds
141
+ - CI Integration: How tests run in CI/CD
142
+
143
+ IMPORTANT: Reference REAL test files. If there are no tests yet, document what SHOULD be tested.`,
144
+ },
145
+
146
+ 'docs-canonical/ENVIRONMENT.md': {
147
+ label: 'Environment',
148
+ purpose: 'Document setup steps, dependencies, and environment variables',
149
+ qualitySignals: [
150
+ 'Has actual setup commands (not placeholders)',
151
+ 'Lists real environment variables with descriptions',
152
+ 'Documents Node/Python/runtime version requirements',
153
+ 'Includes troubleshooting for common setup issues',
154
+ 'Works as a "new contributor can follow this and get running" guide',
155
+ ],
156
+ aiResearchInstructions: `
157
+ RESEARCH STEPS:
158
+ 1. Read package.json for: engines, scripts, dependencies
159
+ 2. Check for .nvmrc, .node-version, .python-version, .tool-versions
160
+ 3. Search for process.env usage: grep -r "process.env\\|os.environ" src/ lib/ cli/ --include="*.ts" --include="*.js" --include="*.mjs" --include="*.py"
161
+ 4. Check for .env.example or .env.template files
162
+ 5. Check for Docker/docker-compose files
163
+ 6. Look for setup scripts in scripts/ or Makefile
164
+
165
+ WRITE THE DOCUMENT:
166
+ - Prerequisites: Node version, package manager, any system deps
167
+ - Setup Steps: Exact commands from clone to running (git clone → npm install → npm run dev)
168
+ - Environment Variables: Table of all env vars (name, required/optional, description, example value — never real secrets)
169
+ - If CLI with no env vars: State "No environment variables required"
170
+ - Common Issues: Known gotchas during setup
171
+
172
+ IMPORTANT: A new contributor should be able to follow this doc and have the project running in under 10 minutes.`,
173
+ },
174
+ };
175
+
176
+ // ── Main Entry ─────────────────────────────────────────────────────────────
177
+
178
+ export function runFix(projectDir, config, flags) {
179
+ const isJson = flags.format === 'json';
180
+ const isPrompt = flags.format === 'prompt';
181
+ const autoFix = flags.auto || false;
182
+ const specificDoc = flags.doc || null;
183
+
184
+ // If --doc flag is provided, generate a deep prompt for that specific document
185
+ if (specificDoc) {
186
+ return generateDocPrompt(projectDir, config, specificDoc);
187
+ }
188
+
189
+ if (!isJson && !isPrompt) {
190
+ console.log(`${c.bold}🔧 DocGuard Fix — ${config.projectName}${c.reset}`);
191
+ console.log(`${c.dim} Directory: ${projectDir}${c.reset}`);
192
+ console.log(`${c.dim} Scanning for issues...${c.reset}\n`);
193
+ }
194
+
195
+ const issues = collectIssues(projectDir, config);
196
+
197
+ if (autoFix) {
198
+ const fixed = autoFixIssues(projectDir, config, issues);
199
+ if (!isJson) {
200
+ console.log(` ${c.green}✅ Created ${fixed} skeleton file(s)${c.reset}`);
201
+ console.log(` ${c.dim} Now run ${c.cyan}docguard fix --format prompt${c.dim} to generate AI instructions for filling them in${c.reset}\n`);
202
+ }
203
+ const remaining = collectIssues(projectDir, config);
204
+ outputResults(remaining, projectDir, config, flags);
205
+ } else {
206
+ outputResults(issues, projectDir, config, flags);
207
+ }
208
+ }
209
+
210
+ // ── Issue Collection ───────────────────────────────────────────────────────
211
+
212
+ function collectIssues(projectDir, config) {
213
+ const issues = [];
214
+ const ptc = config.projectTypeConfig || {};
215
+
216
+ // 1. Missing required files
217
+ const requiredFiles = [
218
+ ...config.requiredFiles.canonical,
219
+ config.requiredFiles.changelog,
220
+ config.requiredFiles.driftLog,
221
+ ];
222
+
223
+ for (const file of requiredFiles) {
224
+ if (!existsSync(resolve(projectDir, file))) {
225
+ issues.push({
226
+ type: 'missing-file',
227
+ severity: 'error',
228
+ file,
229
+ message: `Missing: ${file}`,
230
+ autoFixable: true,
231
+ fix: {
232
+ action: 'create',
233
+ command: 'docguard fix --auto',
234
+ ai_instruction: `Create ${file} with real project content. Run: docguard fix --doc ${basename(file, '.md').toLowerCase()}`,
235
+ },
236
+ });
237
+ }
238
+ }
239
+
240
+ // Agent file check
241
+ const hasAgent = config.requiredFiles.agentFile.some(f =>
242
+ existsSync(resolve(projectDir, f))
243
+ );
244
+ if (!hasAgent) {
245
+ issues.push({
246
+ type: 'missing-file',
247
+ severity: 'error',
248
+ file: 'AGENTS.md',
249
+ message: 'Missing: AGENTS.md (AI agent config)',
250
+ autoFixable: true,
251
+ fix: {
252
+ action: 'create',
253
+ command: 'docguard fix --auto',
254
+ ai_instruction: 'Create AGENTS.md with project stack, workflow rules, and DocGuard integration.',
255
+ },
256
+ });
257
+ }
258
+
259
+ // 2. Document quality assessment (not just placeholder detection)
260
+ for (const [filePath, expectations] of Object.entries(DOC_EXPECTATIONS)) {
261
+ const fullPath = resolve(projectDir, filePath);
262
+ if (!existsSync(fullPath)) continue;
263
+
264
+ const content = readFileSync(fullPath, 'utf-8');
265
+ const quality = assessDocQuality(content, expectations);
266
+
267
+ if (quality.score === 'empty') {
268
+ issues.push({
269
+ type: 'empty-doc',
270
+ severity: 'error',
271
+ file: filePath,
272
+ message: `${expectations.label} doc is a skeleton template with no real content`,
273
+ autoFixable: false,
274
+ fix: {
275
+ action: 'rewrite',
276
+ ai_instruction: `This document is just a template. Run: docguard fix --doc ${basename(filePath, '.md').toLowerCase()}\nThen have your AI assistant execute the generated prompt to write real content.`,
277
+ },
278
+ });
279
+ } else if (quality.score === 'partial') {
280
+ issues.push({
281
+ type: 'partial-doc',
282
+ severity: 'warning',
283
+ file: filePath,
284
+ message: `${expectations.label} doc has ${quality.placeholders} unfilled placeholder(s) — needs AI to complete`,
285
+ autoFixable: false,
286
+ fix: {
287
+ action: 'improve',
288
+ ai_instruction: `Improve ${filePath}. ${quality.failedSignals.join('. ')}.\nRun: docguard fix --doc ${basename(filePath, '.md').toLowerCase()} --format prompt`,
289
+ },
290
+ });
291
+ }
292
+ // quality.score === 'good' → no issue
293
+ }
294
+
295
+ // 3. Missing .docguard.json
296
+ if (!existsSync(resolve(projectDir, '.docguard.json'))) {
297
+ issues.push({
298
+ type: 'missing-config',
299
+ severity: 'info',
300
+ file: '.docguard.json',
301
+ message: 'No .docguard.json — using defaults',
302
+ autoFixable: true,
303
+ fix: {
304
+ action: 'create',
305
+ command: 'docguard fix --auto',
306
+ ai_instruction: 'Create .docguard.json with projectName, projectType, and projectTypeConfig.',
307
+ },
308
+ });
309
+ }
310
+
311
+ // 4. Check .env.example if needed
312
+ if (ptc.needsEnvExample !== false && ptc.needsEnvVars !== false) {
313
+ if (!existsSync(resolve(projectDir, '.env.example'))) {
314
+ const hasEnv = ['.env', '.env.local', '.env.development'].some(f =>
315
+ existsSync(resolve(projectDir, f))
316
+ );
317
+ if (hasEnv) {
318
+ issues.push({
319
+ type: 'missing-env-example',
320
+ severity: 'warning',
321
+ file: '.env.example',
322
+ message: '.env exists but no .env.example for contributors',
323
+ autoFixable: false,
324
+ fix: {
325
+ action: 'create',
326
+ ai_instruction: 'Create .env.example with all env var names from .env, replace secrets with descriptions.',
327
+ },
328
+ });
329
+ }
330
+ }
331
+ }
332
+
333
+ // 5. CHANGELOG quality
334
+ const changelogPath = resolve(projectDir, config.requiredFiles.changelog);
335
+ if (existsSync(changelogPath)) {
336
+ const content = readFileSync(changelogPath, 'utf-8');
337
+ if (!content.includes('[Unreleased]') && !content.includes('## [')) {
338
+ issues.push({
339
+ type: 'empty-changelog',
340
+ severity: 'warning',
341
+ file: config.requiredFiles.changelog,
342
+ message: 'CHANGELOG.md has no version entries',
343
+ autoFixable: false,
344
+ fix: {
345
+ action: 'edit',
346
+ ai_instruction: 'Add version entries to CHANGELOG.md following Keep a Changelog format. Check git log for recent changes.',
347
+ },
348
+ });
349
+ }
350
+ }
351
+
352
+ return issues;
353
+ }
354
+
355
+ // ── Document Quality Assessment ────────────────────────────────────────────
356
+
357
+ function assessDocQuality(content, expectations) {
358
+ const lines = content.split('\n');
359
+ const nonEmptyLines = lines.filter(l => l.trim() && !l.startsWith('<!--'));
360
+ const placeholders = (content.match(/<!-- TODO|<!-- e\.g\./g) || []).length;
361
+ const hasRealContent = nonEmptyLines.length > 10; // More than just headers
362
+
363
+ // Check if this is basically just a template
364
+ const contentLines = lines.filter(l =>
365
+ l.trim() &&
366
+ !l.startsWith('#') &&
367
+ !l.startsWith('<!--') &&
368
+ !l.startsWith('|--') &&
369
+ !l.startsWith('| *') &&
370
+ !l.match(/^\|\s*$/)
371
+ );
372
+
373
+ // Count lines that are actual content (not table headers, not metadata)
374
+ const realContentLines = contentLines.filter(l =>
375
+ !l.includes('<!-- TODO') &&
376
+ !l.includes('<!-- e.g.') &&
377
+ !l.match(/^\|\s*\|/) // empty table cells
378
+ );
379
+
380
+ const failedSignals = [];
381
+ for (const signal of expectations.qualitySignals) {
382
+ // Simple heuristic checks
383
+ if (signal.includes('TODO placeholder') && placeholders > 0) {
384
+ failedSignals.push(`Still has ${placeholders} placeholder(s)`);
385
+ }
386
+ if (signal.includes('actual') && realContentLines.length < 5) {
387
+ failedSignals.push('Lacks specific, project-relevant content');
388
+ }
389
+ }
390
+
391
+ if (!hasRealContent || realContentLines.length < 5) {
392
+ return { score: 'empty', placeholders, failedSignals };
393
+ }
394
+
395
+ if (placeholders > 0 || failedSignals.length > 2) {
396
+ return { score: 'partial', placeholders, failedSignals };
397
+ }
398
+
399
+ return { score: 'good', placeholders: 0, failedSignals: [] };
400
+ }
401
+
402
+ // ── Deep Document Prompt Generator ─────────────────────────────────────────
403
+
404
+ function generateDocPrompt(projectDir, config, docName) {
405
+ // Normalize doc name: "architecture" → "docs-canonical/ARCHITECTURE.md"
406
+ const normalized = docName.toLowerCase().replace(/\.md$/, '');
407
+ const mapping = {
408
+ 'architecture': 'docs-canonical/ARCHITECTURE.md',
409
+ 'data-model': 'docs-canonical/DATA-MODEL.md',
410
+ 'datamodel': 'docs-canonical/DATA-MODEL.md',
411
+ 'security': 'docs-canonical/SECURITY.md',
412
+ 'test-spec': 'docs-canonical/TEST-SPEC.md',
413
+ 'testspec': 'docs-canonical/TEST-SPEC.md',
414
+ 'environment': 'docs-canonical/ENVIRONMENT.md',
415
+ 'env': 'docs-canonical/ENVIRONMENT.md',
416
+ };
417
+
418
+ const filePath = mapping[normalized];
419
+ if (!filePath) {
420
+ console.error(`${c.red}Unknown document: ${docName}${c.reset}`);
421
+ console.log(`${c.dim}Available: architecture, data-model, security, test-spec, environment${c.reset}`);
422
+ process.exit(1);
423
+ }
424
+
425
+ const expectations = DOC_EXPECTATIONS[filePath];
426
+ if (!expectations) {
427
+ console.error(`${c.red}No prompt template for: ${filePath}${c.reset}`);
428
+ process.exit(1);
429
+ }
430
+
431
+ // Build context about the project
432
+ const projectType = config.projectType || 'unknown';
433
+ const projectName = config.projectName || basename(projectDir);
434
+
435
+ // Check if doc exists and what state it's in
436
+ const fullPath = resolve(projectDir, filePath);
437
+ const exists = existsSync(fullPath);
438
+ const currentContent = exists ? readFileSync(fullPath, 'utf-8') : null;
439
+ const quality = currentContent ? assessDocQuality(currentContent, expectations) : null;
440
+
441
+ const action = !exists ? 'CREATE' : quality?.score === 'empty' ? 'REWRITE (current file is just a template)' : 'IMPROVE';
442
+
443
+ console.log(`\nYou are documenting the project "${projectName}" (a ${projectType} project).`);
444
+ console.log(`Project directory: ${projectDir}\n`);
445
+ console.log(`TASK: ${action} the file ${filePath}`);
446
+ console.log(`PURPOSE: ${expectations.purpose}\n`);
447
+
448
+ if (exists && quality?.score !== 'good') {
449
+ console.log(`CURRENT STATE: The document ${quality?.score === 'empty' ? 'is just a skeleton template with TODO placeholders — it needs to be completely rewritten with REAL project content' : `has ${quality?.placeholders} placeholder(s) that need to be replaced with real content`}.`);
450
+ console.log('');
451
+ }
452
+
453
+ console.log(expectations.aiResearchInstructions.trim());
454
+
455
+ console.log(`\nVALIDATION: After writing, run \`npx docguard guard\` to verify the document passes all checks.`);
456
+ console.log(`The document should have NO <!-- TODO --> or <!-- e.g. --> placeholders.`);
457
+ console.log(`Set the docguard:status header to 'active' (not 'draft').`);
458
+ }
459
+
460
+ // ── Auto-Fix (skeleton creation only) ──────────────────────────────────────
461
+
462
+ function autoFixIssues(projectDir, config, issues) {
463
+ let fixed = 0;
464
+ const autoFixable = issues.filter(i => i.autoFixable);
465
+
466
+ if (autoFixable.length === 0) return 0;
467
+
468
+ const docsDir = resolve(projectDir, 'docs-canonical');
469
+ if (!existsSync(docsDir)) {
470
+ mkdirSync(docsDir, { recursive: true });
471
+ }
472
+
473
+ try {
474
+ const cliPath = resolve(import.meta.dirname, '..', 'docguard.mjs');
475
+ execSync(`node ${cliPath} init --dir "${projectDir}"`, {
476
+ encoding: 'utf-8',
477
+ stdio: 'pipe',
478
+ });
479
+ fixed = autoFixable.length;
480
+ } catch { /* init may partially succeed */ }
481
+
482
+ return fixed;
483
+ }
484
+
485
+ // ── Output ─────────────────────────────────────────────────────────────────
486
+
487
+ function outputResults(issues, projectDir, config, flags) {
488
+ const isJson = flags.format === 'json';
489
+ const isPrompt = flags.format === 'prompt';
490
+
491
+ if (issues.length === 0) {
492
+ if (isJson) {
493
+ console.log(JSON.stringify({ status: 'clean', issues: [], fixCount: 0 }));
494
+ } else if (isPrompt) {
495
+ console.log('No CDD issues found. All documentation is complete.');
496
+ } else {
497
+ console.log(` ${c.green}${c.bold}✅ No issues — documentation is complete!${c.reset}\n`);
498
+ }
499
+ return;
500
+ }
501
+
502
+ if (isJson) {
503
+ console.log(JSON.stringify({
504
+ status: 'issues-found',
505
+ project: config.projectName,
506
+ projectType: config.projectType || 'unknown',
507
+ issueCount: issues.length,
508
+ autoFixable: issues.filter(i => i.autoFixable).length,
509
+ issues: issues.map(i => ({
510
+ type: i.type,
511
+ severity: i.severity,
512
+ file: i.file,
513
+ message: i.message,
514
+ autoFixable: i.autoFixable,
515
+ fix: i.fix,
516
+ })),
517
+ }, null, 2));
518
+ return;
519
+ }
520
+
521
+ if (isPrompt) {
522
+ // Smart prompt that groups by action type
523
+ console.log(`You are working on "${config.projectName}" (${config.projectType || 'unknown'} project).`);
524
+ console.log(`DocGuard found ${issues.length} documentation issue(s).\n`);
525
+
526
+ // Group: empty/missing docs first (these need AI to write)
527
+ const needsWriting = issues.filter(i => i.type === 'empty-doc' || i.type === 'missing-file');
528
+ const needsFixing = issues.filter(i => i.type === 'partial-doc' || i.type === 'missing-env-example' || i.type === 'empty-changelog');
529
+ const other = issues.filter(i => !needsWriting.includes(i) && !needsFixing.includes(i));
530
+
531
+ if (needsWriting.length > 0) {
532
+ console.log('## Documents That Need To Be Written\n');
533
+ console.log('These documents are empty templates or missing. For each one, run the docguard fix --doc command to get detailed research instructions:\n');
534
+ for (const issue of needsWriting) {
535
+ const docKey = basename(issue.file, '.md').toLowerCase();
536
+ console.log(`- **${issue.file}**: ${issue.message}`);
537
+ console.log(` → Run: \`docguard fix --doc ${docKey}\` for AI research prompt\n`);
538
+ }
539
+ }
540
+
541
+ if (needsFixing.length > 0) {
542
+ console.log('## Documents That Need Improvement\n');
543
+ for (const issue of needsFixing) {
544
+ console.log(`- **${issue.file}**: ${issue.message}`);
545
+ console.log(` → ${issue.fix.ai_instruction}\n`);
546
+ }
547
+ }
548
+
549
+ if (other.length > 0) {
550
+ console.log('## Other Issues\n');
551
+ for (const issue of other) {
552
+ console.log(`- **${issue.file}**: ${issue.message}`);
553
+ console.log(` → ${issue.fix.ai_instruction}\n`);
554
+ }
555
+ }
556
+
557
+ console.log('\nAfter fixing, run `docguard guard` to verify compliance.');
558
+ return;
559
+ }
560
+
561
+ // Text output
562
+ const errors = issues.filter(i => i.severity === 'error');
563
+ const warnings = issues.filter(i => i.severity === 'warning');
564
+ const infos = issues.filter(i => i.severity === 'info');
565
+
566
+ if (errors.length > 0) {
567
+ console.log(` ${c.red}${c.bold}Errors (${errors.length})${c.reset}`);
568
+ for (const e of errors) {
569
+ console.log(` ${c.red}✖${c.reset} ${e.message}`);
570
+ if (e.type === 'empty-doc') {
571
+ const docKey = basename(e.file, '.md').toLowerCase();
572
+ console.log(` ${c.dim}Run: ${c.cyan}docguard fix --doc ${docKey}${c.dim} → paste prompt into AI${c.reset}`);
573
+ } else {
574
+ console.log(` ${c.dim}Run: ${c.cyan}${e.fix.command || 'docguard fix --auto'}${c.reset}`);
575
+ }
576
+ }
577
+ console.log('');
578
+ }
579
+
580
+ if (warnings.length > 0) {
581
+ console.log(` ${c.yellow}${c.bold}Warnings (${warnings.length})${c.reset}`);
582
+ for (const w of warnings) {
583
+ console.log(` ${c.yellow}⚠${c.reset} ${w.message}`);
584
+ if (w.type === 'partial-doc') {
585
+ const docKey = basename(w.file, '.md').toLowerCase();
586
+ console.log(` ${c.dim}Run: ${c.cyan}docguard fix --doc ${docKey}${c.dim} → paste prompt into AI${c.reset}`);
587
+ } else {
588
+ console.log(` ${c.dim}${w.fix.ai_instruction.slice(0, 100)}${c.reset}`);
589
+ }
590
+ }
591
+ console.log('');
592
+ }
593
+
594
+ if (infos.length > 0) {
595
+ console.log(` ${c.cyan}${c.bold}Info (${infos.length})${c.reset}`);
596
+ for (const info of infos) {
597
+ console.log(` ${c.cyan}ℹ${c.reset} ${info.message}`);
598
+ }
599
+ console.log('');
600
+ }
601
+
602
+ console.log(` ${c.bold}─────────────────────────────────────${c.reset}`);
603
+ console.log(` ${c.bold}Total: ${issues.length} issue(s)${c.reset}`);
604
+ console.log('');
605
+ console.log(` ${c.dim}${c.bold}Workflow:${c.reset}`);
606
+ console.log(` ${c.dim} 1. ${c.cyan}docguard fix --auto${c.dim} Create skeleton files${c.reset}`);
607
+ console.log(` ${c.dim} 2. ${c.cyan}docguard fix --doc architecture${c.dim} Get AI prompt for each doc${c.reset}`);
608
+ console.log(` ${c.dim} 3. Paste prompt into your AI assistant (Copilot, Cursor, Claude)${c.reset}`);
609
+ console.log(` ${c.dim} 4. ${c.cyan}docguard guard${c.dim} Verify compliance${c.reset}\n`);
610
+ }