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.
@@ -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
- // Find model/entity files in code monorepo-aware (honors config.sourceRoot/workspaces).
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 modelSubdirs = ['models', 'entities', 'schema', 'schemas', 'prisma'];
169
- const roots = resolveSourceRoots(dir, config);
170
- for (const root of roots) {
171
- for (const sub of modelSubdirs) {
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
- const varRegex = /`([A-Z][A-Z0-9_]{2,})`/g;
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_]+)\s*=/gm;
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
  }
@@ -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
- if (flags.verbose || v.status === 'fail') {
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 (flags.verbose || v.status === 'warn') {
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++;
@@ -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' },
@@ -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
- /process\.env\.([A-Z][A-Z0-9_]+)/g,
215
- /process\.env\[\s*['"]([A-Z][A-Z0-9_]+)['"]\s*\]/g,
216
- /import\.meta\.env\.([A-Z][A-Z0-9_]+)/g,
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
- for (const re of patterns) {
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(re.source, 'g');
231
- while ((m = rx.exec(content)) !== null) names.add(m[1]);
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
- const varRe = /`([A-Z][A-Z0-9_]{2,})`/g;
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) documented.add(m[1]);
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_]+)\s*=/gm;
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.1"
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.1
9
+ version: 0.11.2
10
10
  source: extensions/spec-kit-docguard/skills/docguard-fix
11
11
  ---
12
- <!-- docguard:version: 0.11.1 -->
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.1
10
+ version: 0.11.2
11
11
  source: extensions/spec-kit-docguard/skills/docguard-guard
12
12
  ---
13
- <!-- docguard:version: 0.11.1 -->
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.1
9
+ version: 0.11.2
10
10
  source: extensions/spec-kit-docguard/skills/docguard-review
11
11
  ---
12
- <!-- docguard:version: 0.11.1 -->
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.1
9
+ version: 0.11.2
10
10
  source: extensions/spec-kit-docguard/skills/docguard-score
11
11
  ---
12
- <!-- docguard:version: 0.11.1 -->
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.1
7
+ version: 0.11.2
8
8
  source: extensions/spec-kit-docguard/skills/docguard-sync
9
9
  ---
10
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docguard-cli",
3
- "version": "0.11.1",
3
+ "version": "0.11.2",
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": {