docguard-cli 0.28.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.es.md +102 -0
  2. package/README.md +80 -32
  3. package/README.pt-BR.md +101 -0
  4. package/STANDARD.md +20 -10
  5. package/cli/commands/agents.mjs +149 -0
  6. package/cli/commands/diff.mjs +6 -15
  7. package/cli/commands/generate.mjs +14 -1001
  8. package/cli/commands/guard.mjs +136 -8
  9. package/cli/commands/llms.mjs +67 -5
  10. package/cli/commands/mcp.mjs +263 -0
  11. package/cli/commands/memory.mjs +115 -0
  12. package/cli/commands/score.mjs +76 -12
  13. package/cli/commands/trace.mjs +364 -1
  14. package/cli/commands/verify.mjs +93 -6
  15. package/cli/docguard.mjs +42 -5
  16. package/cli/findings.mjs +511 -0
  17. package/cli/scanners/agent-readability.mjs +202 -0
  18. package/cli/scanners/instruction-audit.mjs +320 -0
  19. package/cli/scanners/semantic-claims.mjs +7 -1
  20. package/cli/scanners/speckit.mjs +443 -28
  21. package/cli/shared-ignore.mjs +148 -16
  22. package/cli/shared.mjs +45 -1
  23. package/cli/validators/api-surface.mjs +113 -26
  24. package/cli/validators/architecture.mjs +66 -43
  25. package/cli/validators/canonical-sync.mjs +59 -28
  26. package/cli/validators/changelog.mjs +41 -17
  27. package/cli/validators/cross-reference.mjs +28 -11
  28. package/cli/validators/doc-quality.mjs +78 -44
  29. package/cli/validators/docs-coverage.mjs +90 -63
  30. package/cli/validators/docs-diff.mjs +63 -64
  31. package/cli/validators/docs-sync.mjs +48 -33
  32. package/cli/validators/drift.mjs +40 -34
  33. package/cli/validators/environment.mjs +67 -27
  34. package/cli/validators/freshness.mjs +12 -5
  35. package/cli/validators/generated-staleness.mjs +26 -10
  36. package/cli/validators/metadata-sync.mjs +28 -25
  37. package/cli/validators/metrics-consistency.mjs +89 -47
  38. package/cli/validators/schema-sync.mjs +37 -32
  39. package/cli/validators/security.mjs +7 -20
  40. package/cli/validators/spec-kit.mjs +3 -0
  41. package/cli/validators/structure.mjs +58 -23
  42. package/cli/validators/surface-sync.mjs +34 -15
  43. package/cli/validators/test-spec.mjs +87 -29
  44. package/cli/validators/todo-tracking.mjs +83 -74
  45. package/cli/validators/traceability.mjs +67 -39
  46. package/cli/writers/doc-generators.mjs +853 -0
  47. package/cli/writers/generate-io.mjs +142 -0
  48. package/cli/writers/sarif.mjs +129 -0
  49. package/commands/docguard.fix.md +56 -53
  50. package/commands/docguard.guard.md +53 -47
  51. package/commands/docguard.review.md +49 -31
  52. package/docs/ai-integration.md +133 -134
  53. package/docs/commands.md +49 -3
  54. package/docs/configuration.md +38 -0
  55. package/docs/faq.md +15 -0
  56. package/extensions/spec-kit-docguard/extension.yml +1 -1
  57. package/extensions/spec-kit-docguard/skills/docguard-fix/SKILL.md +2 -2
  58. package/extensions/spec-kit-docguard/skills/docguard-guard/SKILL.md +2 -2
  59. package/extensions/spec-kit-docguard/skills/docguard-review/SKILL.md +2 -2
  60. package/extensions/spec-kit-docguard/skills/docguard-score/SKILL.md +2 -2
  61. package/extensions/spec-kit-docguard/skills/docguard-sync/SKILL.md +2 -2
  62. package/package.json +2 -1
  63. package/schemas/docguard-config.schema.json +28 -0
  64. package/templates/ci/gitlab-component.yml +90 -0
  65. package/templates/commands/docguard.fix.md +33 -10
  66. package/templates/commands/docguard.guard.md +40 -26
  67. package/templates/commands/docguard.init.md +23 -11
  68. package/templates/commands/docguard.review.md +25 -8
  69. package/templates/commands/docguard.update.md +14 -4
@@ -1,22 +1,38 @@
1
1
  /**
2
- * Verify Command — `docguard verify --semantic` (LLM field report #5).
2
+ * Verify Command — `docguard verify` (LLM field reports #5, #11).
3
3
  *
4
- * Surfaces the semantic claims in the canonical docs (documented numbers, limits,
5
- * and enums) as a structured verification task list for the agent to check
6
- * against the code. DocGuard does the deterministic discovery; the LLM does the
7
- * judgment the same division of labour as `docguard agent`.
4
+ * Two modes, same division of labour (DocGuard does the deterministic
5
+ * discovery; the LLM does the judgment like `docguard agent`):
6
+ *
7
+ * --semantic (default) Surface the semantic claims in the canonical docs
8
+ * (documented numbers, limits, enums) as a verification
9
+ * task list for the agent to check against the code.
10
+ *
11
+ * --instructions Audit the agent instruction files themselves
12
+ * (AGENTS.md, CLAUDE.md) for drift: duplicate rules,
13
+ * direct never/always contradictions, stale file
14
+ * pointers, and unknown docguard commands are found
15
+ * deterministically; topically-clustered rule pairs
16
+ * become agent tasks ("do these contradict in
17
+ * practice?"). Inspired by spec-kit's MemoryLint.
8
18
  *
9
19
  * Read-only. JSON is the machine artifact (the agent-executable task list);
10
20
  * text is the human summary.
11
21
  *
12
- * docguard verify [--semantic] [--format json]
22
+ * docguard verify [--semantic | --instructions] [--format json]
13
23
  */
14
24
 
15
25
  import { c } from '../shared.mjs';
16
26
  import { detectAgentMode } from '../ensure-skills.mjs';
17
27
  import { extractSemanticClaims, buildSemanticVerifyTasks } from '../scanners/semantic-claims.mjs';
28
+ import { auditInstructions } from '../scanners/instruction-audit.mjs';
18
29
 
19
30
  export function runVerify(projectDir, config, flags) {
31
+ if (flags.instructions) {
32
+ runInstructionAudit(projectDir, config, flags);
33
+ return;
34
+ }
35
+
20
36
  const isJson = flags.format === 'json';
21
37
  const claims = extractSemanticClaims(projectDir, config);
22
38
  const tasks = buildSemanticVerifyTasks(claims);
@@ -65,3 +81,74 @@ export function runVerify(projectDir, config, flags) {
65
81
  console.log(` ${c.dim}This is the highest-value bug class and DocGuard can't judge it — an agent must.${c.reset}`);
66
82
  console.log(` ${c.dim}Get the machine task list: ${c.cyan}${cmd}${c.dim}, then read each cited file and confirm the value.${c.reset}\n`);
67
83
  }
84
+
85
+ // ── verify --instructions: agent-instruction drift/conflict audit ───────────
86
+
87
+ function runInstructionAudit(projectDir, config, flags) {
88
+ const isJson = flags.format === 'json';
89
+ const { rules, deterministic, tasks } = auditInstructions(projectDir, config);
90
+ const { duplicates, negations, stalePointers, staleCommands } = deterministic;
91
+ const findingCount = duplicates.length + negations.length + stalePointers.length + staleCommands.length;
92
+
93
+ if (isJson) {
94
+ console.log(JSON.stringify({
95
+ command: 'verify --instructions',
96
+ project: config.projectName,
97
+ ruleCount: rules.length,
98
+ findingCount,
99
+ findings: deterministic,
100
+ taskCount: tasks.length,
101
+ // How to act on this: findings are proven; tasks need judgment.
102
+ howToVerify: 'The findings are deterministic — fix them directly (delete the duplicate copy, resolve the negation in favour of one rule, repoint or remove stale paths/commands). For each task, read both rules in context and judge whether they contradict in practice; if so, report which should win, why, and which file to edit. DocGuard cannot judge the tasks — they require understanding intent.',
103
+ tasks,
104
+ }, null, 2));
105
+ return;
106
+ }
107
+
108
+ console.log(`${c.bold}🔬 DocGuard Verify — instruction audit${c.reset}`);
109
+ console.log(`${c.dim} ${config.projectName} · duplicate / contradictory / stale rules in AGENTS.md + CLAUDE.md${c.reset}\n`);
110
+
111
+ if (rules.length === 0) {
112
+ console.log(` ${c.green}✅ No instruction rules found (no AGENTS.md/CLAUDE.md, or nothing imperative in them).${c.reset}\n`);
113
+ return;
114
+ }
115
+
116
+ console.log(` ${c.dim}${rules.length} rule(s) extracted from ${[...new Set(rules.map(r => r.file))].join(' + ')}${c.reset}\n`);
117
+
118
+ if (findingCount === 0) {
119
+ console.log(` ${c.green}✅ No duplicate, directly-contradictory, or stale rules found.${c.reset}\n`);
120
+ } else {
121
+ console.log(` ${c.yellow}${findingCount} deterministic finding(s):${c.reset}\n`);
122
+ for (const d of duplicates) {
123
+ const where = d.rules.map(r => `${r.file}:${r.line}`).join(` ${c.dim}≡${c.reset} `);
124
+ console.log(` ${c.yellow}⚠${c.reset} duplicate rule — ${where}: ${c.dim}"${d.rules[0].text}"${c.reset}`);
125
+ }
126
+ for (const n of negations) {
127
+ console.log(` ${c.yellow}⚠${c.reset} negation conflict — ${n.a.file}:${n.a.line} ${c.dim}⇄${c.reset} ${n.b.file}:${n.b.line}: ${c.dim}"${n.a.text}" vs "${n.b.text}"${c.reset}`);
128
+ }
129
+ for (const s of stalePointers) {
130
+ console.log(` ${c.yellow}⚠${c.reset} stale pointer — ${s.file}:${s.line}: ${c.cyan}${s.path}${c.reset} does not exist`);
131
+ }
132
+ for (const s of staleCommands) {
133
+ console.log(` ${c.yellow}⚠${c.reset} stale command — ${s.file}:${s.line}: ${c.cyan}docguard ${s.command}${c.reset} is not a docguard command`);
134
+ }
135
+ console.log('');
136
+ }
137
+
138
+ if (tasks.length > 0) {
139
+ console.log(` ${c.yellow}${tasks.length} rule pair(s) for the agent to judge:${c.reset}\n`);
140
+ for (const t of tasks) {
141
+ console.log(` ${c.bold}${t.a.file}:${t.a.line} ↔ ${t.b.file}:${t.b.line}${c.reset} ${c.dim}(shared: ${t.sharedTerms.join(', ')})${c.reset}`);
142
+ console.log(` ${c.yellow}A${c.reset} ${c.dim}${t.a.section ? `[${t.a.section}] ` : ''}${c.reset}"${t.a.text}"`);
143
+ console.log(` ${c.yellow}B${c.reset} ${c.dim}${t.b.section ? `[${t.b.section}] ` : ''}${c.reset}"${t.b.text}"`);
144
+ console.log('');
145
+ }
146
+
147
+ const mode = detectAgentMode(projectDir);
148
+ const cmd = mode === 'llm' ? '/docguard.verify' : 'docguard verify --instructions --format json';
149
+ console.log(` ${c.dim}Whether clustered rules contradict in practice is judgment DocGuard can't make — an agent must.${c.reset}`);
150
+ console.log(` ${c.dim}Get the machine task list: ${c.cyan}${cmd}${c.dim}, then judge each pair and report which rule should win.${c.reset}\n`);
151
+ } else if (findingCount === 0) {
152
+ console.log(` ${c.dim}(Looks for duplicate/negated rules, dead file pointers, unknown docguard commands, and topically-clustered rule pairs.)${c.reset}\n`);
153
+ }
154
+ }
package/cli/docguard.mjs CHANGED
@@ -48,6 +48,7 @@ import { runVerify } from './commands/verify.mjs';
48
48
  import { runMemory } from './commands/memory.mjs';
49
49
  import { runDemo } from './commands/demo.mjs';
50
50
  import { runAgent } from './commands/agent.mjs';
51
+ import { runMcp } from './commands/mcp.mjs';
51
52
  import { ensureSkills } from './ensure-skills.mjs';
52
53
 
53
54
  // ── Shared constants (imported to break circular dependencies) ──────────
@@ -90,8 +91,9 @@ ${c.bold}Tools (situational, but day-to-day useful)${c.reset}
90
91
  ${c.green}explain${c.reset} Explain a validator key, warning text, or finding code (${c.cyan}docguard explain SEC001${c.reset})
91
92
  ${c.green}verify${c.reset} Extract documented numbers/limits/enums for an agent to check vs code (${c.cyan}--semantic${c.reset})
92
93
  ${c.green}feedback${c.reset} Report likely false positives back to DocGuard (local-first + 1-click prefilled issue)
94
+ ${c.green}mcp${c.reset} MCP server over stdio — guard/score/explain/verify/diagnose as agent tools
93
95
  ${c.green}memory${c.reset} Show what DocGuard remembers (${c.cyan}--diff${c.reset} drills into drift)
94
- ${c.green}trace${c.reset} Requirements traceability matrix (${c.cyan}--reverse${c.reset} for code→doc map)
96
+ ${c.green}trace${c.reset} Requirements traceability matrix (${c.cyan}--reverse${c.reset} for code→doc map, ${c.cyan}--features${c.reset} for per-feature adherence)
95
97
  ${c.green}upgrade${c.reset} Migrate ${c.cyan}.docguard.json${c.reset} schema + CLI (${c.cyan}--apply --pr${c.reset} for team-wide PR)
96
98
  ${c.green}watch${c.reset} Live mode: re-run guard on file changes
97
99
 
@@ -256,7 +258,7 @@ const COMMAND_HELP = {
256
258
  },
257
259
  trace: {
258
260
  summary: 'Requirements traceability matrix.',
259
- usage: 'docguard trace [--reverse]',
261
+ usage: 'docguard trace [--reverse] [--features]',
260
262
  flags: [['--reverse', 'Code→doc map instead of doc→code']],
261
263
  examples: ['docguard trace', 'docguard trace --reverse'],
262
264
  },
@@ -292,9 +294,10 @@ const COMMAND_HELP = {
292
294
  },
293
295
  verify: {
294
296
  summary: 'Extract the semantic claims in your canonical docs — documented numbers, limits, and enums (retention days, rate limits, GSI/role counts, status enums) — as a verification task list the agent checks against the code. This is the highest-value bug class (a doc value that drifted from code) and the one regex/AST cannot judge. DocGuard finds the claims; the LLM confirms them.',
295
- usage: 'docguard verify [--semantic] [--format json]',
297
+ usage: 'docguard verify [--semantic|--instructions] [--format json]',
296
298
  flags: [
297
299
  ['--semantic', 'Extract documented numbers/limits/enums to verify against code (the current — and default — mode)'],
300
+ ['--instructions', 'Audit AGENTS.md/CLAUDE.md for duplicate, contradictory, and stale-pointer rules (deterministic findings + agent conflict tasks)'],
298
301
  ['--format json', 'Machine-readable task list (the agent-executable artifact)'],
299
302
  ],
300
303
  examples: ['docguard verify --semantic', 'docguard verify --semantic --format json'],
@@ -375,6 +378,27 @@ async function main() {
375
378
  // v0.28 (field report #5): `docguard verify --semantic` extracts
376
379
  // documented numbers/enums/limits for the agent to check against code.
377
380
  flags.semantic = true;
381
+ } else if (args[i] === '--instructions') {
382
+ // v0.30: `docguard verify --instructions` audits AGENTS.md/CLAUDE.md for
383
+ // duplicate/contradictory/stale rules (MemoryLint-inspired).
384
+ flags.instructions = true;
385
+ } else if (args[i] === '--full') {
386
+ // v0.29: `docguard llms --full` emits llms-full.txt (inline doc bodies,
387
+ // the Mintlify-popularized companion to the llms.txt index).
388
+ flags.full = true;
389
+ } else if (args[i] === '--pack') {
390
+ // v0.29: `docguard memory --pack` writes .docguard/context-pack.md — a
391
+ // compact code-truth-stamped session-start context for AI agents.
392
+ flags.pack = true;
393
+ } else if (args[i] === '--sync') {
394
+ // v0.29: `docguard agents --sync` regenerates the agent-file family
395
+ // (CLAUDE.md, GEMINI.md, copilot-instructions, .cursor rules) from
396
+ // AGENTS.md — the canonical source. Kills hand-duplication drift.
397
+ flags.sync = true;
398
+ } else if (args[i] === '--check') {
399
+ // v0.29: `docguard agents --check` — CI staleness gate for the synced
400
+ // agent-file family (exit 2 when a variant is missing or stale).
401
+ flags.check = true;
378
402
  } else if (args[i] === '--plan') {
379
403
  flags.plan = true;
380
404
  } else if (args[i] === '--since' && args[i + 1]) {
@@ -390,6 +414,9 @@ async function main() {
390
414
  flags.changedOnly = true;
391
415
  } else if (args[i] === '--reverse') {
392
416
  flags.reverse = true;
417
+ } else if (args[i] === '--features') {
418
+ // v0.30: `docguard trace --features` — per-feature spec-adherence report.
419
+ flags.features = true;
393
420
  } else if (args[i] === '--history') {
394
421
  flags.history = true;
395
422
  } else if (args[i] === '--force-redo') {
@@ -512,10 +539,13 @@ async function main() {
512
539
  // touch" — so it joins the club to suppress the banner AND ensureSkills'
513
540
  // .agent/.specify writes, which were a surprising side effect of a bare
514
541
  // `generate --plan` (and were already suppressed for `--plan --write`).
515
- const jsonMode = flags.format === 'json';
542
+ // v0.29: 'sarif' joins 'json' — any machine format where stdout IS the
543
+ // artifact belongs here, or the banner corrupts the payload.
544
+ const jsonMode = flags.format === 'json' || flags.format === 'sarif';
516
545
  // `agent` emits a machine task graph (JSON by default) — it must be banner-
517
546
  // free and side-effect-free like the other read-only commands.
518
- const headless = jsonMode || flags.write || flags.checkOnly || flags.changedOnly || flags.quiet || flags.plan || command === 'agent';
547
+ // `mcp`: stdout IS the JSON-RPC transport any banner byte corrupts the stream.
548
+ const headless = jsonMode || flags.write || flags.checkOnly || flags.changedOnly || flags.quiet || flags.plan || command === 'agent' || command === 'mcp';
519
549
 
520
550
  if (!headless) printBanner();
521
551
 
@@ -540,6 +570,8 @@ async function main() {
540
570
  'feedback',
541
571
  // verify only reads docs and emits a task list — pure report.
542
572
  'verify',
573
+ // mcp serves read-only tools over stdio — scaffolding writes are off-limits.
574
+ 'mcp',
543
575
  ]);
544
576
 
545
577
  // Silent auto-check: install skills/commands if missing. Skip entirely in
@@ -691,6 +723,11 @@ async function main() {
691
723
  // drift — the class regex/AST can't see). Read-only.
692
724
  runVerify(projectDir, config, flags);
693
725
  break;
726
+ case 'mcp':
727
+ // MCP stdio server — guard/score/explain/verify-claims/diagnose as tools
728
+ // for MCP clients. Long-lived; resolves when stdin closes.
729
+ await runMcp(projectDir, config, flags);
730
+ break;
694
731
  case 'memory':
695
732
  runMemory(projectDir, config, flags);
696
733
  break;