docguard-cli 0.9.3 → 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.
- package/cli/commands/diagnose.mjs +2 -2
- package/cli/commands/generate.mjs +49 -10
- package/cli/commands/init.mjs +1 -0
- package/cli/scanners/speckit.mjs +318 -87
- package/cli/validators/traceability.mjs +17 -9
- package/commands/docguard.fix.md +45 -0
- package/commands/docguard.guard.md +27 -0
- package/commands/docguard.review.md +35 -0
- package/commands/docguard.score.md +42 -0
- package/package.json +4 -2
- package/templates/REQUIREMENTS.md.template +68 -0
|
@@ -120,10 +120,10 @@ export function runDiagnose(projectDir, config, flags) {
|
|
|
120
120
|
});
|
|
121
121
|
} catch { /* init may partially succeed */ }
|
|
122
122
|
|
|
123
|
-
// Run generate to fill in content
|
|
123
|
+
// Run generate to fill in MISSING content only (never --force, which would overwrite existing docs)
|
|
124
124
|
try {
|
|
125
125
|
const cliPath = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'docguard.mjs');
|
|
126
|
-
execSync(`node "${cliPath}" generate --dir "${projectDir}"
|
|
126
|
+
execSync(`node "${cliPath}" generate --dir "${projectDir}"`, {
|
|
127
127
|
encoding: 'utf-8',
|
|
128
128
|
stdio: 'pipe',
|
|
129
129
|
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* This is the "killer feature" — take any project and auto-generate CDD docs.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
9
9
|
import { resolve, join, extname, basename, relative, dirname } from 'node:path';
|
|
10
10
|
import { c } from '../shared.mjs';
|
|
11
11
|
import { detectDocTools } from '../scanners/doc-tools.mjs';
|
|
@@ -18,6 +18,30 @@ const IGNORE_DIRS = new Set([
|
|
|
18
18
|
'.amplify-hosting', '.serverless',
|
|
19
19
|
]);
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Create a .bak backup of an existing file before --force overwrites it.
|
|
23
|
+
* Only backs up if the file exists and has content.
|
|
24
|
+
*/
|
|
25
|
+
function backupFile(filePath) {
|
|
26
|
+
if (existsSync(filePath)) {
|
|
27
|
+
try {
|
|
28
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
29
|
+
if (content.trim().length > 0) {
|
|
30
|
+
copyFileSync(filePath, filePath + '.bak');
|
|
31
|
+
}
|
|
32
|
+
} catch { /* backup failure is non-fatal */ }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Safe write — creates a .bak backup before overwriting existing files.
|
|
38
|
+
* Call this instead of raw writeFileSync when generating docs.
|
|
39
|
+
*/
|
|
40
|
+
function safeWrite(filePath, content) {
|
|
41
|
+
backupFile(filePath);
|
|
42
|
+
writeFileSync(filePath, content, 'utf-8');
|
|
43
|
+
}
|
|
44
|
+
|
|
21
45
|
const CODE_EXTENSIONS = new Set([
|
|
22
46
|
'.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx',
|
|
23
47
|
'.py', '.java', '.go', '.rs', '.rb', '.php', '.cs',
|
|
@@ -137,6 +161,21 @@ export function runGenerate(projectDir, config, flags) {
|
|
|
137
161
|
mkdirSync(docsDir, { recursive: true });
|
|
138
162
|
}
|
|
139
163
|
|
|
164
|
+
// ── Safety: warn if --force will overwrite existing files ──
|
|
165
|
+
if (flags.force) {
|
|
166
|
+
const targetFiles = [
|
|
167
|
+
'docs-canonical/ARCHITECTURE.md', 'docs-canonical/API-REFERENCE.md',
|
|
168
|
+
'docs-canonical/DATA-MODEL.md', 'docs-canonical/ENVIRONMENT.md',
|
|
169
|
+
'docs-canonical/TEST-SPEC.md', 'docs-canonical/SECURITY.md',
|
|
170
|
+
'AGENTS.md', 'CHANGELOG.md', 'DRIFT-LOG.md',
|
|
171
|
+
];
|
|
172
|
+
const existing = targetFiles.filter(f => existsSync(resolve(projectDir, f)));
|
|
173
|
+
if (existing.length > 0) {
|
|
174
|
+
console.log(` ${c.yellow}⚠️ --force: ${existing.length} existing file(s) will be overwritten.${c.reset}`);
|
|
175
|
+
console.log(` ${c.dim} Backups saved as .bak files.${c.reset}\n`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
140
179
|
let created = 0;
|
|
141
180
|
let skipped = 0;
|
|
142
181
|
|
|
@@ -633,7 +672,7 @@ See \\\`docs-canonical/KNOWN-GOTCHAS.md\\\` for known issues.
|
|
|
633
672
|
| 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (arc42 + C4 aligned) |
|
|
634
673
|
`;
|
|
635
674
|
|
|
636
|
-
|
|
675
|
+
safeWrite(path, appendStandardsCitation(content, 'ARCHITECTURE.md'), 'utf-8');
|
|
637
676
|
console.log(` ${c.green}✅ ARCHITECTURE.md${c.reset} (arc42 §1-§12, ${componentRows.length} components, ${Object.values(stack).filter(Boolean).length} tech)`);
|
|
638
677
|
return true;
|
|
639
678
|
}
|
|
@@ -730,7 +769,7 @@ ${resourceSections}
|
|
|
730
769
|
| 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${deepRoutes.length} endpoints from ${deepRoutes[0]?.source || 'code'}) |
|
|
731
770
|
`;
|
|
732
771
|
|
|
733
|
-
|
|
772
|
+
safeWrite(path, appendStandardsCitation(content, 'API-REFERENCE.md'), 'utf-8');
|
|
734
773
|
console.log(` ${c.green}✅ API-REFERENCE.md${c.reset} (${deepRoutes.length} endpoints, ${Object.keys(groups).length} resources)`);
|
|
735
774
|
return true;
|
|
736
775
|
}
|
|
@@ -885,7 +924,7 @@ ${erDiagram}
|
|
|
885
924
|
| 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${entities.length} entities, ${relationships.length} relationships from ${schemaSource}) |
|
|
886
925
|
`;
|
|
887
926
|
|
|
888
|
-
|
|
927
|
+
safeWrite(path, appendStandardsCitation(content, 'DATA-MODEL.md'), 'utf-8');
|
|
889
928
|
console.log(` ${c.green}✅ DATA-MODEL.md${c.reset} (${entities.length} entities, ${relationships.length} relationships from ${schemaSource})`);
|
|
890
929
|
return true;
|
|
891
930
|
}
|
|
@@ -948,7 +987,7 @@ ${envVarRows || '| <!-- No .env.example found --> | | | | |'}
|
|
|
948
987
|
| 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated (${scan.envVars.length} env vars found) |
|
|
949
988
|
`;
|
|
950
989
|
|
|
951
|
-
|
|
990
|
+
safeWrite(path, appendStandardsCitation(content, 'ENVIRONMENT.md'), 'utf-8');
|
|
952
991
|
console.log(` ${c.green}✅ ENVIRONMENT.md${c.reset} (${scan.envVars.length} env vars detected)`);
|
|
953
992
|
return true;
|
|
954
993
|
}
|
|
@@ -1033,7 +1072,7 @@ ${serviceRows || '| <!-- No services found --> | | | |'}
|
|
|
1033
1072
|
| 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) |
|
|
1034
1073
|
`;
|
|
1035
1074
|
|
|
1036
|
-
|
|
1075
|
+
safeWrite(path, appendStandardsCitation(content, 'TEST-SPEC.md'), 'utf-8');
|
|
1037
1076
|
console.log(` ${c.green}✅ TEST-SPEC.md${c.reset} (${scan.tests.length} tests, ${serviceMap.filter(s => s.status === '✅').length}/${serviceMap.length} services mapped)`);
|
|
1038
1077
|
return true;
|
|
1039
1078
|
}
|
|
@@ -1099,7 +1138,7 @@ ${scan.envVars.filter(v => isSecretVar(v.name)).map(v =>
|
|
|
1099
1138
|
| 0.1.0 | ${new Date().toISOString().split('T')[0]} | DocGuard Generate | Auto-generated |
|
|
1100
1139
|
`;
|
|
1101
1140
|
|
|
1102
|
-
|
|
1141
|
+
safeWrite(path, appendStandardsCitation(content, 'SECURITY.md'), 'utf-8');
|
|
1103
1142
|
console.log(` ${c.green}✅ SECURITY.md${c.reset} (auth: ${stack.auth || 'not detected'})`);
|
|
1104
1143
|
return true;
|
|
1105
1144
|
}
|
|
@@ -1209,7 +1248,7 @@ npx docguard-cli generate # Generate docs from code
|
|
|
1209
1248
|
- Test requirements in TEST-SPEC.md must be met
|
|
1210
1249
|
- Documentation changes must pass \`docguard guard\`
|
|
1211
1250
|
`;
|
|
1212
|
-
|
|
1251
|
+
safeWrite(agentsPath, content);
|
|
1213
1252
|
console.log(` ${c.green}✅ AGENTS.md${c.reset} (AGENTS.md standard compliant)`);
|
|
1214
1253
|
created++;
|
|
1215
1254
|
} else {
|
|
@@ -1231,7 +1270,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
1231
1270
|
### Added
|
|
1232
1271
|
- CDD documentation via DocGuard generate
|
|
1233
1272
|
`;
|
|
1234
|
-
|
|
1273
|
+
safeWrite(changelogPath, content);
|
|
1235
1274
|
console.log(` ${c.green}✅ CHANGELOG.md${c.reset}`);
|
|
1236
1275
|
created++;
|
|
1237
1276
|
} else {
|
|
@@ -1251,7 +1290,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
1251
1290
|
|------|------|---------------|-------------------|----------|------------|
|
|
1252
1291
|
| | | | | | |
|
|
1253
1292
|
`;
|
|
1254
|
-
|
|
1293
|
+
safeWrite(driftPath, content);
|
|
1255
1294
|
console.log(` ${c.green}✅ DRIFT-LOG.md${c.reset}`);
|
|
1256
1295
|
created++;
|
|
1257
1296
|
} else {
|
package/cli/commands/init.mjs
CHANGED
|
@@ -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;
|
package/cli/scanners/speckit.mjs
CHANGED
|
@@ -1,30 +1,114 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Spec Kit Scanner — Detect and integrate with GitHub Spec Kit
|
|
2
|
+
* Spec Kit Scanner — Detect, validate, and integrate with GitHub Spec Kit
|
|
3
3
|
*
|
|
4
|
-
* Auto-detects
|
|
5
|
-
*
|
|
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
|
-
*
|
|
9
|
-
* .specify/
|
|
10
|
-
* specs/
|
|
11
|
-
* specs/
|
|
12
|
-
* specs/
|
|
13
|
-
* constitution.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
|
-
*
|
|
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,
|
|
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
|
-
*
|
|
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
|
-
//
|
|
46
|
-
|
|
47
|
-
if (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
*
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
179
|
-
*
|
|
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,
|
|
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
|
|
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(
|
|
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:
|
|
399
|
+
// ── Check 2: Validate each spec's quality ──
|
|
202
400
|
for (const spec of speckit.specs) {
|
|
203
|
-
|
|
401
|
+
// 2a: spec.md quality
|
|
402
|
+
if (spec.hasSpec && spec.specPath) {
|
|
204
403
|
results.total++;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
210
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
|
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
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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.
|
|
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-
|
|
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
|
+

|
|
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.*
|