docguard-cli 0.9.4 → 0.9.5

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.
@@ -70,6 +70,7 @@ export async function runInit(projectDir, config, flags) {
70
70
  { key: 'SECURITY', file: 'docs-canonical/SECURITY.md', template: 'SECURITY.md.template', desc: 'Auth, secrets, security controls', defaultYes: ['webapp', 'api'].includes(detectedType) },
71
71
  { key: 'TEST-SPEC', file: 'docs-canonical/TEST-SPEC.md', template: 'TEST-SPEC.md.template', desc: 'Test strategy, coverage requirements', defaultYes: true },
72
72
  { key: 'ENVIRONMENT', file: 'docs-canonical/ENVIRONMENT.md', template: 'ENVIRONMENT.md.template', desc: 'Environment variables, deployment config', defaultYes: ['webapp', 'api'].includes(detectedType) },
73
+ { key: 'REQUIREMENTS', file: 'docs-canonical/REQUIREMENTS.md', template: 'REQUIREMENTS.md.template', desc: 'Functional requirements, user stories (spec-kit aligned)', defaultYes: true },
73
74
  ];
74
75
 
75
76
  let selectedDocs;
@@ -1,30 +1,114 @@
1
1
  /**
2
- * Spec Kit Scanner — Detect and integrate with GitHub Spec Kit artifacts
2
+ * Spec Kit Scanner — Detect, validate, and integrate with GitHub Spec Kit
3
3
  *
4
- * Auto-detects .specify/ directory (Spec Kit projects) and maps Spec Kit
5
- * artifacts to CDD canonical docs. Enables DocGuard to work seamlessly
6
- * with Spec Kit-managed projects.
4
+ * Auto-detects Spec Kit artifacts and validates their quality against
5
+ * spec-kit standards (github.com/github/spec-kit).
7
6
  *
8
- * Spec Kit artifact mapping:
9
- * .specify/ → Project uses Spec Kit
10
- * specs/[name]/spec.md → Requirements (maps to REQUIREMENTS.md / docs-canonical/)
11
- * specs/[name]/plan.md Design decisions (maps to ARCHITECTURE.md)
12
- * specs/[name]/tasks.md → Work items (maps to ROADMAP.md)
13
- * constitution.md → Project rules (maps to AGENTS.md)
14
- * memory/ → Learned context (maps to DRIFT-LOG.md)
7
+ * v0.9.5 Aligned with spec-kit's actual file structure:
8
+ * .specify/ → Project uses Spec Kit
9
+ * .specify/specs/NNN-feature/spec.md → Requirements (FR-IDs, User Scenarios)
10
+ * .specify/specs/NNN-feature/plan.md Implementation plan (Technical Context)
11
+ * .specify/specs/NNN-feature/tasks.md → Task breakdown (Phased)
12
+ * .specify/memory/constitution.md → Project governing principles
15
13
  *
16
- * Credit: Integration inspired by GitHub's Spec Kit framework
14
+ * Also supports legacy paths (pre-v3 spec-kit):
15
+ * specs/[name]/spec.md → Legacy spec location
16
+ * constitution.md → Legacy constitution at root
17
+ * memory/ → Legacy memory at root
18
+ *
19
+ * Credit: Integration with GitHub's Spec Kit framework
17
20
  * (github.com/github/spec-kit)
18
21
  *
19
22
  * Zero dependencies — pure Node.js built-ins only.
20
23
  */
21
24
 
22
- import { existsSync, readFileSync, readdirSync, writeFileSync, statSync } from 'node:fs';
25
+ import { existsSync, readFileSync, readdirSync, statSync, copyFileSync, writeFileSync } from 'node:fs';
23
26
  import { resolve, join } from 'node:path';
24
27
 
28
+ // ──── Spec Kit Mandatory Sections ────
29
+ // Based on spec-kit's spec-template.md, plan-template.md, tasks-template.md
30
+
31
+ const SPEC_MANDATORY_SECTIONS = [
32
+ 'User Scenarios', // or "User Stories"
33
+ 'Requirements', // must have FR-xxx IDs
34
+ 'Success Criteria', // must have SC-xxx IDs
35
+ ];
36
+
37
+ const PLAN_MANDATORY_SECTIONS = [
38
+ 'Summary',
39
+ 'Technical Context',
40
+ 'Project Structure',
41
+ ];
42
+
43
+ const TASKS_MANDATORY_PATTERNS = [
44
+ /Phase\s+\d/i, // Must have phased breakdown
45
+ ];
46
+
47
+ // ──── Safety Helper ────
48
+
49
+ /**
50
+ * Create a .bak backup before overwriting existing files.
51
+ */
52
+ function backupFile(filePath) {
53
+ if (existsSync(filePath)) {
54
+ try {
55
+ const content = readFileSync(filePath, 'utf-8');
56
+ if (content.trim().length > 0) {
57
+ copyFileSync(filePath, filePath + '.bak');
58
+ }
59
+ } catch { /* non-fatal */ }
60
+ }
61
+ }
62
+
63
+ function safeWrite(filePath, content) {
64
+ backupFile(filePath);
65
+ writeFileSync(filePath, content, 'utf-8');
66
+ }
67
+
68
+ // ──── Detection ────
69
+
70
+ /**
71
+ * Scan a specs directory for feature spec folders.
72
+ * Returns array of { name, hasSpec, hasPlan, hasTasks, specPath, planPath, tasksPath }.
73
+ */
74
+ function scanSpecsDir(specsDir) {
75
+ const specs = [];
76
+ if (!existsSync(specsDir)) return specs;
77
+
78
+ try {
79
+ const features = readdirSync(specsDir);
80
+ for (const feature of features) {
81
+ const featureDir = join(specsDir, feature);
82
+ try {
83
+ if (!statSync(featureDir).isDirectory()) continue;
84
+ } catch { continue; }
85
+
86
+ const specFile = join(featureDir, 'spec.md');
87
+ const planFile = join(featureDir, 'plan.md');
88
+ const tasksFile = join(featureDir, 'tasks.md');
89
+
90
+ if (existsSync(specFile) || existsSync(planFile) || existsSync(tasksFile)) {
91
+ specs.push({
92
+ name: feature,
93
+ hasSpec: existsSync(specFile),
94
+ hasPlan: existsSync(planFile),
95
+ hasTasks: existsSync(tasksFile),
96
+ specPath: existsSync(specFile) ? specFile : null,
97
+ planPath: existsSync(planFile) ? planFile : null,
98
+ tasksPath: existsSync(tasksFile) ? tasksFile : null,
99
+ });
100
+ }
101
+ }
102
+ } catch { /* ignore */ }
103
+
104
+ return specs;
105
+ }
106
+
25
107
  /**
26
108
  * Detect if a project uses Spec Kit.
27
- * Returns details about detected Spec Kit artifacts.
109
+ * Checks both spec-kit v3+ paths (.specify/) and legacy paths.
110
+ *
111
+ * @returns {{ detected, specifyDir, specs[], constitution, constitutionPath, memory, source }}
28
112
  */
29
113
  export function detectSpecKit(projectDir) {
30
114
  const result = {
@@ -32,87 +116,189 @@ export function detectSpecKit(projectDir) {
32
116
  specifyDir: false,
33
117
  specs: [],
34
118
  constitution: false,
119
+ constitutionPath: null,
35
120
  memory: false,
121
+ source: null, // 'specify' (v3+) or 'legacy'
36
122
  };
37
123
 
38
- // Check for .specify/ directory
124
+ // ── 1. Check for .specify/ directory (v3+ standard) ──
39
125
  const specifyDir = resolve(projectDir, '.specify');
40
126
  if (existsSync(specifyDir)) {
41
127
  result.detected = true;
42
128
  result.specifyDir = true;
129
+ result.source = 'specify';
130
+
131
+ // Specs under .specify/specs/ (v3 standard path)
132
+ const v3Specs = scanSpecsDir(resolve(specifyDir, 'specs'));
133
+ if (v3Specs.length > 0) {
134
+ result.specs.push(...v3Specs);
135
+ }
136
+
137
+ // Constitution at .specify/memory/constitution.md (v3 standard)
138
+ const v3Constitution = resolve(specifyDir, 'memory', 'constitution.md');
139
+ if (existsSync(v3Constitution)) {
140
+ result.constitution = true;
141
+ result.constitutionPath = v3Constitution;
142
+ }
143
+
144
+ // Memory directory at .specify/memory/ (v3 standard)
145
+ const v3Memory = resolve(specifyDir, 'memory');
146
+ if (existsSync(v3Memory)) {
147
+ result.memory = true;
148
+ }
43
149
  }
44
150
 
45
- // Check for specs/ directory with spec.md files
46
- const specsDir = resolve(projectDir, 'specs');
47
- if (existsSync(specsDir)) {
48
- try {
49
- const features = readdirSync(specsDir);
50
- for (const feature of features) {
51
- const featureDir = join(specsDir, feature);
52
- try {
53
- const fstat = statSync(featureDir);
54
- if (!fstat.isDirectory()) continue;
55
- } catch { continue; }
56
-
57
- const specFile = join(featureDir, 'spec.md');
58
- const planFile = join(featureDir, 'plan.md');
59
- const tasksFile = join(featureDir, 'tasks.md');
60
-
61
- if (existsSync(specFile) || existsSync(planFile) || existsSync(tasksFile)) {
62
- result.detected = true;
63
- result.specs.push({
64
- name: feature,
65
- hasSpec: existsSync(specFile),
66
- hasPlan: existsSync(planFile),
67
- hasTasks: existsSync(tasksFile),
68
- });
69
- }
70
- }
71
- } catch { /* ignore */ }
151
+ // ── 2. Legacy paths (fallback for pre-v3 or manual setups) ──
152
+ // Only check legacy if not already detected via .specify/
153
+ if (result.specs.length === 0) {
154
+ const legacySpecs = scanSpecsDir(resolve(projectDir, 'specs'));
155
+ if (legacySpecs.length > 0) {
156
+ result.detected = true;
157
+ result.source = result.source || 'legacy';
158
+ result.specs.push(...legacySpecs);
159
+ }
72
160
  }
73
161
 
74
- // Check for constitution.md
75
- const constitutionPath = resolve(projectDir, 'constitution.md');
76
- if (existsSync(constitutionPath)) {
77
- result.detected = true;
78
- result.constitution = true;
162
+ // Constitution at project root (legacy)
163
+ if (!result.constitution) {
164
+ const rootConstitution = resolve(projectDir, 'constitution.md');
165
+ if (existsSync(rootConstitution)) {
166
+ result.detected = true;
167
+ result.constitution = true;
168
+ result.constitutionPath = rootConstitution;
169
+ result.source = result.source || 'legacy';
170
+ }
79
171
  }
80
172
 
81
- // Check for memory/ directory
82
- const memoryDir = resolve(projectDir, 'memory');
83
- if (existsSync(memoryDir)) {
84
- result.detected = true;
85
- result.memory = true;
173
+ // Memory at project root (legacy)
174
+ if (!result.memory) {
175
+ const rootMemory = resolve(projectDir, 'memory');
176
+ if (existsSync(rootMemory)) {
177
+ result.detected = true;
178
+ result.memory = true;
179
+ result.source = result.source || 'legacy';
180
+ }
86
181
  }
87
182
 
88
183
  return result;
89
184
  }
90
185
 
186
+ // ──── Quality Validation ────
187
+
188
+ /**
189
+ * Check if a markdown file contains specific section headings.
190
+ *
191
+ * @param {string} content - File content
192
+ * @param {string[]} sections - Required section heading texts
193
+ * @returns {{ found: string[], missing: string[] }}
194
+ */
195
+ function checkSections(content, sections) {
196
+ const found = [];
197
+ const missing = [];
198
+
199
+ for (const section of sections) {
200
+ // Match both "## Requirements" and "### Functional Requirements"
201
+ const pattern = new RegExp(`^#{1,4}\\s+.*${section.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}`, 'im');
202
+ if (pattern.test(content)) {
203
+ found.push(section);
204
+ } else {
205
+ missing.push(section);
206
+ }
207
+ }
208
+
209
+ return { found, missing };
210
+ }
211
+
212
+ /**
213
+ * Validate the quality of a spec.md file against spec-kit standards.
214
+ *
215
+ * Checks:
216
+ * - Has mandatory sections (User Scenarios, Requirements, Success Criteria)
217
+ * - Has FR-xxx requirement IDs
218
+ * - Has acceptance scenarios (Given/When/Then)
219
+ */
220
+ function validateSpecQuality(specPath) {
221
+ const issues = [];
222
+ const content = readFileSync(specPath, 'utf-8');
223
+
224
+ // Check mandatory sections
225
+ const { missing } = checkSections(content, SPEC_MANDATORY_SECTIONS);
226
+ for (const section of missing) {
227
+ issues.push(`Missing mandatory section: "${section}" (spec-kit spec-template.md)`);
228
+ }
229
+
230
+ // Check for FR-xxx or FR-NNN requirement IDs
231
+ const hasFRIds = /\b(FR|REQ|NFR)-\d{2,4}\b/.test(content);
232
+ if (!hasFRIds) {
233
+ issues.push('No requirement IDs found (expected FR-001, REQ-001, etc.)');
234
+ }
235
+
236
+ // Check for SC-xxx success criteria IDs
237
+ const hasSCIds = /\bSC-\d{2,4}\b/.test(content);
238
+ if (!hasSCIds) {
239
+ issues.push('No success criteria IDs found (expected SC-001, SC-002, etc.)');
240
+ }
241
+
242
+ return issues;
243
+ }
244
+
245
+ /**
246
+ * Validate the quality of a plan.md file.
247
+ */
248
+ function validatePlanQuality(planPath) {
249
+ const issues = [];
250
+ const content = readFileSync(planPath, 'utf-8');
251
+
252
+ const { missing } = checkSections(content, PLAN_MANDATORY_SECTIONS);
253
+ for (const section of missing) {
254
+ issues.push(`Missing mandatory section: "${section}" (spec-kit plan-template.md)`);
255
+ }
256
+
257
+ return issues;
258
+ }
259
+
91
260
  /**
92
- * Map Spec Kit artifact to CDD equivalent.
93
- * Returns the canonical doc name and section.
261
+ * Validate the quality of a tasks.md file.
94
262
  */
263
+ function validateTasksQuality(tasksPath) {
264
+ const issues = [];
265
+ const content = readFileSync(tasksPath, 'utf-8');
266
+
267
+ // Must have phased breakdown
268
+ const hasPhases = TASKS_MANDATORY_PATTERNS.some(p => p.test(content));
269
+ if (!hasPhases) {
270
+ issues.push('No phased task breakdown found (expected "Phase 1:", "Phase 2:", etc.)');
271
+ }
272
+
273
+ // Must have task IDs
274
+ const hasTaskIds = /\bT\d{3}\b/.test(content);
275
+ if (!hasTaskIds) {
276
+ issues.push('No task IDs found (expected T001, T002, etc.)');
277
+ }
278
+
279
+ return issues;
280
+ }
281
+
282
+ // ──── CDD Mapping ────
283
+
95
284
  const SPECKIT_CDD_MAP = {
96
285
  'spec.md': { cddDoc: 'REQUIREMENTS.md', section: 'Requirements', type: 'requirement' },
97
286
  'plan.md': { cddDoc: 'ARCHITECTURE.md', section: 'Design Decisions', type: 'design' },
98
287
  'tasks.md': { cddDoc: 'ROADMAP.md', section: 'Task Backlog', type: 'roadmap' },
99
288
  };
100
289
 
290
+ // ──── Generate from Spec Kit ────
291
+
101
292
  /**
102
293
  * Generate CDD canonical docs from Spec Kit artifacts.
103
294
  * Used by `docguard generate --from-speckit`.
104
- *
105
- * @param {string} projectDir - Project root
106
- * @param {object} config - DocGuard config
107
- * @param {object} flags - CLI flags
108
- * @returns {object} - { generated: string[], skipped: string[], errors: string[] }
109
295
  */
110
296
  export function generateFromSpecKit(projectDir, config, flags) {
111
297
  const results = { generated: [], skipped: [], errors: [] };
112
298
 
113
299
  const speckit = detectSpecKit(projectDir);
114
300
  if (!speckit.detected) {
115
- results.errors.push('No Spec Kit artifacts detected. This project does not use Spec Kit.');
301
+ results.errors.push('No Spec Kit artifacts detected. Run `specify init` to initialize, or install via: uv tool install specify-cli --from git+https://github.com/github/spec-kit.git');
116
302
  return results;
117
303
  }
118
304
 
@@ -131,8 +317,7 @@ export function generateFromSpecKit(projectDir, config, flags) {
131
317
 
132
318
  for (const spec of speckit.specs) {
133
319
  if (!spec.hasSpec) continue;
134
- const specPath = join(projectDir, 'specs', spec.name, 'spec.md');
135
- const content = readFileSync(specPath, 'utf-8');
320
+ const content = readFileSync(spec.specPath, 'utf-8');
136
321
 
137
322
  lines.push(`## ${spec.name}`);
138
323
  lines.push('');
@@ -144,20 +329,17 @@ export function generateFromSpecKit(projectDir, config, flags) {
144
329
 
145
330
  lines.push(`<!-- Generated by DocGuard from Spec Kit artifacts on ${new Date().toISOString().split('T')[0]} -->`);
146
331
 
147
- writeFileSync(reqPath, lines.join('\n'), 'utf-8');
332
+ safeWrite(reqPath, lines.join('\n'));
148
333
  results.generated.push('REQUIREMENTS.md');
149
334
  }
150
335
  }
151
336
 
152
337
  // ── Map constitution.md to AGENTS.md context ──
153
338
  if (speckit.constitution) {
154
- const constitutionPath = resolve(projectDir, 'constitution.md');
155
339
  const agentsPath = resolve(projectDir, 'AGENTS.md');
156
340
 
157
341
  if (existsSync(agentsPath)) {
158
342
  const agentsContent = readFileSync(agentsPath, 'utf-8');
159
-
160
- // Check if AGENTS.md already references constitution
161
343
  if (!agentsContent.includes('constitution.md') && !agentsContent.includes('Constitution')) {
162
344
  results.skipped.push('AGENTS.md exists but does not reference constitution.md — consider adding a reference');
163
345
  } else {
@@ -166,17 +348,27 @@ export function generateFromSpecKit(projectDir, config, flags) {
166
348
  }
167
349
  }
168
350
 
169
- // ── Map memory/ to DRIFT-LOG.md context ──
351
+ // ── Map memory/ to DRIFT-LOG.md ──
170
352
  if (speckit.memory) {
171
- results.skipped.push('memory/ directory detected — maps conceptually to DRIFT-LOG.md (no auto-generation needed)');
353
+ results.skipped.push('memory/ directory detected — maps conceptually to DRIFT-LOG.md');
172
354
  }
173
355
 
174
356
  return results;
175
357
  }
176
358
 
359
+ // ──── Guard Validator ────
360
+
177
361
  /**
178
- * Validate Spec Kit artifact consistency.
179
- * Used by guard to give CDD credit for Spec Kit artifacts.
362
+ * Validate Spec Kit integration quality.
363
+ *
364
+ * When spec-kit is NOT detected:
365
+ * - Shows 1 informational warning suggesting spec-kit
366
+ *
367
+ * When spec-kit IS detected:
368
+ * - Validates spec.md quality (mandatory sections, FR-IDs, SC-IDs)
369
+ * - Validates plan.md quality (mandatory sections)
370
+ * - Validates tasks.md quality (phased breakdown, T-IDs)
371
+ * - Checks constitution → AGENTS.md mapping
180
372
  *
181
373
  * @returns {{ errors: string[], warnings: string[], passed: number, total: number }}
182
374
  */
@@ -185,41 +377,80 @@ export function validateSpecKitIntegration(projectDir, config) {
185
377
 
186
378
  const speckit = detectSpecKit(projectDir);
187
379
 
188
- // If no Spec Kit detected, silently pass
380
+ // If no Spec Kit detected, suggest it
189
381
  if (!speckit.detected) {
382
+ results.total++;
383
+ results.warnings.push(
384
+ 'No Spec Kit artifacts detected. Consider `specify init` for spec-driven development (github.com/github/spec-kit)'
385
+ );
190
386
  return results;
191
387
  }
192
388
 
193
- // Check 1: .specify/ directory exists → Spec Kit initialized
389
+ // ── Check 1: .specify/ directory exists ──
194
390
  results.total++;
195
391
  if (speckit.specifyDir) {
196
392
  results.passed++;
197
393
  } else {
198
- results.warnings.push('Spec Kit artifacts detected but .specify/ directory missing. Run `specify init` to initialize');
394
+ results.warnings.push(
395
+ 'Spec Kit artifacts found but .specify/ directory missing. Run `specify init` to create standard structure'
396
+ );
199
397
  }
200
398
 
201
- // Check 2: Each spec has corresponding canonical doc coverage
399
+ // ── Check 2: Validate each spec's quality ──
202
400
  for (const spec of speckit.specs) {
203
- if (spec.hasSpec) {
401
+ // 2a: spec.md quality
402
+ if (spec.hasSpec && spec.specPath) {
204
403
  results.total++;
205
- // Check if spec.md content appears in any canonical doc
206
- const specPath = join(projectDir, 'specs', spec.name, 'spec.md');
207
- const content = readFileSync(specPath, 'utf-8');
404
+ try {
405
+ const issues = validateSpecQuality(spec.specPath);
406
+ if (issues.length === 0) {
407
+ results.passed++;
408
+ } else {
409
+ for (const issue of issues) {
410
+ results.warnings.push(`specs/${spec.name}/spec.md: ${issue}`);
411
+ }
412
+ }
413
+ } catch {
414
+ results.warnings.push(`specs/${spec.name}/spec.md: Could not read file`);
415
+ }
416
+ }
208
417
 
209
- // Look for requirement IDs in the spec
210
- const hasReqIds = /\b(REQ|FR|NFR|US|STORY|AC)-\d{2,4}\b/.test(content);
418
+ // 2b: plan.md quality
419
+ if (spec.hasPlan && spec.planPath) {
420
+ results.total++;
421
+ try {
422
+ const issues = validatePlanQuality(spec.planPath);
423
+ if (issues.length === 0) {
424
+ results.passed++;
425
+ } else {
426
+ for (const issue of issues) {
427
+ results.warnings.push(`specs/${spec.name}/plan.md: ${issue}`);
428
+ }
429
+ }
430
+ } catch {
431
+ results.warnings.push(`specs/${spec.name}/plan.md: Could not read file`);
432
+ }
433
+ }
211
434
 
212
- if (hasReqIds) {
213
- results.passed++;
214
- } else {
215
- results.warnings.push(
216
- `specs/${spec.name}/spec.md has no requirement IDs. Add IDs (e.g., REQ-001) for traceability`
217
- );
435
+ // 2c: tasks.md quality
436
+ if (spec.hasTasks && spec.tasksPath) {
437
+ results.total++;
438
+ try {
439
+ const issues = validateTasksQuality(spec.tasksPath);
440
+ if (issues.length === 0) {
441
+ results.passed++;
442
+ } else {
443
+ for (const issue of issues) {
444
+ results.warnings.push(`specs/${spec.name}/tasks.md: ${issue}`);
445
+ }
446
+ }
447
+ } catch {
448
+ results.warnings.push(`specs/${spec.name}/tasks.md: Could not read file`);
218
449
  }
219
450
  }
220
451
  }
221
452
 
222
- // Check 3: Constitution mapped to AGENTS.md
453
+ // ── Check 3: Constitution AGENTS.md mapping ──
223
454
  if (speckit.constitution) {
224
455
  results.total++;
225
456
  const agentsPath = resolve(projectDir, 'AGENTS.md');
@@ -72,6 +72,7 @@ const TRACE_MAP = {
72
72
 
73
73
  // ──── Default requirement ID patterns ────
74
74
  // Users can override via config.traceability.requirementPattern
75
+ // Includes spec-kit standard IDs: FR-xxx, SC-xxx, T-xxx
75
76
  const DEFAULT_REQ_PATTERNS = [
76
77
  /\b(REQ)-(\d{2,4})\b/g,
77
78
  /\b(FR)-(\d{2,4})\b/g,
@@ -83,6 +84,8 @@ const DEFAULT_REQ_PATTERNS = [
83
84
  /\b(SYS)-(\d{2,4})\b/g,
84
85
  /\b(ARCH)-(\d{2,4})\b/g,
85
86
  /\b(MOD)-(\d{2,4})\b/g,
87
+ /\b(SC)-(\d{2,4})\b/g, // Spec Kit: Success Criteria
88
+ /\b(T)(\d{3,4})\b/g, // Spec Kit: Task IDs (T001, T002)
86
89
  ];
87
90
 
88
91
  /**
@@ -305,15 +308,20 @@ function getRequirementDocPaths(projectDir, config) {
305
308
  if (existsSync(p) && !paths.includes(p)) paths.push(p);
306
309
  }
307
310
 
308
- // Spec Kit artifacts: specs/*/spec.md
309
- const specsDir = resolve(projectDir, 'specs');
310
- if (existsSync(specsDir)) {
311
- try {
312
- for (const feature of readdirSync(specsDir)) {
313
- const specPath = join(specsDir, feature, 'spec.md');
314
- if (existsSync(specPath)) paths.push(specPath);
315
- }
316
- } catch { /* ignore */ }
311
+ // Spec Kit artifacts: .specify/specs/*/spec.md (v3+) and specs/*/spec.md (legacy)
312
+ const specKitDirs = [
313
+ resolve(projectDir, '.specify', 'specs'), // v3+ standard
314
+ resolve(projectDir, 'specs'), // legacy
315
+ ];
316
+ for (const specsDir of specKitDirs) {
317
+ if (existsSync(specsDir)) {
318
+ try {
319
+ for (const feature of readdirSync(specsDir)) {
320
+ const specPath = join(specsDir, feature, 'spec.md');
321
+ if (existsSync(specPath) && !paths.includes(specPath)) paths.push(specPath);
322
+ }
323
+ } catch { /* ignore */ }
324
+ }
317
325
  }
318
326
 
319
327
  return paths;
@@ -0,0 +1,45 @@
1
+ ---
2
+ description: Generate AI prompts to fix specific documentation issues identified by DocGuard
3
+ ---
4
+
5
+ # DocGuard Fix — AI-Assisted Documentation Repair
6
+
7
+ Generate targeted fix prompts for specific documentation issues.
8
+
9
+ ## What to do
10
+
11
+ 1. First, identify what needs fixing:
12
+ ```bash
13
+ npx docguard-cli diagnose
14
+ ```
15
+
16
+ 2. Generate fix prompts for specific documents:
17
+ ```bash
18
+ # Fix a specific canonical doc
19
+ npx docguard-cli fix --doc architecture
20
+ npx docguard-cli fix --doc security
21
+ npx docguard-cli fix --doc test-spec
22
+ npx docguard-cli fix --doc data-model
23
+ npx docguard-cli fix --doc environment
24
+ ```
25
+
26
+ 3. The fix command generates a detailed AI prompt. Read the prompt and execute its instructions:
27
+ - It will reference the project's actual code structure
28
+ - It will list specific sections to add or update
29
+ - It will include examples aligned with the project's tech stack
30
+
31
+ 4. After fixing, verify the improvement:
32
+ ```bash
33
+ npx docguard-cli guard
34
+ ```
35
+
36
+ 5. If the project uses Spec Kit, ensure specs align with spec-kit templates:
37
+ - `spec.md` must have: User Scenarios, Requirements (FR-IDs), Success Criteria (SC-IDs)
38
+ - `plan.md` must have: Summary, Technical Context, Project Structure
39
+ - `tasks.md` must have: Phased breakdown (Phase 1, 2, 3+), Task IDs (T001+)
40
+
41
+ ## Important
42
+
43
+ - Never overwrite existing documentation without creating a `.bak` backup
44
+ - Use `--force` only when explicitly instructed by the user
45
+ - Log any deviations from canonical docs in DRIFT-LOG.md with `// DRIFT: reason`
@@ -0,0 +1,27 @@
1
+ ---
2
+ description: Run DocGuard guard validation — check project documentation quality against CDD standards
3
+ ---
4
+
5
+ # DocGuard Guard — Documentation Quality Gate
6
+
7
+ Run the DocGuard CLI to validate all documentation against Canonical-Driven Development standards.
8
+
9
+ ## What to do
10
+
11
+ 1. Run the guard command:
12
+ ```bash
13
+ npx docguard-cli guard
14
+ ```
15
+
16
+ 2. Review the output. Each validator reports ✅ (pass) or ❌ (fail):
17
+ - **Structure**: Required CDD files exist
18
+ - **Doc Sections**: Canonical docs have required sections
19
+ - **Drift**: Code deviations logged in DRIFT-LOG.md
20
+ - **Test-Spec**: Tests match TEST-SPEC.md rules
21
+ - **Security**: No hardcoded secrets
22
+ - **Spec-Kit**: Spec quality validation (FR-IDs, sections)
23
+ - **Doc-Quality**: Readability and writing quality
24
+
25
+ 3. If any checks fail, run `docguard diagnose` for a remediation plan.
26
+
27
+ 4. All checks must pass before committing. Exit code 0 = pass, 1 = fail, 2 = warnings only.
@@ -0,0 +1,35 @@
1
+ ---
2
+ description: Review documentation quality — identify improvements and suggest fixes based on CDD and spec-kit standards
3
+ ---
4
+
5
+ # DocGuard Review — Documentation Quality Analysis
6
+
7
+ Analyze the project's documentation quality and suggest specific improvements.
8
+
9
+ ## What to do
10
+
11
+ 1. Run the full diagnostic:
12
+ ```bash
13
+ npx docguard-cli diagnose
14
+ ```
15
+
16
+ 2. Run the scoring engine:
17
+ ```bash
18
+ npx docguard-cli score
19
+ ```
20
+
21
+ 3. For each issue found, analyze the root cause:
22
+ - **Missing sections**: Check which mandatory sections are absent from canonical docs
23
+ - **Low readability**: Identify overly complex sentences or passive voice
24
+ - **Drift**: Find `// DRIFT:` comments in code not logged in DRIFT-LOG.md
25
+ - **Stale docs**: Compare doc modification dates against code changes
26
+ - **Spec quality**: Verify specs have FR-IDs, SC-IDs, and Given/When/Then scenarios
27
+
28
+ 4. Suggest specific improvements for each issue. Quote the relevant section and propose the fix.
29
+
30
+ 5. Prioritize fixes by impact:
31
+ - 🔴 HIGH: Missing docs, security issues, broken traceability
32
+ - 🟡 MEDIUM: Low readability, missing sections, stale content
33
+ - 🟢 LOW: Minor formatting, missing badges, metadata sync
34
+
35
+ 6. After making changes, re-run `npx docguard-cli guard` to verify improvements.
@@ -0,0 +1,42 @@
1
+ ---
2
+ description: Show CDD maturity score — comprehensive documentation health assessment with category breakdown
3
+ ---
4
+
5
+ # DocGuard Score — CDD Maturity Assessment
6
+
7
+ Calculate and display the project's Canonical-Driven Development maturity score.
8
+
9
+ ## What to do
10
+
11
+ 1. Run the scoring engine:
12
+ ```bash
13
+ npx docguard-cli score
14
+ ```
15
+
16
+ 2. For JSON output (CI/CD integration):
17
+ ```bash
18
+ npx docguard-cli score --format json
19
+ ```
20
+
21
+ 3. Interpret the results:
22
+
23
+ | Grade | Score | Meaning |
24
+ |-------|-------|---------|
25
+ | A+ | 95-100 | Exemplary — production-grade documentation |
26
+ | A | 85-94 | Strong — minor improvements possible |
27
+ | B | 70-84 | Good — some gaps to address |
28
+ | C | 50-69 | Fair — significant documentation debt |
29
+ | D | 30-49 | Poor — major gaps in documentation |
30
+ | F | 0-29 | Critical — documentation infrastructure missing |
31
+
32
+ 4. Focus on the category breakdown:
33
+ - **Structure** (25%): Do required files exist?
34
+ - **Doc Quality** (20%): Readability, completeness, sections
35
+ - **Testing** (15%): Test coverage documentation
36
+ - **Security** (10%): Security documentation
37
+ - **Environment** (10%): Environment variable documentation
38
+ - **Drift** (10%): Deviation tracking
39
+ - **Changelog** (5%): Change log maintenance
40
+ - **Architecture** (5%): Architecture documentation
41
+
42
+ 5. Prioritize improvements by category weight — fixing Structure issues has the highest impact.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docguard-cli",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "description": "The enforcement tool for Canonical-Driven Development (CDD). Audit, generate, and guard your project documentation.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -24,7 +24,8 @@
24
24
  "documentation",
25
25
  "governance",
26
26
  "ai-agents",
27
- "spec-guard",
27
+ "spec-kit",
28
+ "spec-driven-development",
28
29
  "docguard",
29
30
  "validation",
30
31
  "cli",
@@ -47,6 +48,7 @@
47
48
  "files": [
48
49
  "cli/",
49
50
  "templates/",
51
+ "commands/",
50
52
  "docs/",
51
53
  "STANDARD.md",
52
54
  "PHILOSOPHY.md",
@@ -0,0 +1,68 @@
1
+ # Requirements
2
+
3
+ <!-- docguard:version 0.1.0 -->
4
+ <!-- docguard:status draft -->
5
+
6
+ > Tracks functional requirements, non-functional requirements, and success criteria.
7
+ > Use requirement IDs (FR-001, NFR-001, SC-001) for traceability back to code and tests.
8
+
9
+ ![CDD Canonical](https://img.shields.io/badge/CDD-Canonical-blue)
10
+
11
+ ## Functional Requirements
12
+
13
+ <!-- List functional requirements with FR-NNN IDs -->
14
+
15
+ | ID | Priority | Requirement | Status | Test Coverage |
16
+ |----|----------|-------------|--------|---------------|
17
+ | FR-001 | P1 | System MUST [capability] | 🔴 Draft | ❌ |
18
+ | FR-002 | P1 | System MUST [capability] | 🔴 Draft | ❌ |
19
+ | FR-003 | P2 | Users MUST be able to [interaction] | 🔴 Draft | ❌ |
20
+
21
+ ## Non-Functional Requirements
22
+
23
+ <!-- Quality attributes: performance, security, reliability -->
24
+
25
+ | ID | Category | Requirement | Metric |
26
+ |----|----------|-------------|--------|
27
+ | NFR-001 | Performance | Response time < 200ms p95 | Measured via [tool] |
28
+ | NFR-002 | Security | All endpoints require authentication | Validated by guard |
29
+ | NFR-003 | Reliability | 99.9% uptime SLA | Monitored via [tool] |
30
+
31
+ ## Success Criteria
32
+
33
+ <!-- Measurable outcomes — aligned with spec-kit SC-NNN format -->
34
+
35
+ | ID | Criteria | Measurement | Target |
36
+ |----|----------|-------------|--------|
37
+ | SC-001 | [Measurable user outcome] | [How measured] | [Target value] |
38
+ | SC-002 | [Performance metric] | [How measured] | [Target value] |
39
+
40
+ ## User Scenarios
41
+
42
+ <!-- Spec-kit aligned: Given/When/Then acceptance scenarios -->
43
+
44
+ ### User Story 1 - [Title] (Priority: P1)
45
+
46
+ [Description of user journey]
47
+
48
+ **Acceptance Scenarios**:
49
+ 1. **Given** [initial state], **When** [action], **Then** [expected outcome]
50
+ 2. **Given** [initial state], **When** [action], **Then** [expected outcome]
51
+
52
+ ## Traceability Matrix
53
+
54
+ <!-- Maps requirements → code → tests -->
55
+
56
+ | Requirement | Source File | Test File | Status |
57
+ |-------------|------------|-----------|--------|
58
+ | FR-001 | `src/[file]` | `tests/[file]` | ❌ |
59
+
60
+ ## Revision History
61
+
62
+ | Version | Date | Author | Changes |
63
+ |---------|------|--------|---------|
64
+ | 0.1.0 | YYYY-MM-DD | DocGuard Init | Initial template |
65
+
66
+ ---
67
+
68
+ *Generated by [DocGuard](https://github.com/raccioly/docguard) — aligned with [Spec Kit](https://github.com/github/spec-kit) standards.*