docguard-cli 0.18.1 → 0.20.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.
- package/README.md +120 -34
- package/cli/commands/guard.mjs +20 -2
- package/cli/commands/init.mjs +54 -0
- package/cli/docguard.mjs +106 -49
- package/cli/validators/canonical-sync.mjs +211 -0
- package/cli/validators/spec-kit.mjs +14 -0
- package/docs/quickstart.md +1 -1
- package/extensions/spec-kit-docguard/README.md +1 -1
- package/extensions/spec-kit-docguard/extension.yml +5 -5
- 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/README.md
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
## Table of Contents
|
|
16
16
|
|
|
17
17
|
- [What is DocGuard?](#what-is-docguard)
|
|
18
|
+
- [What's New](#-whats-new)
|
|
18
19
|
- [Quick Start](#-quick-start)
|
|
19
20
|
- [Spec Kit Integration](#-spec-kit-integration)
|
|
20
21
|
- [Usage](#usage)
|
|
@@ -50,15 +51,15 @@ DocGuard is an official [GitHub Spec Kit](https://github.com/github/spec-kit) co
|
|
|
50
51
|
|
|
51
52
|
```mermaid
|
|
52
53
|
graph TD
|
|
53
|
-
CLI["CLI Entry<br/>docguard.mjs"] --> Commands["Commands (
|
|
54
|
+
CLI["CLI Entry<br/>docguard.mjs"] --> Commands["Commands (13)"]
|
|
54
55
|
Commands --> guard["guard"]
|
|
55
56
|
Commands --> generate["generate"]
|
|
56
57
|
Commands --> score["score"]
|
|
57
58
|
Commands --> diagnose["diagnose"]
|
|
58
59
|
Commands --> setup["setup wizard"]
|
|
59
|
-
Commands --> other["diff · init · fix · trace<br/>agents · hooks · badge · ci · watch"]
|
|
60
|
+
Commands --> other["diff · init · fix · trace · impact · sync<br/>explain · memory · upgrade · agents · hooks · badge · ci · watch"]
|
|
60
61
|
|
|
61
|
-
guard --> Validators["Validators (
|
|
62
|
+
guard --> Validators["Validators (23)"]
|
|
62
63
|
generate --> Scanners["Scanners (4)<br/>routes · schemas · doc-tools · speckit"]
|
|
63
64
|
score --> Scoring["Weighted Scoring<br/>8 categories"]
|
|
64
65
|
diagnose --> Validators
|
|
@@ -81,6 +82,37 @@ graph TD
|
|
|
81
82
|
|
|
82
83
|
---
|
|
83
84
|
|
|
85
|
+
## ✨ What's New
|
|
86
|
+
|
|
87
|
+
Recent highlights across the v0.16 → v0.19 line:
|
|
88
|
+
|
|
89
|
+
- **`docguard explain <validator>`** — `docguard explain freshness` prints purpose, rules, common
|
|
90
|
+
failures, and fix recipes for any of the 23 validators. No need to dig into source.
|
|
91
|
+
- **`docguard memory --diff`** — surface what changed in your canonical docs between two refs
|
|
92
|
+
(`HEAD~10..HEAD` by default). Great for code review and changelog drafting.
|
|
93
|
+
- **`docguard score --diff`** — see exactly which validators moved the score up or down between
|
|
94
|
+
two commits. Pinpoints regressions without re-running the full suite by hand.
|
|
95
|
+
- **`docguard upgrade --apply --pr`** — when the config schema bumps, DocGuard migrates
|
|
96
|
+
`.docguard.json` for you and (optionally) opens a PR with the change.
|
|
97
|
+
- **Language-aware patterns** — test discovery and trace mapping now understand Python, Rust, Go,
|
|
98
|
+
Java, Ruby, and PHP layouts in addition to JS/TS. Sensible defaults, override via config.
|
|
99
|
+
- **Per-validator severity overrides** — escalate `freshness` to `high` for production repos,
|
|
100
|
+
demote `doc-quality` to `low` for prototypes. Configurable per-project.
|
|
101
|
+
- **JSON Schema for `.docguard.json`** — IDE autocomplete, in-line docs, and validation via
|
|
102
|
+
`$schema`. Shipped in the package at `schemas/docguard-config.schema.json`.
|
|
103
|
+
- **Version pin (`docguardVersion` + `--pin`)** — pin the CLI version your project supports so
|
|
104
|
+
CI fails loudly if someone bumps DocGuard without re-running the suite.
|
|
105
|
+
- **Cross-process plan cache** — repeated runs reuse the validator plan across processes when
|
|
106
|
+
the working tree hasn't changed. ~30% faster guard runs on typical repos.
|
|
107
|
+
- **Headless-aware banner** — `--quiet`, `--format json`, `--write`, and `--changed-only`
|
|
108
|
+
automatically suppress the banner so JSON output stays parse-clean.
|
|
109
|
+
- **npm-pack smoke gate** — every release now extracts the actual tarball and runs the CLI
|
|
110
|
+
end-to-end before publish, catching missing-file regressions.
|
|
111
|
+
|
|
112
|
+
See [CHANGELOG.md](CHANGELOG.md) for the full history.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
84
116
|
## ⚡ Quick Start
|
|
85
117
|
|
|
86
118
|
### Node.js (npm)
|
|
@@ -203,24 +235,49 @@ This installs DocGuard's slash commands (`/docguard.guard`, `/docguard.review`,
|
|
|
203
235
|
|
|
204
236
|
## Usage
|
|
205
237
|
|
|
206
|
-
DocGuard ships **13 commands
|
|
238
|
+
DocGuard ships **13 commands** (the "Daily 5" + 8 situational tools). Six additional one-shot scaffolders are accessed via `docguard init --with <name>`. Eight v0.19 commands continue to work as deprecation aliases through v0.20.x — see [MIGRATION-v0.20.md](docs-implementation/MIGRATION-v0.20.md).
|
|
239
|
+
|
|
240
|
+
**The Daily 5** — what you'll reach for 95% of the time:
|
|
241
|
+
|
|
242
|
+
| Command | What It Does |
|
|
243
|
+
|:--------|:-------------|
|
|
244
|
+
| `init` | Bootstrap a project (`--wizard` for interactive · `--with <name>` for scaffolders) |
|
|
245
|
+
| `guard` | Validate against canonical docs — 23 validators |
|
|
246
|
+
| `diff` | Show gaps between docs and code (`--since <ref>` for impact mode) |
|
|
247
|
+
| `sync` | Refresh code-truth doc sections — keeps memory always up to date |
|
|
248
|
+
| `score` | CDD maturity score (0-100; `--diff` for delta between refs) |
|
|
249
|
+
|
|
250
|
+
**Tools (situational, but day-to-day useful):**
|
|
207
251
|
|
|
208
252
|
| Command | Purpose |
|
|
209
253
|
|:--------|:--------|
|
|
210
|
-
| `diagnose` |
|
|
211
|
-
| `
|
|
212
|
-
| `
|
|
213
|
-
| `
|
|
214
|
-
| `
|
|
215
|
-
| `
|
|
216
|
-
| `
|
|
217
|
-
| `diff` |
|
|
218
|
-
| `
|
|
219
|
-
| `trace` | Requirements traceability
|
|
220
|
-
| `
|
|
221
|
-
| `watch` | Live
|
|
222
|
-
|
|
223
|
-
|
|
254
|
+
| `diagnose` | AI orchestrator — guard → emit fix prompts in one command |
|
|
255
|
+
| `fix` | Generate AI fix instructions for specific docs (`--doc <name> --format prompt`) |
|
|
256
|
+
| `fix --write` | Apply deterministic fixes (no AI — version bumps, counts, anchors, sections) |
|
|
257
|
+
| `fix --history` | Audit log of every mechanical fix applied (from `.docguard/fixed.json`) |
|
|
258
|
+
| `generate` | Reverse-engineer docs from existing codebase (`--plan` for AI scan) |
|
|
259
|
+
| `explain <warning>` | Paste any warning — get the validator's docstring + fix path |
|
|
260
|
+
| `memory` | Per-domain accuracy headline (endpoints / entities / env / tech) |
|
|
261
|
+
| `memory --diff` | Drill into which specific claims don't match code |
|
|
262
|
+
| `score --diff` | Drill into which checks pulled each category down |
|
|
263
|
+
| `trace` / `trace --reverse <file>` | Requirements traceability — forward AND reverse |
|
|
264
|
+
| `upgrade [--apply] [--pr]` | Check + migrate `.docguard.json` schema; `--pr` opens a PR |
|
|
265
|
+
| `watch` | Live mode: re-run guard on file changes |
|
|
266
|
+
|
|
267
|
+
**`init --with <name>` scaffolders** — picked at init time:
|
|
268
|
+
|
|
269
|
+
| Scaffolder | What It Generates |
|
|
270
|
+
|:-----------|:------------------|
|
|
271
|
+
| `agents` | `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/`, `.github/copilot-instructions.md` |
|
|
272
|
+
| `hooks` | Git pre-commit / pre-push hooks |
|
|
273
|
+
| `ci` | GitHub Actions / pipeline YAML |
|
|
274
|
+
| `badge` | Shields.io score badges for README |
|
|
275
|
+
| `llms` | `llms.txt` (AI-friendly summary) |
|
|
276
|
+
| `publish` | External doc-site config (Mintlify) — experimental |
|
|
277
|
+
|
|
278
|
+
Run them solo (`docguard init --with hooks`) or stacked (`docguard init --with agents,hooks,badge,ci`).
|
|
279
|
+
|
|
280
|
+
**Deprecation aliases** — `setup` · `agents` · `hooks` · `ci` · `badge` · `llms` · `publish` · `impact` keep working in v0.20.x with a yellow stderr warning. `audit → guard` is permanent (no warning). See [MIGRATION-v0.20.md](docs-implementation/MIGRATION-v0.20.md).
|
|
224
281
|
|
|
225
282
|
### CLI Flags
|
|
226
283
|
|
|
@@ -228,10 +285,22 @@ DocGuard ships **13 commands**:
|
|
|
228
285
|
|:-----|:------------|:---------|
|
|
229
286
|
| `--dir <path>` | Project directory (default: `.`) | All |
|
|
230
287
|
| `--verbose` | Show detailed output | All |
|
|
231
|
-
| `--
|
|
288
|
+
| `--quiet` / `-q` | Suppress banner — for hooks, CI loops, scripts | All |
|
|
289
|
+
| `--format json` | Machine-readable output (clean JSON, no ANSI bleed) | guard, score, diff, trace, diagnose, memory, impact, explain |
|
|
232
290
|
| `--force` | Overwrite existing files (creates `.bak` backups) | generate, agents, init |
|
|
233
|
-
| `--
|
|
234
|
-
| `--
|
|
291
|
+
| `--force-redo` | Bypass ping-pong suppression in `.docguard/fixed.json` | fix --write |
|
|
292
|
+
| `--profile <name>` | Starter / standard / enterprise | init |
|
|
293
|
+
| `--no-spec-kit` | Skip auto-init of `.specify/` / `.agent/` scaffolding | init |
|
|
294
|
+
| `--changed-only [--since <ref>]` | Pre-commit lite mode (5 fast validators on changed files only) | guard |
|
|
295
|
+
| `--timings` | Per-validator wall-time profile (slowest first) | guard |
|
|
296
|
+
| `--show-failing` | Show warnings/errors even when status is PASS | guard |
|
|
297
|
+
| `--pin` | Record running CLI version into `.docguard.json` (reproducibility) | guard |
|
|
298
|
+
| `--diff` | Per-category drill-down | score, memory |
|
|
299
|
+
| `--check-only` | Exit 1 if behind (for CI) | upgrade |
|
|
300
|
+
| `--apply` | Actually run the migration | upgrade |
|
|
301
|
+
| `--pr` | Open a PR with the migration | upgrade |
|
|
302
|
+
| `--reverse <file>` | Reverse traceability (code → docs) | trace |
|
|
303
|
+
| `--history` | Show fix audit log | fix |
|
|
235
304
|
|
|
236
305
|
### Example Output
|
|
237
306
|
|
|
@@ -266,30 +335,47 @@ $ npx docguard-cli generate
|
|
|
266
335
|
|
|
267
336
|
## 🔍 Validators
|
|
268
337
|
|
|
269
|
-
DocGuard runs **
|
|
338
|
+
DocGuard runs **23 automated validators** on every `guard` check. Every one is **language-aware** as of v0.16 — patterns for Python (`test_*.py`), Rust (`tests/*.rs`), Go (`*_test.go`), Java (`*Test.java`), Ruby (`*_spec.rb`), PHP, and JS/TS all match.
|
|
270
339
|
|
|
271
340
|
| # | Validator | What It Checks | Default |
|
|
272
341
|
|:--|:----------|:--------------|:--------|
|
|
273
342
|
| 1 | **Structure** | Required CDD files exist | ✅ On |
|
|
274
|
-
| 2 | **Doc Sections** | Canonical docs have required sections | ✅ On |
|
|
343
|
+
| 2 | **Doc Sections** | Canonical docs have required sections (or N/A markers) | ✅ On |
|
|
275
344
|
| 3 | **Docs-Sync** | Routes/services referenced in docs + OpenAPI cross-check | ✅ On |
|
|
276
|
-
| 4 | **Drift-Comments** | `// DRIFT:` comments logged in DRIFT-LOG.md | ✅ On |
|
|
345
|
+
| 4 | **Drift-Comments** | `// DRIFT:` comments logged in DRIFT-LOG.md (skips test files by default) | ✅ On |
|
|
277
346
|
| 5 | **Changelog** | CHANGELOG.md has [Unreleased] section | ✅ On |
|
|
278
347
|
| 6 | **Test-Spec** | Tests exist per TEST-SPEC.md rules | ✅ On |
|
|
279
|
-
| 7 | **Environment** | Env vars documented,
|
|
348
|
+
| 7 | **Environment** | Env vars documented, `.env.example` exists | ✅ On |
|
|
280
349
|
| 8 | **Security** | No hardcoded secrets in source code | ✅ On |
|
|
281
|
-
| 9 | **Architecture** | Imports follow layer boundaries | ✅ On |
|
|
282
|
-
| 10 | **Freshness** | Docs not stale relative to code changes | ✅ On |
|
|
350
|
+
| 9 | **Architecture** | Imports follow layer boundaries (honors `config.ignore`) | ✅ On |
|
|
351
|
+
| 10 | **Freshness** | Docs not stale relative to code changes (rename-aware via `git log --follow`) | ✅ On |
|
|
283
352
|
| 11 | **Traceability** | Requirement IDs (FR, SC, NFR, US, AC, T) trace to tests | ✅ On |
|
|
284
353
|
| 12 | **Docs-Diff** | Code artifacts match documented entities | ✅ On |
|
|
285
|
-
| 13 | **
|
|
286
|
-
| 14 | **
|
|
287
|
-
| 15 | **
|
|
288
|
-
| 16 | **Doc-Quality** | Writing quality (readability, passive voice, atomicity) | ✅ On |
|
|
289
|
-
| 17 | **TODO-Tracking** | Untracked TODOs/FIXMEs and skipped tests | ✅ On |
|
|
354
|
+
| 13 | **API-Surface** | API-REFERENCE.md endpoints match real routes (OpenAPI cross-check) | ✅ On |
|
|
355
|
+
| 14 | **Metadata-Sync** | Version refs consistent across docs | ✅ On |
|
|
356
|
+
| 15 | **Docs-Coverage** | Code features referenced in documentation | ✅ On |
|
|
357
|
+
| 16 | **Doc-Quality** | Writing quality (readability, passive voice, atomicity, IEEE 830) | ✅ On |
|
|
358
|
+
| 17 | **TODO-Tracking** | Untracked TODOs/FIXMEs and skipped tests (skips test files by default) | ✅ On |
|
|
290
359
|
| 18 | **Schema-Sync** | Database models documented in DATA-MODEL.md | ✅ On |
|
|
291
360
|
| 19 | **Spec-Kit** | Spec quality validation (FR-IDs, mandatory sections, phased tasks) | ✅ On |
|
|
292
|
-
| 20 | **
|
|
361
|
+
| 20 | **Cross-Reference** | Internal markdown links + anchors resolve (with "did you mean?" hints) | ✅ On |
|
|
362
|
+
| 21 | **Generated-Staleness** | `source=code` sections match scanner output; `status: draft` doc age | ✅ On |
|
|
363
|
+
| 22 | **Canonical-Sync** | DocGuard's own README count claims match code-truth (DocGuard repo only — N/A elsewhere) | ✅ On |
|
|
364
|
+
| 23 | **Metrics-Consistency** | Hardcoded numbers match actual counts | ✅ On |
|
|
365
|
+
|
|
366
|
+
**Per-validator controls** (in `.docguard.json`):
|
|
367
|
+
```json
|
|
368
|
+
{
|
|
369
|
+
"validators": {
|
|
370
|
+
"test-spec": false, // disable (kebab-case OR camelCase both accepted)
|
|
371
|
+
"freshness": true
|
|
372
|
+
},
|
|
373
|
+
"severity": {
|
|
374
|
+
"todoTracking": "high", // warnings fail CI
|
|
375
|
+
"freshness": "low" // warnings ignored for exit code
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
```
|
|
293
379
|
|
|
294
380
|
---
|
|
295
381
|
|
|
@@ -343,7 +429,7 @@ DocGuard provides AI agent slash commands for integrated workflows. Installed au
|
|
|
343
429
|
|
|
344
430
|
| Command | What It Does |
|
|
345
431
|
|:--------|:-------------|
|
|
346
|
-
| `/docguard.guard` | Run quality validation — check all
|
|
432
|
+
| `/docguard.guard` | Run quality validation — check all 23 validators |
|
|
347
433
|
| `/docguard.review` | Analyze doc quality and suggest improvements |
|
|
348
434
|
| `/docguard.fix` | Generate targeted fix prompts for specific issues |
|
|
349
435
|
| `/docguard.score` | Show CDD maturity score with category breakdown |
|
package/cli/commands/guard.mjs
CHANGED
|
@@ -139,7 +139,8 @@ import { validateCrossReferences } from '../validators/cross-reference.mjs';
|
|
|
139
139
|
import { validateGeneratedStaleness } from '../validators/generated-staleness.mjs';
|
|
140
140
|
import { validateTodoTracking } from '../validators/todo-tracking.mjs';
|
|
141
141
|
import { validateSchemaSync } from '../validators/schema-sync.mjs';
|
|
142
|
-
import { validateSpecKitIntegration } from '../
|
|
142
|
+
import { validateSpecKitIntegration } from '../validators/spec-kit.mjs';
|
|
143
|
+
import { validateCanonicalSync } from '../validators/canonical-sync.mjs';
|
|
143
144
|
|
|
144
145
|
/**
|
|
145
146
|
* Internal guard — returns structured data, no console output, no process.exit.
|
|
@@ -237,6 +238,22 @@ export function runGuardInternal(projectDir, config) {
|
|
|
237
238
|
}
|
|
238
239
|
}
|
|
239
240
|
|
|
241
|
+
// ── Canonical-Sync runs AFTER main loop, BEFORE metrics-consistency.
|
|
242
|
+
// Needs the live validator results to count "real" validators that ran.
|
|
243
|
+
// (Pre-canonical-sync ordering — comes before metrics-consistency so the
|
|
244
|
+
// metrics validator sees a stable surface count.)
|
|
245
|
+
if (validators.canonicalSync !== false) {
|
|
246
|
+
const start = performance.now();
|
|
247
|
+
try {
|
|
248
|
+
const result = validateCanonicalSync(projectDir, config, results);
|
|
249
|
+
const durationMs = Math.round((performance.now() - start) * 100) / 100;
|
|
250
|
+
results.push({ ...result, name: 'Canonical-Sync', key: 'canonicalSync', durationMs, ...classifyResult(result) });
|
|
251
|
+
} catch (err) {
|
|
252
|
+
const durationMs = Math.round((performance.now() - start) * 100) / 100;
|
|
253
|
+
results.push({ name: 'Canonical-Sync', key: 'canonicalSync', status: 'fail', quality: 'LOW', errors: [err.message], warnings: [], passed: 0, total: 1, durationMs });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
240
257
|
// ── Metrics-Consistency runs AFTER all other validators (needs their results) ──
|
|
241
258
|
if (validators.metricsConsistency !== false) {
|
|
242
259
|
const start = performance.now();
|
|
@@ -317,7 +334,8 @@ function liteValidatorsConfig() {
|
|
|
317
334
|
'structure', 'docsSync', 'drift', 'changelog', 'testSpec', 'environment',
|
|
318
335
|
'security', 'architecture', 'freshness', 'traceability', 'docsDiff',
|
|
319
336
|
'apiSurface', 'metadataSync', 'docsCoverage', 'docQuality', 'todoTracking',
|
|
320
|
-
'schemaSync', 'specKit', 'crossReference', 'generatedStaleness',
|
|
337
|
+
'schemaSync', 'specKit', 'crossReference', 'generatedStaleness',
|
|
338
|
+
'canonicalSync', 'metricsConsistency',
|
|
321
339
|
];
|
|
322
340
|
const out = {};
|
|
323
341
|
for (const k of all) out[k] = CHANGED_ONLY_VALIDATORS.includes(k);
|
package/cli/commands/init.mjs
CHANGED
|
@@ -17,6 +17,45 @@ import { execSync } from 'node:child_process';
|
|
|
17
17
|
import { c, PROFILES } from '../shared.mjs';
|
|
18
18
|
import { ensureSkills, detectAgentMode, detectAIAgent, isSpecKitAvailable, isSpecKitInitialized, getDetectedAgent } from '../ensure-skills.mjs';
|
|
19
19
|
|
|
20
|
+
// v0.20: scaffolder names that can be passed via `init --with <name>` and
|
|
21
|
+
// dispatched to the corresponding standalone runner. Each name maps to its
|
|
22
|
+
// canonical command module. Keep in sync with cli/docguard.mjs router.
|
|
23
|
+
const SCAFFOLDER_DISPATCH = {
|
|
24
|
+
agents: async (dir, cfg, flags) => (await import('./agents.mjs')).runAgents(dir, cfg, flags),
|
|
25
|
+
hooks: async (dir, cfg, flags) => (await import('./hooks.mjs')).runHooks(dir, cfg, flags),
|
|
26
|
+
ci: async (dir, cfg, flags) => (await import('./ci.mjs')).runCI(dir, cfg, flags),
|
|
27
|
+
badge: async (dir, cfg, flags) => (await import('./badge.mjs')).runBadge(dir, cfg, flags),
|
|
28
|
+
llms: async (dir, cfg, flags) => (await import('./llms.mjs')).runLlms(dir, cfg, flags),
|
|
29
|
+
publish: async (dir, cfg, flags) => (await import('./publish.mjs')).runPublish(dir, cfg, flags),
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Run one or more scaffolders after init has completed.
|
|
34
|
+
* Called when `docguard init --with agents,hooks,ci` is invoked.
|
|
35
|
+
* Each scaffolder runs in sequence; if one throws, the rest are skipped
|
|
36
|
+
* (matches the standalone command's failure semantics).
|
|
37
|
+
*/
|
|
38
|
+
async function runScaffolders(projectDir, config, flags, names) {
|
|
39
|
+
const unknown = names.filter(n => !SCAFFOLDER_DISPATCH[n]);
|
|
40
|
+
if (unknown.length > 0) {
|
|
41
|
+
console.error(`${c.red}Unknown --with target(s): ${unknown.join(', ')}${c.reset}`);
|
|
42
|
+
console.log(`${c.dim}Valid: ${Object.keys(SCAFFOLDER_DISPATCH).join(', ')}${c.reset}`);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log(`\n${c.bold}🧰 Scaffolders:${c.reset} ${c.cyan}${names.join(', ')}${c.reset}\n`);
|
|
47
|
+
|
|
48
|
+
for (const name of names) {
|
|
49
|
+
console.log(`${c.dim}── ${name} ───────────────────────────────────────${c.reset}`);
|
|
50
|
+
try {
|
|
51
|
+
await SCAFFOLDER_DISPATCH[name](projectDir, config, flags);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error(`${c.red}✗ ${name} failed: ${err.message}${c.reset}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
20
59
|
function detectProjectType(dir) {
|
|
21
60
|
const pkgPath = resolve(dir, 'package.json');
|
|
22
61
|
if (existsSync(pkgPath)) {
|
|
@@ -54,6 +93,14 @@ function askQuestion(prompt) {
|
|
|
54
93
|
// ── Init Command ─────────────────────────────────────────────────────────
|
|
55
94
|
|
|
56
95
|
export async function runInit(projectDir, config, flags) {
|
|
96
|
+
// v0.20: `--wizard` dispatches to the full interactive onboarding (formerly
|
|
97
|
+
// `docguard setup`). Done before profile validation so the wizard can ask
|
|
98
|
+
// for the profile itself if needed.
|
|
99
|
+
if (flags.wizard) {
|
|
100
|
+
const { runSetup } = await import('./setup.mjs');
|
|
101
|
+
return runSetup(projectDir, config, flags);
|
|
102
|
+
}
|
|
103
|
+
|
|
57
104
|
const profileName = flags.profile || 'standard';
|
|
58
105
|
const profile = PROFILES[profileName];
|
|
59
106
|
|
|
@@ -369,4 +416,11 @@ poetry.lock
|
|
|
369
416
|
|
|
370
417
|
// Auto-install DocGuard skills and commands (spec-kit skills handled by specify init)
|
|
371
418
|
ensureSkills(projectDir, flags);
|
|
419
|
+
|
|
420
|
+
// v0.20: `docguard init --with agents,hooks,ci,badge,llms,publish` runs
|
|
421
|
+
// the named scaffolders after init has finished. Each one runs in sequence
|
|
422
|
+
// and uses the same flags object (so --force / --skip-prompts propagate).
|
|
423
|
+
if (Array.isArray(flags.with) && flags.with.length > 0) {
|
|
424
|
+
await runScaffolders(projectDir, config, flags, flags.with);
|
|
425
|
+
}
|
|
372
426
|
}
|
package/cli/docguard.mjs
CHANGED
|
@@ -282,36 +282,37 @@ function printHelp() {
|
|
|
282
282
|
console.log(`${c.bold}Usage:${c.reset}
|
|
283
283
|
docguard <command> [options]
|
|
284
284
|
|
|
285
|
-
${c.bold}
|
|
286
|
-
${c.green}init${c.reset}
|
|
287
|
-
${c.green}
|
|
288
|
-
${c.green}
|
|
289
|
-
|
|
290
|
-
${c.
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
${c.bold}Memory (build & maintain docs):${c.reset}
|
|
295
|
-
${c.green}generate --plan${c.reset} AI-powered: scan any project, emit agent task manifest + skeleton
|
|
296
|
-
${c.green}sync${c.reset} Refresh code-truth doc sections to match current code (always up to date)
|
|
297
|
-
|
|
298
|
-
${c.bold}Analysis:${c.reset}
|
|
299
|
-
${c.green}score${c.reset} CDD maturity score (0-100)
|
|
300
|
-
${c.green}trace${c.reset} Requirements traceability matrix
|
|
301
|
-
${c.green}diff${c.reset} Show gaps between docs and code (detailed view)
|
|
302
|
-
|
|
303
|
-
${c.bold}CI/CD & Automation:${c.reset}
|
|
304
|
-
${c.green}ci${c.reset} Pipeline gate (guard + score, exit codes)
|
|
305
|
-
${c.green}hooks${c.reset} Install/manage git hooks
|
|
306
|
-
${c.green}watch${c.reset} Watch for changes, re-run guard
|
|
307
|
-
|
|
308
|
-
${c.bold}Utilities:${c.reset}
|
|
285
|
+
${c.bold}The Daily 5${c.reset} ${c.dim}— what you'll reach for 95% of the time${c.reset}
|
|
286
|
+
${c.green}init${c.reset} Bootstrap a project (use ${c.cyan}--wizard${c.reset} for guided / ${c.cyan}--with <name>${c.reset} for scaffolders)
|
|
287
|
+
${c.green}guard${c.reset} Validate against canonical docs (23 validators)
|
|
288
|
+
${c.green}diff${c.reset} Show gaps between docs and code (add ${c.cyan}--since <ref>${c.reset} for changed-file impact)
|
|
289
|
+
${c.green}sync${c.reset} Refresh code-truth doc sections — keeps memory always up to date
|
|
290
|
+
${c.green}score${c.reset} CDD maturity score (0-100; ${c.cyan}--diff${c.reset} for delta between refs)
|
|
291
|
+
|
|
292
|
+
${c.bold}Tools (situational, but day-to-day useful)${c.reset}
|
|
293
|
+
${c.green}diagnose${c.reset} AI orchestrator — guard → emit fix prompts in one command
|
|
309
294
|
${c.green}fix${c.reset} Generate AI fix instructions for specific docs
|
|
310
|
-
${c.green}
|
|
311
|
-
${c.green}
|
|
312
|
-
|
|
313
|
-
${c.
|
|
314
|
-
${c.
|
|
295
|
+
${c.green}generate${c.reset} Reverse-engineer canonical docs from existing code (${c.cyan}--plan${c.reset} for AI scan)
|
|
296
|
+
${c.green}explain${c.reset} Explain a validator key or warning text
|
|
297
|
+
${c.green}memory${c.reset} Show what DocGuard remembers (${c.cyan}--diff${c.reset} drills into drift)
|
|
298
|
+
${c.green}trace${c.reset} Requirements traceability matrix (${c.cyan}--reverse${c.reset} for code→doc map)
|
|
299
|
+
${c.green}upgrade${c.reset} Migrate ${c.cyan}.docguard.json${c.reset} schema + CLI (${c.cyan}--apply --pr${c.reset} for team-wide PR)
|
|
300
|
+
${c.green}watch${c.reset} Live mode: re-run guard on file changes
|
|
301
|
+
|
|
302
|
+
${c.bold}init --with <name>${c.reset} ${c.dim}— optional scaffolders, picked at init time${c.reset}
|
|
303
|
+
${c.dim}agents${c.reset} AGENTS.md / CLAUDE.md / .cursor/rules / Copilot instructions
|
|
304
|
+
${c.dim}hooks${c.reset} Git pre-commit / pre-push hooks
|
|
305
|
+
${c.dim}ci${c.reset} GitHub Actions / pipeline config
|
|
306
|
+
${c.dim}badge${c.reset} Shields.io score badges for README
|
|
307
|
+
${c.dim}llms${c.reset} llms.txt generation
|
|
308
|
+
${c.dim}publish${c.reset} External doc-site scaffold (Mintlify) ${c.dim}— experimental${c.reset}
|
|
309
|
+
|
|
310
|
+
${c.bold}Deprecation aliases${c.reset} ${c.dim}— still work in v0.20.x with a yellow warning${c.reset}
|
|
311
|
+
${c.dim}setup${c.reset} → ${c.cyan}init --wizard${c.reset}
|
|
312
|
+
${c.dim}agents · hooks · ci · badge · llms · publish${c.reset} → ${c.cyan}init --with <name>${c.reset}
|
|
313
|
+
${c.dim}impact${c.reset} → ${c.cyan}diff --since <ref>${c.reset}
|
|
314
|
+
${c.dim}audit${c.reset} → ${c.green}guard${c.reset} ${c.dim}(permanent — no warning, no removal planned)${c.reset}
|
|
315
|
+
${c.dim}See docs-implementation/MIGRATION-v0.20.md for the full timeline.${c.reset}
|
|
315
316
|
|
|
316
317
|
${c.bold}Options:${c.reset}
|
|
317
318
|
--dir <path> Project directory (default: current directory)
|
|
@@ -459,6 +460,17 @@ async function main() {
|
|
|
459
460
|
// v0.17-P2: `docguard memory --diff` drills into accuracy mismatches.
|
|
460
461
|
// Distinct from the `diff` command itself (which is a top-level cmd).
|
|
461
462
|
flags.diff = true;
|
|
463
|
+
} else if (args[i] === '--with' && args[i + 1]) {
|
|
464
|
+
// v0.20: `docguard init --with agents,hooks,ci,badge,llms,publish`
|
|
465
|
+
// folds the six standalone scaffolders into init. Comma-separated
|
|
466
|
+
// names, each dispatched to the matching runner after init finishes.
|
|
467
|
+
flags.with = args[i + 1].split(',').map(s => s.trim()).filter(Boolean);
|
|
468
|
+
i++;
|
|
469
|
+
} else if (args[i] === '--wizard') {
|
|
470
|
+
// v0.20: `docguard init --wizard` runs the 7-step interactive
|
|
471
|
+
// onboarding flow (previously `docguard setup`). `setup` keeps
|
|
472
|
+
// working as a deprecation alias.
|
|
473
|
+
flags.wizard = true;
|
|
462
474
|
} else if (!args[i].startsWith('--') && i > 0) {
|
|
463
475
|
// Positional args go into flags.args for commands that take them (e.g.
|
|
464
476
|
// `docguard trace --reverse <path>`). Skip the command itself (i === 0).
|
|
@@ -522,17 +534,61 @@ async function main() {
|
|
|
522
534
|
ensureSkills(projectDir, flags);
|
|
523
535
|
}
|
|
524
536
|
|
|
537
|
+
// v0.20: deprecation aliases. The legacy command keeps working through v0.20
|
|
538
|
+
// and emits a yellow stderr warning suggesting the new shape. Quiet mode
|
|
539
|
+
// (e.g. inside hooks) suppresses the warning so CI output stays clean.
|
|
540
|
+
// The full deprecation timeline is in docs-implementation/MIGRATION-v0.20.md.
|
|
541
|
+
const DEPRECATED_COMMANDS = {
|
|
542
|
+
setup: { since: '0.20', replacement: 'docguard init --wizard' },
|
|
543
|
+
agents: { since: '0.20', replacement: 'docguard init --with agents' },
|
|
544
|
+
hooks: { since: '0.20', replacement: 'docguard init --with hooks' },
|
|
545
|
+
ci: { since: '0.20', replacement: 'docguard init --with ci' },
|
|
546
|
+
badge: { since: '0.20', replacement: 'docguard init --with badge' },
|
|
547
|
+
llms: { since: '0.20', replacement: 'docguard init --with llms' },
|
|
548
|
+
publish: { since: '0.20', replacement: 'docguard init --with publish' },
|
|
549
|
+
impact: { since: '0.20', replacement: 'docguard diff --since <ref>' },
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
// v0.20: dropped aliases — the 10 cute variants the audit identified.
|
|
553
|
+
// `audit` is intentionally NOT here; it remains a permanent silent alias
|
|
554
|
+
// for `guard` (per SURFACE-AUDIT §8.1 — older CI scripts depend on it).
|
|
555
|
+
const DROPPED_ALIASES = {
|
|
556
|
+
onboard: 'setup (deprecated) — try `docguard init --wizard`',
|
|
557
|
+
gen: 'generate',
|
|
558
|
+
badges: 'badge (deprecated) — try `docguard init --with badge`',
|
|
559
|
+
pipeline: 'ci (deprecated) — try `docguard init --with ci`',
|
|
560
|
+
repair: 'fix',
|
|
561
|
+
dx: 'diagnose',
|
|
562
|
+
pub: 'publish (deprecated) — try `docguard init --with publish`',
|
|
563
|
+
traceability: 'trace',
|
|
564
|
+
'help-warning': 'explain',
|
|
565
|
+
update: 'upgrade',
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
if (DROPPED_ALIASES[command]) {
|
|
569
|
+
console.error(`${c.red}Unknown command: ${command}${c.reset}`);
|
|
570
|
+
console.error(`${c.yellow}Hint: this alias was removed in v0.20. Try ${c.cyan}docguard ${DROPPED_ALIASES[command]}${c.yellow}.${c.reset}`);
|
|
571
|
+
console.error(`${c.dim}See docs-implementation/MIGRATION-v0.20.md for the full list.${c.reset}`);
|
|
572
|
+
process.exit(1);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
if (DEPRECATED_COMMANDS[command] && !flags.quiet) {
|
|
576
|
+
const { since, replacement } = DEPRECATED_COMMANDS[command];
|
|
577
|
+
console.error(`${c.yellow}⚠ Deprecated since v${since}:${c.reset} ${c.cyan}docguard ${command}${c.reset} → use ${c.cyan}${replacement}${c.reset}`);
|
|
578
|
+
console.error(`${c.dim} The old form still works in v0.20.x but will be removed in v1.0. See MIGRATION-v0.20.md.${c.reset}`);
|
|
579
|
+
}
|
|
580
|
+
|
|
525
581
|
switch (command) {
|
|
526
582
|
case 'audit':
|
|
527
|
-
//
|
|
583
|
+
// Permanent silent alias for guard (SURFACE-AUDIT §8.1).
|
|
528
584
|
runGuard(projectDir, config, flags);
|
|
529
585
|
break;
|
|
530
586
|
case 'init':
|
|
531
587
|
await runInit(projectDir, config, flags);
|
|
532
588
|
break;
|
|
533
589
|
case 'setup':
|
|
534
|
-
|
|
535
|
-
await
|
|
590
|
+
// v0.20: deprecated → dispatches to init --wizard
|
|
591
|
+
await runInit(projectDir, config, { ...flags, wizard: true });
|
|
536
592
|
break;
|
|
537
593
|
case 'guard':
|
|
538
594
|
runGuard(projectDir, config, flags);
|
|
@@ -541,32 +597,35 @@ async function main() {
|
|
|
541
597
|
runScore(projectDir, config, flags);
|
|
542
598
|
break;
|
|
543
599
|
case 'diff':
|
|
544
|
-
|
|
600
|
+
// v0.20: `docguard diff --since <ref>` dispatches to the impact-mode
|
|
601
|
+
// analyzer (post-commit "which docs reference files changed since X").
|
|
602
|
+
// Without --since it's the standard current-state drift report.
|
|
603
|
+
if (flags.since) {
|
|
604
|
+
runImpact(projectDir, config, flags);
|
|
605
|
+
} else {
|
|
606
|
+
runDiff(projectDir, config, flags);
|
|
607
|
+
}
|
|
545
608
|
break;
|
|
546
609
|
case 'agents':
|
|
547
|
-
|
|
610
|
+
// v0.20: deprecated → dispatches through init --with
|
|
611
|
+
await runInit(projectDir, config, { ...flags, with: ['agents'], skipPrompts: true });
|
|
548
612
|
break;
|
|
549
613
|
case 'generate':
|
|
550
|
-
case 'gen':
|
|
551
614
|
runGenerate(projectDir, config, flags);
|
|
552
615
|
break;
|
|
553
616
|
case 'hooks':
|
|
554
|
-
|
|
617
|
+
await runInit(projectDir, config, { ...flags, with: ['hooks'], skipPrompts: true });
|
|
555
618
|
break;
|
|
556
619
|
case 'badge':
|
|
557
|
-
|
|
558
|
-
runBadge(projectDir, config, flags);
|
|
620
|
+
await runInit(projectDir, config, { ...flags, with: ['badge'], skipPrompts: true });
|
|
559
621
|
break;
|
|
560
622
|
case 'ci':
|
|
561
|
-
|
|
562
|
-
runCI(projectDir, config, flags);
|
|
623
|
+
await runInit(projectDir, config, { ...flags, with: ['ci'], skipPrompts: true });
|
|
563
624
|
break;
|
|
564
625
|
case 'fix':
|
|
565
|
-
case 'repair':
|
|
566
626
|
runFix(projectDir, config, flags);
|
|
567
627
|
break;
|
|
568
628
|
case 'diagnose':
|
|
569
|
-
case 'dx':
|
|
570
629
|
runDiagnose(projectDir, config, flags);
|
|
571
630
|
break;
|
|
572
631
|
case 'watch':
|
|
@@ -576,25 +635,23 @@ async function main() {
|
|
|
576
635
|
runSync(projectDir, config, flags);
|
|
577
636
|
break;
|
|
578
637
|
case 'publish':
|
|
579
|
-
|
|
580
|
-
runPublish(projectDir, config, flags);
|
|
638
|
+
await runInit(projectDir, config, { ...flags, with: ['publish'], skipPrompts: true });
|
|
581
639
|
break;
|
|
582
640
|
case 'trace':
|
|
583
|
-
case 'traceability':
|
|
584
641
|
runTrace(projectDir, config, flags);
|
|
585
642
|
break;
|
|
586
643
|
case 'llms':
|
|
587
|
-
|
|
644
|
+
await runInit(projectDir, config, { ...flags, with: ['llms'], skipPrompts: true });
|
|
588
645
|
break;
|
|
589
646
|
case 'upgrade':
|
|
590
|
-
case 'update':
|
|
591
647
|
await runUpgrade(projectDir, config, flags);
|
|
592
648
|
break;
|
|
593
649
|
case 'impact':
|
|
594
|
-
|
|
650
|
+
// v0.20: deprecated alias for `diff --since`. Default --since HEAD~1
|
|
651
|
+
// matches impact's historical behavior.
|
|
652
|
+
runImpact(projectDir, config, { ...flags, since: flags.since || 'HEAD~1' });
|
|
595
653
|
break;
|
|
596
654
|
case 'explain':
|
|
597
|
-
case 'help-warning':
|
|
598
655
|
runExplain(projectDir, config, flags);
|
|
599
656
|
break;
|
|
600
657
|
case 'memory':
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical-Sync Validator — v0.19-A.
|
|
3
|
+
*
|
|
4
|
+
* The missing self-check. Until this validator existed, `guard` could not
|
|
5
|
+
* see when the docs lied about DocGuard's own surface. README claimed
|
|
6
|
+
* "ships 19 commands" while 21 files existed in `cli/commands/`; the
|
|
7
|
+
* architecture diagram drifted across 5 releases (15 → 19 → still wrong)
|
|
8
|
+
* because no validator was checking. SURFACE-AUDIT.md §7 specifies the
|
|
9
|
+
* rules; this is the implementation.
|
|
10
|
+
*
|
|
11
|
+
* Scope: DocGuard's own repository only. Gated by `package.json` name ===
|
|
12
|
+
* "docguard-cli". For every other project, this validator returns N/A —
|
|
13
|
+
* the "ships N commands" pattern is meaningless in a generic project's
|
|
14
|
+
* docs (their N refers to their own product, not DocGuard's surface).
|
|
15
|
+
*
|
|
16
|
+
* What it checks:
|
|
17
|
+
* 1. README "ships N commands" matches `cli/commands/*.mjs` file count
|
|
18
|
+
* 2. README "N validators" matches `runGuardInternal()` output length
|
|
19
|
+
* (or, if no guardResults passed, falls back to file count + the 2
|
|
20
|
+
* inlined validators: Doc Sections in structure.mjs + Spec-Kit)
|
|
21
|
+
* 3. Validator names enumerated inline in README appear in guard output
|
|
22
|
+
*
|
|
23
|
+
* What it explicitly skips:
|
|
24
|
+
* - ROADMAP.md (historical phase logs — "Built with 9 validators" is
|
|
25
|
+
* legitimately about v0.7, not today)
|
|
26
|
+
* - CHANGELOG.md (same — entries describe what shipped, not current state)
|
|
27
|
+
* - docs-implementation/ (snapshots of past state)
|
|
28
|
+
* - `<!-- docguard:section source=human -->` blocks (prose, not inventory)
|
|
29
|
+
*
|
|
30
|
+
* Self-counting: per SURFACE-AUDIT §8.5, this validator counts itself.
|
|
31
|
+
* After v0.19 ships, README claims "23 validators" (the previous 22 +
|
|
32
|
+
* canonical-sync). The check passes only if the README matches reality
|
|
33
|
+
* INCLUDING the new validator.
|
|
34
|
+
*
|
|
35
|
+
* Severity: HIGH — a doc lying about the basic surface is a credibility
|
|
36
|
+
* killer for a documentation-quality tool.
|
|
37
|
+
*
|
|
38
|
+
* Zero NPM runtime dependencies — pure Node.js built-ins only.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
42
|
+
import { resolve, join } from 'node:path';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Validate that README count claims about DocGuard's surface match code-truth.
|
|
46
|
+
* Returns N/A for non-DocGuard projects.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} projectDir - Project root directory
|
|
49
|
+
* @param {object} config - DocGuard config (unused but required by validator interface)
|
|
50
|
+
* @param {Array} [guardResults] - Results array from runGuardInternal (optional but recommended)
|
|
51
|
+
* @returns {{ errors: string[], warnings: string[], fixes: object[], passed: number, total: number, na?: boolean, naReason?: string }}
|
|
52
|
+
*/
|
|
53
|
+
export function validateCanonicalSync(projectDir, config, guardResults) {
|
|
54
|
+
const result = { errors: [], warnings: [], fixes: [], passed: 0, total: 0 };
|
|
55
|
+
|
|
56
|
+
// ── Gate: only run in DocGuard's own repo ─────────────────────────────
|
|
57
|
+
const pkgPath = resolve(projectDir, 'package.json');
|
|
58
|
+
if (!existsSync(pkgPath)) {
|
|
59
|
+
return { ...result, na: true, naReason: 'no package.json' };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let pkg;
|
|
63
|
+
try {
|
|
64
|
+
pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
65
|
+
} catch {
|
|
66
|
+
return { ...result, na: true, naReason: 'unreadable package.json' };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (pkg.name !== 'docguard-cli') {
|
|
70
|
+
return {
|
|
71
|
+
...result,
|
|
72
|
+
na: true,
|
|
73
|
+
naReason: 'canonical-sync only runs in the docguard-cli repo (it polices DocGuard\'s own surface)',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Gather code-truth ────────────────────────────────────────────────
|
|
78
|
+
const cliDir = resolve(projectDir, 'cli');
|
|
79
|
+
const commandsDir = resolve(cliDir, 'commands');
|
|
80
|
+
const validatorsDir = resolve(cliDir, 'validators');
|
|
81
|
+
|
|
82
|
+
if (!existsSync(commandsDir) || !existsSync(validatorsDir)) {
|
|
83
|
+
return { ...result, na: true, naReason: 'cli/commands or cli/validators not found' };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const commandFiles = readdirSync(commandsDir).filter(f => f.endsWith('.mjs'));
|
|
87
|
+
const actualCommandFileCount = commandFiles.length;
|
|
88
|
+
|
|
89
|
+
// v0.20: the user-facing command count is what shows up in --help's "Daily 5"
|
|
90
|
+
// and "Tools" sections — NOT the file count, since deprecation aliases dispatch
|
|
91
|
+
// through the same files. Parse cli/docguard.mjs to find names in those
|
|
92
|
+
// sections so the README claim and code-truth stay in lockstep across renames.
|
|
93
|
+
const cliEntry = resolve(cliDir, 'docguard.mjs');
|
|
94
|
+
let actualUserFacingCount = actualCommandFileCount; // fallback if parse fails
|
|
95
|
+
if (existsSync(cliEntry)) {
|
|
96
|
+
try {
|
|
97
|
+
const cliSrc = readFileSync(cliEntry, 'utf-8');
|
|
98
|
+
// Match `${c.green}<name>${c.reset}` inside the Daily 5 / Tools blocks.
|
|
99
|
+
// Anything in init --with / Deprecation aliases sections uses c.dim, so
|
|
100
|
+
// they're naturally excluded.
|
|
101
|
+
const helpBlock = cliSrc.match(/The Daily 5[\s\S]*?Deprecation aliases/);
|
|
102
|
+
if (helpBlock) {
|
|
103
|
+
const greenNames = [...helpBlock[0].matchAll(/\$\{c\.green\}(\w+)\$\{c\.reset\}/g)]
|
|
104
|
+
.map(m => m[1]);
|
|
105
|
+
// Unique names (init shows up only once)
|
|
106
|
+
actualUserFacingCount = [...new Set(greenNames)].length;
|
|
107
|
+
}
|
|
108
|
+
} catch { /* fall through to file-count fallback */ }
|
|
109
|
+
}
|
|
110
|
+
const actualCommandCount = actualUserFacingCount;
|
|
111
|
+
|
|
112
|
+
// Validator count: always use the file-count truth source. It's run-order
|
|
113
|
+
// independent (canonical-sync runs BEFORE metrics-consistency at guard time,
|
|
114
|
+
// so guardResults.length would undercount by 1). File count + 1 for the
|
|
115
|
+
// single inlined validator (Doc Sections, exported alongside Structure from
|
|
116
|
+
// structure.mjs).
|
|
117
|
+
const validatorFiles = readdirSync(validatorsDir).filter(f => f.endsWith('.mjs'));
|
|
118
|
+
const actualValidatorCount = validatorFiles.length + 1; // +1 for Doc Sections inlined in structure.mjs
|
|
119
|
+
|
|
120
|
+
// Names list (currently unused for warnings, but kept for future Check 3
|
|
121
|
+
// where the README enumerates validator names inline).
|
|
122
|
+
let actualValidatorNames = [];
|
|
123
|
+
if (Array.isArray(guardResults) && guardResults.length > 0) {
|
|
124
|
+
actualValidatorNames = guardResults.map(r => r.name).filter(Boolean);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ── Read README ─────────────────────────────────────────────────────
|
|
128
|
+
const readmePath = resolve(projectDir, 'README.md');
|
|
129
|
+
if (!existsSync(readmePath)) {
|
|
130
|
+
result.warnings.push('canonical-sync: README.md not found — cannot check surface claims');
|
|
131
|
+
result.total = 1;
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let readme;
|
|
136
|
+
try {
|
|
137
|
+
readme = readFileSync(readmePath, 'utf-8');
|
|
138
|
+
} catch {
|
|
139
|
+
result.warnings.push('canonical-sync: README.md unreadable');
|
|
140
|
+
result.total = 1;
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Check 1: "ships N commands" ─────────────────────────────────────
|
|
145
|
+
result.total++;
|
|
146
|
+
const shipsCommandsRe = /ships\s+\*{0,2}(\d+)\s+commands?\*{0,2}/i;
|
|
147
|
+
const m1 = readme.match(shipsCommandsRe);
|
|
148
|
+
if (m1) {
|
|
149
|
+
const claimed = Number(m1[1]);
|
|
150
|
+
if (claimed === actualCommandCount) {
|
|
151
|
+
result.passed++;
|
|
152
|
+
} else {
|
|
153
|
+
const detail = actualUserFacingCount !== actualCommandFileCount
|
|
154
|
+
? `${actualCommandCount} user-facing commands in --help (${actualCommandFileCount} files including deprecation aliases)`
|
|
155
|
+
: `${actualCommandCount} command file(s)`;
|
|
156
|
+
result.warnings.push(
|
|
157
|
+
`README.md claims "ships ${claimed} commands" but the real count is ${detail}. Update the README.`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
// No claim found — that's OK, just don't check this one
|
|
162
|
+
result.passed++;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── Check 2: "N validators" in surface context ──────────────────────
|
|
166
|
+
// Match phrases like "22 validators", "all 22 validators", "the 22 validators"
|
|
167
|
+
// but NOT phase-log entries like "Built with 9 validators" (those are
|
|
168
|
+
// historical, and ROADMAP.md/CHANGELOG.md are skipped at the file level).
|
|
169
|
+
result.total++;
|
|
170
|
+
const validatorMatches = [...readme.matchAll(/(?:all|the|with|across|ships?)\s+\*{0,2}(\d+)\s+validators?\*{0,2}/gi)];
|
|
171
|
+
if (validatorMatches.length > 0) {
|
|
172
|
+
const wrongClaims = validatorMatches
|
|
173
|
+
.map(m => Number(m[1]))
|
|
174
|
+
.filter(n => n !== actualValidatorCount);
|
|
175
|
+
if (wrongClaims.length === 0) {
|
|
176
|
+
result.passed++;
|
|
177
|
+
} else {
|
|
178
|
+
const uniqueWrong = [...new Set(wrongClaims)];
|
|
179
|
+
result.warnings.push(
|
|
180
|
+
`README.md claims ${uniqueWrong.map(n => `"${n} validators"`).join(' / ')} but guard reports ${actualValidatorCount}. Update the README.`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
result.passed++;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ── Check 3: architecture-diagram counts ────────────────────────────
|
|
188
|
+
// Catches the specific "Commands (N)" and "Validators (N)" patterns in
|
|
189
|
+
// the mermaid block that drifted across 5 releases.
|
|
190
|
+
result.total++;
|
|
191
|
+
const archMatches = [
|
|
192
|
+
{ re: /Commands\s*\((\d+)\)/, label: 'Commands', expected: actualCommandCount },
|
|
193
|
+
{ re: /Validators\s*\((\d+)\)/, label: 'Validators', expected: actualValidatorCount },
|
|
194
|
+
];
|
|
195
|
+
const archWrong = [];
|
|
196
|
+
for (const { re, label, expected } of archMatches) {
|
|
197
|
+
const m = readme.match(re);
|
|
198
|
+
if (m && Number(m[1]) !== expected) {
|
|
199
|
+
archWrong.push(`${label} (${m[1]}) → should be (${expected})`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (archWrong.length === 0) {
|
|
203
|
+
result.passed++;
|
|
204
|
+
} else {
|
|
205
|
+
result.warnings.push(
|
|
206
|
+
`README.md architecture diagram has stale counts: ${archWrong.join('; ')}. Update the mermaid block.`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return result;
|
|
211
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec-Kit Validator — Validates Spec Kit artifacts (specs/, plans/, tasks/, constitution).
|
|
3
|
+
*
|
|
4
|
+
* v0.19-D: Architectural move. The validation logic still lives in
|
|
5
|
+
* `scanners/speckit.mjs` (where the spec-kit data is scanned), but the
|
|
6
|
+
* validator entry-point now lives here in `validators/` so the file count
|
|
7
|
+
* matches the validator count. This also makes the canonical-sync validator's
|
|
8
|
+
* truth source (`ls cli/validators/*.mjs`) align with what `guard` reports.
|
|
9
|
+
*
|
|
10
|
+
* Re-exports `validateSpecKitIntegration` from the scanner. Importers should
|
|
11
|
+
* use this path (`validators/spec-kit.mjs`) going forward.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export { validateSpecKitIntegration } from '../scanners/speckit.mjs';
|
package/docs/quickstart.md
CHANGED
|
@@ -68,7 +68,7 @@ diagnose → AI reads prompts → AI fixes docs → guard verifies
|
|
|
68
68
|
## Verify
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
npx docguard-cli guard # Pass/fail check (
|
|
71
|
+
npx docguard-cli guard # Pass/fail check (23 validators)
|
|
72
72
|
npx docguard-cli score # 0-100 maturity score
|
|
73
73
|
```
|
|
74
74
|
|
|
@@ -4,7 +4,7 @@ Enterprise-grade Canonical-Driven Development (CDD) enforcement and **AI-readabl
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- **
|
|
7
|
+
- **23 Validators** — Structure, Security, Doc Quality, Test-Spec, Drift-Comments, API-Surface, Freshness, Cross-Reference, and 13 more
|
|
8
8
|
- **Language-agnostic** — JS/TS, Python, Rust, Go, Java/Kotlin, Ruby, PHP, C#. Polyglot/monorepo-aware.
|
|
9
9
|
- **AI-powered Generate** — `generate --plan` builds the code-truth skeleton in `<!-- docguard:section -->` markers and emits a structured agent task manifest; the AI writes the prose.
|
|
10
10
|
- **Always up to date** — `sync` surgically refreshes code-truth doc sections in place, **preserves human prose**, flags prose for agent review.
|
|
@@ -3,7 +3,7 @@ schema_version: "1.0"
|
|
|
3
3
|
extension:
|
|
4
4
|
id: "docguard"
|
|
5
5
|
name: "DocGuard — CDD Enforcement"
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.20.0"
|
|
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"
|
|
@@ -28,22 +28,22 @@ provides:
|
|
|
28
28
|
- name: "speckit.docguard.guard"
|
|
29
29
|
file: "commands/guard.md"
|
|
30
30
|
description: "Run 19-validator quality gate with severity triage and remediation plan"
|
|
31
|
-
aliases: ["docguard.guard"]
|
|
31
|
+
aliases: ["speckit.docguard.guard"]
|
|
32
32
|
|
|
33
33
|
- name: "speckit.docguard.fix"
|
|
34
34
|
file: "commands/generate.md"
|
|
35
35
|
description: "AI-driven documentation repair with codebase research and validation loops"
|
|
36
|
-
aliases: ["docguard.fix"]
|
|
36
|
+
aliases: ["speckit.docguard.fix"]
|
|
37
37
|
|
|
38
38
|
- name: "speckit.docguard.review"
|
|
39
39
|
file: "commands/diagnose.md"
|
|
40
40
|
description: "Cross-document semantic consistency analysis (read-only)"
|
|
41
|
-
aliases: ["docguard.review"]
|
|
41
|
+
aliases: ["speckit.docguard.review"]
|
|
42
42
|
|
|
43
43
|
- name: "speckit.docguard.score"
|
|
44
44
|
file: "commands/score.md"
|
|
45
45
|
description: "CDD maturity score with ROI-based improvement roadmap"
|
|
46
|
-
aliases: ["docguard.score"]
|
|
46
|
+
aliases: ["speckit.docguard.score"]
|
|
47
47
|
|
|
48
48
|
- name: "speckit.docguard.diagnose"
|
|
49
49
|
file: "commands/diagnose.md"
|
|
@@ -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.
|
|
9
|
+
version: 0.20.0
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-fix
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.
|
|
12
|
+
<!-- docguard:version: 0.20.0 -->
|
|
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.
|
|
10
|
+
version: 0.20.0
|
|
11
11
|
source: extensions/spec-kit-docguard/skills/docguard-guard
|
|
12
12
|
---
|
|
13
|
-
<!-- docguard:version: 0.
|
|
13
|
+
<!-- docguard:version: 0.20.0 -->
|
|
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.
|
|
9
|
+
version: 0.20.0
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-review
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.
|
|
12
|
+
<!-- docguard:version: 0.20.0 -->
|
|
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.
|
|
9
|
+
version: 0.20.0
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-score
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.
|
|
12
|
+
<!-- docguard:version: 0.20.0 -->
|
|
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.
|
|
7
|
+
version: 0.20.0
|
|
8
8
|
source: extensions/spec-kit-docguard/skills/docguard-sync
|
|
9
9
|
---
|
|
10
10
|
|
package/package.json
CHANGED