docguard-cli 0.11.1 → 0.11.2
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/diff.mjs +15 -17
- package/cli/commands/guard.mjs +12 -2
- package/cli/docguard.mjs +2 -0
- package/cli/ensure-skills.mjs +8 -1
- package/cli/shared-source.mjs +17 -6
- package/cli/validators/docs-coverage.mjs +4 -1
- package/cli/validators/environment.mjs +9 -3
- package/extensions/spec-kit-docguard/extension.yml +1 -1
- package/extensions/spec-kit-docguard/skills/docguard-fix/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-guard/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-review/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-score/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-sync/SKILL.md +1 -1
- package/package.json +1 -1
package/cli/commands/diff.mjs
CHANGED
|
@@ -10,6 +10,8 @@ import { collectPackageJsons, detectDocker, grepEnvUsage, resolveSourceRoots } f
|
|
|
10
10
|
import { parseApiReferenceDoc, compareEndpoints } from '../scanners/api-doc.mjs';
|
|
11
11
|
import { resolveApiSurface } from '../validators/api-surface.mjs';
|
|
12
12
|
import { collectCodeTests } from '../validators/docs-diff.mjs';
|
|
13
|
+
import { scanSchemasDeep } from '../scanners/schemas.mjs';
|
|
14
|
+
import { detectDocTools } from '../scanners/doc-tools.mjs';
|
|
13
15
|
|
|
14
16
|
const IGNORE_DIRS = new Set([
|
|
15
17
|
'node_modules', '.git', '.next', 'dist', 'build',
|
|
@@ -163,22 +165,17 @@ function diffEntities(dir, config = {}) {
|
|
|
163
165
|
docEntities.add(name.toLowerCase());
|
|
164
166
|
}
|
|
165
167
|
|
|
166
|
-
//
|
|
168
|
+
// Use the REAL exported entity names from scanSchemasDeep, not file basenames
|
|
169
|
+
// (a file `dynamoModels.ts` exports `User`/`Order`/etc. — its basename is not
|
|
170
|
+
// an entity). scanSchemasDeep covers JS ORMs, SQLAlchemy/Pydantic, Diesel,
|
|
171
|
+
// Go structs, JPA, Rails, and OpenAPI schemas.
|
|
172
|
+
const docTools = detectDocTools(dir);
|
|
173
|
+
const schemas = scanSchemasDeep(dir, {}, docTools);
|
|
167
174
|
const codeEntities = new Set();
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const modelDir = join(root, sub);
|
|
173
|
-
if (!existsSync(modelDir)) continue;
|
|
174
|
-
const files = getFilesRecursive(modelDir);
|
|
175
|
-
for (const f of files) {
|
|
176
|
-
const name = basename(f, extname(f)).toLowerCase();
|
|
177
|
-
// Skip non-entity infrastructure/aggregation filenames.
|
|
178
|
-
if (CODE_ENTITY_NOISE.has(name)) continue;
|
|
179
|
-
codeEntities.add(name);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
175
|
+
for (const e of (schemas.entities || [])) {
|
|
176
|
+
const n = String(e.name || '').toLowerCase();
|
|
177
|
+
if (!n || CODE_ENTITY_NOISE.has(n)) continue;
|
|
178
|
+
codeEntities.add(n);
|
|
182
179
|
}
|
|
183
180
|
|
|
184
181
|
// No code-side entity source (e.g. DynamoDB single-table design with no model
|
|
@@ -207,7 +204,8 @@ function diffEnvVars(dir, config = {}) {
|
|
|
207
204
|
|
|
208
205
|
// Extract env var names from ENVIRONMENT.md
|
|
209
206
|
const docVars = new Set();
|
|
210
|
-
|
|
207
|
+
// Reject names ending in `_` (e.g. the literal prefix `VITE_` in prose).
|
|
208
|
+
const varRegex = /`([A-Z][A-Z0-9_]*[A-Z0-9])`/g;
|
|
211
209
|
let match;
|
|
212
210
|
while ((match = varRegex.exec(content)) !== null) {
|
|
213
211
|
docVars.add(match[1]);
|
|
@@ -220,7 +218,7 @@ function diffEnvVars(dir, config = {}) {
|
|
|
220
218
|
const envExamplePath = resolve(dir, envFile);
|
|
221
219
|
if (existsSync(envExamplePath)) {
|
|
222
220
|
const envContent = readFileSync(envExamplePath, 'utf-8');
|
|
223
|
-
const envRegex = /^([A-Z][A-Z0-9_]
|
|
221
|
+
const envRegex = /^([A-Z][A-Z0-9_]*[A-Z0-9])\s*=/gm;
|
|
224
222
|
while ((match = envRegex.exec(envContent)) !== null) {
|
|
225
223
|
codeVars.add(match[1]);
|
|
226
224
|
}
|
package/cli/commands/guard.mjs
CHANGED
|
@@ -194,16 +194,26 @@ export function runGuard(projectDir, config, flags) {
|
|
|
194
194
|
console.log(` ${c.yellow}⚠️ ${v.name}${c.reset} ${qBadge}${c.dim} ${v.passed}/${v.total} checks passed${c.reset}`);
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
|
|
197
|
+
// --show-failing forces enumeration of every error/warning regardless of
|
|
198
|
+
// overall validator status — useful when a validator passes overall
|
|
199
|
+
// (passed < total) without surfacing the specific failing checks.
|
|
200
|
+
const show = flags.verbose || flags.showFailing;
|
|
201
|
+
if (show || v.status === 'fail') {
|
|
198
202
|
for (const err of v.errors) {
|
|
199
203
|
console.log(` ${c.red}✗ ${err}${c.reset}`);
|
|
200
204
|
}
|
|
201
205
|
}
|
|
202
|
-
if (
|
|
206
|
+
if (show || v.status === 'warn') {
|
|
203
207
|
for (const warn of v.warnings) {
|
|
204
208
|
console.log(` ${c.yellow}⚠ ${warn}${c.reset}`);
|
|
205
209
|
}
|
|
206
210
|
}
|
|
211
|
+
// If a validator reports passed < total but has no errors/warnings, surface
|
|
212
|
+
// the gap honestly so users aren't left wondering where the deficit went.
|
|
213
|
+
if (v.status === 'pass' && v.total > v.passed && v.errors.length === 0 && v.warnings.length === 0) {
|
|
214
|
+
const gap = v.total - v.passed;
|
|
215
|
+
console.log(` ${c.yellow}⚠ ${gap} check(s) did not pass but emitted no message — likely a validator bug. Please file an issue.${c.reset}`);
|
|
216
|
+
}
|
|
207
217
|
}
|
|
208
218
|
|
|
209
219
|
// Summary
|
package/cli/docguard.mjs
CHANGED
|
@@ -365,6 +365,8 @@ async function main() {
|
|
|
365
365
|
} else if (args[i] === '--since' && args[i + 1]) {
|
|
366
366
|
flags.since = args[i + 1];
|
|
367
367
|
i++;
|
|
368
|
+
} else if (args[i] === '--show-failing') {
|
|
369
|
+
flags.showFailing = true;
|
|
368
370
|
} else if (args[i] === '--doc' && args[i + 1]) {
|
|
369
371
|
flags.doc = args[i + 1];
|
|
370
372
|
i++;
|
package/cli/ensure-skills.mjs
CHANGED
|
@@ -54,8 +54,13 @@ export function detectAgentMode(projectDir) {
|
|
|
54
54
|
'.specify',
|
|
55
55
|
'.github/copilot-instructions.md',
|
|
56
56
|
'CLAUDE.md',
|
|
57
|
+
'GEMINI.md',
|
|
57
58
|
'.gemini',
|
|
58
59
|
'.agents',
|
|
60
|
+
'.antigravity',
|
|
61
|
+
'ANTIGRAVITY.md',
|
|
62
|
+
'.kiro',
|
|
63
|
+
'.windsurf',
|
|
59
64
|
];
|
|
60
65
|
|
|
61
66
|
for (const signal of llmSignals) {
|
|
@@ -105,7 +110,9 @@ export function detectAIAgent(projectDir) {
|
|
|
105
110
|
{ signal: '.claude', agent: 'claude' },
|
|
106
111
|
{ signal: 'CLAUDE.md', agent: 'claude' },
|
|
107
112
|
{ signal: '.gemini', agent: 'gemini' },
|
|
108
|
-
{ signal: '.agents', agent: 'agy' }, // Antigravity
|
|
113
|
+
{ signal: '.agents', agent: 'agy' }, // Antigravity (Spec Kit convention)
|
|
114
|
+
{ signal: '.antigravity', agent: 'agy' }, // Antigravity (alt convention)
|
|
115
|
+
{ signal: 'ANTIGRAVITY.md', agent: 'agy' }, // Antigravity rules file
|
|
109
116
|
{ signal: '.github/copilot-instructions.md', agent: 'copilot' },
|
|
110
117
|
{ signal: '.windsurf', agent: 'windsurf' },
|
|
111
118
|
{ signal: '.codex', agent: 'codex' },
|
package/cli/shared-source.mjs
CHANGED
|
@@ -210,11 +210,16 @@ export function grepEnvUsage(projectDir, config = {}) {
|
|
|
210
210
|
const roots = resolveSourceRoots(projectDir, config);
|
|
211
211
|
const seen = new Set();
|
|
212
212
|
|
|
213
|
+
// Require names to start with a letter and END with a letter/digit (NOT an
|
|
214
|
+
// underscore) — fixes "VITE_" being captured as a literal env var name.
|
|
215
|
+
const NAME = '([A-Z][A-Z0-9_]*[A-Z0-9])';
|
|
213
216
|
const patterns = [
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
+
new RegExp(`process\\.env\\.${NAME}`, 'g'),
|
|
218
|
+
new RegExp(`process\\.env\\[\\s*['"]${NAME}['"]\\s*\\]`, 'g'),
|
|
219
|
+
new RegExp(`import\\.meta\\.env\\.${NAME}`, 'g'),
|
|
217
220
|
];
|
|
221
|
+
// Vite injects these at build time; they are not user-set env vars.
|
|
222
|
+
const VITE_INTRINSICS = new Set(['DEV', 'PROD', 'MODE', 'BASE_URL', 'SSR']);
|
|
218
223
|
|
|
219
224
|
const visit = (filePath) => {
|
|
220
225
|
if (seen.has(filePath)) return;
|
|
@@ -225,10 +230,16 @@ export function grepEnvUsage(projectDir, config = {}) {
|
|
|
225
230
|
let content;
|
|
226
231
|
try { content = readFileSync(filePath, 'utf-8'); } catch { return; }
|
|
227
232
|
if (!content.includes('env')) return;
|
|
228
|
-
|
|
233
|
+
// patterns[2] is the import.meta.env one — its matches are Vite-injected
|
|
234
|
+
// when the name is an intrinsic, and must not be reported as user env vars.
|
|
235
|
+
for (let i = 0; i < patterns.length; i++) {
|
|
229
236
|
let m;
|
|
230
|
-
const rx = new RegExp(
|
|
231
|
-
|
|
237
|
+
const rx = new RegExp(patterns[i].source, 'g');
|
|
238
|
+
const isViteSource = i === 2;
|
|
239
|
+
while ((m = rx.exec(content)) !== null) {
|
|
240
|
+
if (isViteSource && VITE_INTRINSICS.has(m[1])) continue;
|
|
241
|
+
names.add(m[1]);
|
|
242
|
+
}
|
|
232
243
|
}
|
|
233
244
|
};
|
|
234
245
|
|
|
@@ -422,9 +422,12 @@ function checkReadmeSections(projectDir) {
|
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
// Recommended sections are a BONUS — present = +1 to both passed and total,
|
|
426
|
+
// missing = no-op. Counting missing recommended toward `total` without a
|
|
427
|
+
// corresponding warning would be a silent fail (caught by B-4 nudge).
|
|
425
428
|
for (const section of recommendedSections) {
|
|
426
|
-
total++;
|
|
427
429
|
if (section.patterns.some(p => lowerContent.includes(p))) {
|
|
430
|
+
total++;
|
|
428
431
|
passed++;
|
|
429
432
|
}
|
|
430
433
|
}
|
|
@@ -41,13 +41,19 @@ export function validateEnvironment(projectDir, config) {
|
|
|
41
41
|
// CLI/library projects that declare no env vars skip this.)
|
|
42
42
|
if (ptc.needsEnvVars !== false) {
|
|
43
43
|
const documented = new Set();
|
|
44
|
-
|
|
44
|
+
// Require the matched name to end with a letter/digit — prevents prose-only
|
|
45
|
+
// tokens like `VITE_` (the convention prefix) from being treated as a real
|
|
46
|
+
// variable name.
|
|
47
|
+
const varRe = /`([A-Z][A-Z0-9_]*[A-Z0-9])`/g;
|
|
45
48
|
let m;
|
|
46
|
-
while ((m = varRe.exec(content)) !== null)
|
|
49
|
+
while ((m = varRe.exec(content)) !== null) {
|
|
50
|
+
if (m[1].length < 3) continue; // 'OK' / 'ID' etc. are too short to be env var refs
|
|
51
|
+
documented.add(m[1]);
|
|
52
|
+
}
|
|
47
53
|
for (const envFile of ['.env.example', '.env.template']) {
|
|
48
54
|
const p = resolve(projectDir, envFile);
|
|
49
55
|
if (!existsSync(p)) continue;
|
|
50
|
-
const re = /^([A-Z][A-Z0-9_]
|
|
56
|
+
const re = /^([A-Z][A-Z0-9_]*[A-Z0-9])\s*=/gm;
|
|
51
57
|
const ex = readFileSync(p, 'utf-8');
|
|
52
58
|
let em;
|
|
53
59
|
while ((em = re.exec(ex)) !== null) documented.add(em[1]);
|
|
@@ -3,7 +3,7 @@ schema_version: "1.0"
|
|
|
3
3
|
extension:
|
|
4
4
|
id: "docguard"
|
|
5
5
|
name: "DocGuard — CDD Enforcement"
|
|
6
|
-
version: "0.11.
|
|
6
|
+
version: "0.11.2"
|
|
7
7
|
description: "Canonical-Driven Development enforcement as a true spec-kit extension. LLM-first design with 19 automated validators, 4 AI behavior skills, spec-kit skill chaining, and workflow hooks. Zero NPM runtime dependencies."
|
|
8
8
|
author: "Ricardo Accioly"
|
|
9
9
|
repository: "https://github.com/raccioly/docguard"
|
|
@@ -6,10 +6,10 @@ description: AI-driven documentation repair with structured research workflow, t
|
|
|
6
6
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
7
7
|
metadata:
|
|
8
8
|
author: docguard
|
|
9
|
-
version: 0.11.
|
|
9
|
+
version: 0.11.2
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-fix
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.11.
|
|
12
|
+
<!-- docguard:version: 0.11.2 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Fix Skill
|
|
15
15
|
|
|
@@ -7,10 +7,10 @@ description: Run DocGuard guard validation against Canonical-Driven Development
|
|
|
7
7
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
8
8
|
metadata:
|
|
9
9
|
author: docguard
|
|
10
|
-
version: 0.11.
|
|
10
|
+
version: 0.11.2
|
|
11
11
|
source: extensions/spec-kit-docguard/skills/docguard-guard
|
|
12
12
|
---
|
|
13
|
-
<!-- docguard:version: 0.11.
|
|
13
|
+
<!-- docguard:version: 0.11.2 -->
|
|
14
14
|
|
|
15
15
|
# DocGuard Guard Skill
|
|
16
16
|
|
|
@@ -6,10 +6,10 @@ description: Cross-document consistency analysis and quality assessment. Perform
|
|
|
6
6
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
7
7
|
metadata:
|
|
8
8
|
author: docguard
|
|
9
|
-
version: 0.11.
|
|
9
|
+
version: 0.11.2
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-review
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.11.
|
|
12
|
+
<!-- docguard:version: 0.11.2 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Review Skill
|
|
15
15
|
|
|
@@ -6,10 +6,10 @@ description: CDD maturity assessment with category-aware improvement roadmap. Ru
|
|
|
6
6
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
7
7
|
metadata:
|
|
8
8
|
author: docguard
|
|
9
|
-
version: 0.11.
|
|
9
|
+
version: 0.11.2
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-score
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.11.
|
|
12
|
+
<!-- docguard:version: 0.11.2 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Score Skill
|
|
15
15
|
|
|
@@ -4,7 +4,7 @@ description: Keep canonical documentation ALWAYS UP TO DATE. Refreshes code-trut
|
|
|
4
4
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
5
5
|
metadata:
|
|
6
6
|
author: docguard
|
|
7
|
-
version: 0.11.
|
|
7
|
+
version: 0.11.2
|
|
8
8
|
source: extensions/spec-kit-docguard/skills/docguard-sync
|
|
9
9
|
---
|
|
10
10
|
|
package/package.json
CHANGED