docguard-cli 0.23.0 → 0.25.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 +1 -1
- package/cli/commands/diff.mjs +1 -1
- package/cli/commands/explain.mjs +178 -17
- package/cli/commands/fix.mjs +17 -2
- package/cli/commands/generate.mjs +69 -3
- package/cli/commands/guard.mjs +86 -11
- package/cli/commands/hooks.mjs +12 -7
- package/cli/commands/init.mjs +24 -8
- package/cli/commands/score.mjs +147 -61
- package/cli/commands/setup.mjs +2 -2
- package/cli/commands/sync.mjs +6 -0
- package/cli/commands/trace.mjs +3 -3
- package/cli/commands/upgrade.mjs +61 -13
- package/cli/config.mjs +18 -1
- package/cli/docguard.mjs +156 -2
- package/cli/ensure-skills.mjs +24 -26
- package/cli/scanners/api-doc.mjs +17 -3
- package/cli/scanners/doc-tools.mjs +32 -15
- package/cli/scanners/frontend.mjs +24 -8
- package/cli/scanners/js-ast.mjs +432 -0
- package/cli/scanners/memory-plan.mjs +1 -1
- package/cli/scanners/project-type.mjs +11 -4
- package/cli/scanners/py-ast.mjs +213 -0
- package/cli/scanners/routes.mjs +194 -69
- package/cli/scanners/schemas.mjs +97 -51
- package/cli/shared-git.mjs +0 -0
- package/cli/shared-ignore.mjs +23 -2
- package/cli/shared-source.mjs +59 -2
- package/cli/shared-trace-patterns.mjs +13 -0
- package/cli/shared.mjs +92 -1
- package/cli/validator-markers.mjs +91 -0
- package/cli/validators/api-surface.mjs +37 -3
- package/cli/validators/canonical-sync.mjs +22 -19
- package/cli/validators/doc-quality.mjs +2 -42
- package/cli/validators/docs-coverage.mjs +13 -0
- package/cli/validators/docs-sync.mjs +4 -3
- package/cli/validators/drift.mjs +3 -2
- package/cli/validators/freshness.mjs +47 -15
- package/cli/validators/generated-staleness.mjs +16 -1
- package/cli/validators/metadata-sync.mjs +21 -11
- package/cli/validators/metrics-consistency.mjs +45 -17
- package/cli/validators/security.mjs +13 -5
- package/cli/validators/structure.mjs +6 -5
- package/cli/validators/surface-sync.mjs +7 -5
- package/cli/validators/test-spec.mjs +76 -51
- package/cli/validators/todo-tracking.mjs +4 -2
- package/cli/validators/traceability.mjs +11 -3
- package/cli/writers/sections.mjs +32 -19
- package/docs/commands.md +1 -1
- package/docs/configuration.md +11 -0
- package/docs/faq.md +1 -1
- package/extensions/spec-kit-docguard/README.md +1 -1
- package/extensions/spec-kit-docguard/extension.yml +2 -2
- 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 +2 -1
- package/extensions/spec-kit-docguard/templates/github-workflows/docguard-autofix.yml +3 -2
- package/extensions/spec-kit-docguard/templates/github-workflows/docguard-guard.yml +2 -2
- package/package.json +5 -3
|
@@ -18,76 +18,97 @@ export function validateTestSpec(projectDir, config) {
|
|
|
18
18
|
const content = readFileSync(testSpecPath, 'utf-8');
|
|
19
19
|
const ptc = config.projectTypeConfig || {};
|
|
20
20
|
|
|
21
|
-
// Parse the Source-to-Test Map
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
// Parse the Source-to-Test Map (new header) / Service-to-Test Map (old).
|
|
22
|
+
//
|
|
23
|
+
// Column-HEADER-aware: read the header row to locate the source column, the
|
|
24
|
+
// status column, and EVERY test-file column (Unit Test, Integration Test, …),
|
|
25
|
+
// then map each data row by index WITHOUT discarding empty cells. The old
|
|
26
|
+
// parser filtered empty cells — which shifted every column rightward whenever
|
|
27
|
+
// a cell was blank (e.g. an empty Integration Test) — and only ever checked
|
|
28
|
+
// the 2nd column, so the generated 4-column table's Integration Test was
|
|
29
|
+
// never verified (#9). Splitting on the outer pipes and trimming preserves
|
|
30
|
+
// column alignment so a blank cell stays an empty string in its own slot.
|
|
31
|
+
const mapSection = content.match(/## (?:Service-to-Test Map|Source-to-Test Map)[\s\S]*?(?=\n## |$)/);
|
|
32
|
+
if (mapSection) {
|
|
33
|
+
const splitRow = (line) => {
|
|
34
|
+
const parts = line.split('|');
|
|
35
|
+
parts.shift(); // text before the first pipe
|
|
36
|
+
parts.pop(); // text after the last pipe
|
|
37
|
+
return parts.map(s => s.trim());
|
|
38
|
+
};
|
|
39
|
+
const pipeRows = mapSection[0]
|
|
29
40
|
.split('\n')
|
|
30
|
-
.filter(
|
|
41
|
+
.filter(l => l.trim().startsWith('|') && !/^\s*\|[\s|:-]+\|\s*$/.test(l)); // drop the `---` separator
|
|
42
|
+
const headerCells = pipeRows.length ? splitRow(pipeRows[0]) : [];
|
|
43
|
+
const header = headerCells.map(h => h.toLowerCase());
|
|
44
|
+
|
|
45
|
+
// Classify columns by header name, with positional fallbacks.
|
|
46
|
+
let sourceIdx = header.findIndex(h => /\bsource\b/.test(h));
|
|
47
|
+
if (sourceIdx < 0) sourceIdx = 0;
|
|
48
|
+
let statusIdx = header.findIndex(h => /\bstatus\b/.test(h));
|
|
49
|
+
if (statusIdx < 0) statusIdx = header.length - 1;
|
|
50
|
+
let testIdxs = header
|
|
51
|
+
.map((h, i) => (/\btest\b|\be2e\b/.test(h) ? i : -1))
|
|
52
|
+
.filter(i => i >= 0 && i !== sourceIdx && i !== statusIdx);
|
|
53
|
+
if (testIdxs.length === 0) {
|
|
54
|
+
const fallback = sourceIdx === 1 ? 0 : 1; // the non-source early column
|
|
55
|
+
if (fallback !== statusIdx && fallback < header.length) testIdxs = [fallback];
|
|
56
|
+
}
|
|
31
57
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
.map(s => s.trim())
|
|
36
|
-
.filter(s => s.length > 0);
|
|
58
|
+
const isPlaceholder = (v) =>
|
|
59
|
+
!v || v === '—' || v.includes('N/A') ||
|
|
60
|
+
['source file', 'test file', 'unit test', 'integration test', 'e2e test'].includes(v.toLowerCase());
|
|
37
61
|
|
|
38
|
-
|
|
62
|
+
// Only existence-check a cell that actually LOOKS like a file path: no
|
|
63
|
+
// internal spaces, and either a directory separator or a file extension.
|
|
64
|
+
// A `## Service-to-Test Map` section often holds several sub-tables of
|
|
65
|
+
// different shapes (Controllers, Services, an "Integration Tests" inventory
|
|
66
|
+
// like `| test-file | what it covers |`). Without this guard a prose
|
|
67
|
+
// "what it covers" cell — "Health endpoint with real dependencies" — gets
|
|
68
|
+
// checked as a missing test file (false positive; field test: wu-whatsappinbox).
|
|
69
|
+
const isPathLike = (v) => !!v && !/\s/.test(v) && (/[\\/]/.test(v) || /\.[A-Za-z0-9]{1,6}$/.test(v));
|
|
39
70
|
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
const
|
|
71
|
+
for (const row of pipeRows.slice(1)) { // skip the header row
|
|
72
|
+
const cells = splitRow(row);
|
|
73
|
+
const sourceFile = cells[sourceIdx] || '';
|
|
74
|
+
const status = cells[statusIdx] || '';
|
|
43
75
|
|
|
44
|
-
// Skip template/example rows and italic placeholder rows
|
|
45
|
-
if (sourceFile.startsWith('<!--') || sourceFile === 'Source File' || sourceFile.startsWith('*')) continue;
|
|
76
|
+
// Skip template/example rows and italic placeholder rows.
|
|
77
|
+
if (!sourceFile || sourceFile.startsWith('<!--') || sourceFile === 'Source File' || sourceFile.startsWith('*')) continue;
|
|
46
78
|
|
|
47
79
|
// Author-declared gaps (❌/⚠️) are surfaced as warnings. A ✅ glyph is the
|
|
48
80
|
// author's CLAIM, not proof — it is NOT counted as a pass. The real pass
|
|
49
81
|
// comes from the file-existence checks below (code truth, not the glyph).
|
|
50
|
-
if (status
|
|
82
|
+
if (status.includes('❌')) {
|
|
51
83
|
results.total++;
|
|
52
|
-
results.warnings.push(
|
|
53
|
-
|
|
54
|
-
);
|
|
55
|
-
} else if (status && status.includes('⚠️')) {
|
|
84
|
+
results.warnings.push(`TEST-SPEC declares ${sourceFile} as ❌ — missing tests`);
|
|
85
|
+
} else if (status.includes('⚠️')) {
|
|
56
86
|
results.total++;
|
|
57
|
-
results.warnings.push(
|
|
58
|
-
`TEST-SPEC declares ${sourceFile} as ⚠️ — partial coverage`
|
|
59
|
-
);
|
|
87
|
+
results.warnings.push(`TEST-SPEC declares ${sourceFile} as ⚠️ — partial coverage`);
|
|
60
88
|
}
|
|
61
89
|
|
|
62
90
|
// ── File existence checks ───────────────────────────────────────
|
|
63
|
-
// Verify source file still exists (catch stale map entries)
|
|
91
|
+
// Verify source file still exists (catch stale map entries).
|
|
64
92
|
const cleanSource = sourceFile.replace(/`/g, '').trim();
|
|
65
|
-
if (cleanSource && cleanSource !== '—' && cleanSource !== 'Source File') {
|
|
66
|
-
|
|
67
|
-
if (
|
|
68
|
-
results.total++;
|
|
69
|
-
results.warnings.push(
|
|
70
|
-
`Source-to-Test Map: source file \`${cleanSource}\` not found on disk — stale entry?`
|
|
71
|
-
);
|
|
72
|
-
} else {
|
|
73
|
-
results.total++;
|
|
93
|
+
if (cleanSource && cleanSource !== '—' && cleanSource !== 'Source File' && isPathLike(cleanSource)) {
|
|
94
|
+
results.total++;
|
|
95
|
+
if (existsSync(resolve(projectDir, cleanSource))) {
|
|
74
96
|
results.passed++;
|
|
97
|
+
} else {
|
|
98
|
+
results.warnings.push(`Source-to-Test Map: source file \`${cleanSource}\` not found on disk — stale entry?`);
|
|
75
99
|
}
|
|
76
100
|
}
|
|
77
101
|
|
|
78
|
-
// Verify test file exists
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
results.warnings.push(
|
|
86
|
-
`Source-to-Test Map: test file \`${cleanTest}\` not found — referenced by ${cleanSource}`
|
|
87
|
-
);
|
|
88
|
-
} else {
|
|
89
|
-
results.total++;
|
|
102
|
+
// Verify EVERY declared test file exists — Unit Test AND Integration Test
|
|
103
|
+
// (the old parser only checked one column).
|
|
104
|
+
for (const ti of testIdxs) {
|
|
105
|
+
const cleanTest = (cells[ti] || '').replace(/`/g, '').trim();
|
|
106
|
+
if (isPlaceholder(cleanTest) || !isPathLike(cleanTest)) continue;
|
|
107
|
+
results.total++;
|
|
108
|
+
if (existsSync(resolve(projectDir, cleanTest))) {
|
|
90
109
|
results.passed++;
|
|
110
|
+
} else {
|
|
111
|
+
results.warnings.push(`Source-to-Test Map: test file \`${cleanTest}\` not found — referenced by ${cleanSource}`);
|
|
91
112
|
}
|
|
92
113
|
}
|
|
93
114
|
}
|
|
@@ -175,7 +196,11 @@ export function validateTestSpec(projectDir, config) {
|
|
|
175
196
|
|
|
176
197
|
if (hasTestDir || hasColocated || hasConfigTests) {
|
|
177
198
|
// Tests exist but the spec maps none of them → not applicable, not a pass.
|
|
178
|
-
|
|
199
|
+
// v0.24: the validator reads column 1 as source, column 2 as the test
|
|
200
|
+
// file, and the last as status — so both the minimal 3-column shape and
|
|
201
|
+
// the 4-column table `docguard generate` emits are accepted. Say so, since
|
|
202
|
+
// the guidance previously contradicted the generated skeleton (field report).
|
|
203
|
+
results.note = 'TEST-SPEC.md declares no service-to-test mappings. Add a "## Source-to-Test Map" table — column 1 is the source, column 2 the test file, the last column the status. Both `| Source | Test file | Status |` and the generated `| Source File | Unit Test | Integration Test | Status |` shapes work. Run `docguard explain testSpec` for details.';
|
|
179
204
|
} else {
|
|
180
205
|
results.warnings.push(
|
|
181
206
|
'No test directory or co-located test files found. ' +
|
|
@@ -35,8 +35,10 @@ const TEST_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx']);
|
|
|
35
35
|
|
|
36
36
|
// ──── Patterns ────
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
// TEMP must be the standalone word — `(?![A-Za-z])` excludes TEMPLATE, TEMPORARY,
|
|
39
|
+
// TEMPO, TEMPEST, etc. (the old `(?!late|orar)` only caught the first two).
|
|
40
|
+
const TODO_PATTERN = /\b(TODO|FIXME|HACK|XXX|TEMP(?![A-Za-z])|WORKAROUND)\s*[(:]/;
|
|
41
|
+
const TODO_EXTRACT = /\b(TODO|FIXME|HACK|XXX|TEMP(?![A-Za-z])|WORKAROUND)\s*[:(]?\s*(.+)/;
|
|
40
42
|
|
|
41
43
|
// Matches a comment-opening marker. Real TODOs live in comments — restricting
|
|
42
44
|
// matches to text AFTER a comment marker prevents false positives from regex
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
17
17
|
import { resolve, join, relative, basename, extname } from 'node:path';
|
|
18
|
-
import { TRACE_MAP, TEST_PATTERNS } from '../shared-trace-patterns.mjs';
|
|
18
|
+
import { TRACE_MAP, TEST_PATTERNS, isTraceableSource } from '../shared-trace-patterns.mjs';
|
|
19
19
|
|
|
20
20
|
const IGNORE_DIRS = new Set([
|
|
21
21
|
'node_modules', '.git', '.next', 'dist', 'build', 'coverage',
|
|
@@ -39,7 +39,12 @@ const DEFAULT_REQ_PATTERNS = [
|
|
|
39
39
|
/\b(ARCH)-(\d{2,4})\b/g,
|
|
40
40
|
/\b(MOD)-(\d{2,4})\b/g,
|
|
41
41
|
/\b(SC)-(\d{2,4})\b/g, // Spec Kit: Success Criteria
|
|
42
|
-
|
|
42
|
+
// Spec Kit task IDs (T001, T002). Unlike the hyphenated IDs above, a bare
|
|
43
|
+
// `T350` over-matches prose (timeouts, model names, status codes), forcing
|
|
44
|
+
// spurious "untraced requirement" warnings. Anchor to the two contexts where
|
|
45
|
+
// a real task ID actually appears: a markdown checklist marker (`- [ ] T001`,
|
|
46
|
+
// the spec-kit tasks.md format) or a test annotation (`@req T001`/`@task`).
|
|
47
|
+
/(?<=\[[ xX]\]\s|@(?:req|task|covers)\s)(T)(\d{3,4})\b/g,
|
|
43
48
|
];
|
|
44
49
|
|
|
45
50
|
/**
|
|
@@ -102,9 +107,12 @@ export function validateTraceability(projectDir, config) {
|
|
|
102
107
|
|
|
103
108
|
// Count matching source files
|
|
104
109
|
// ⚡ Bolt: Fast early return using .some() instead of .filter()
|
|
110
|
+
// v0.24: skip .md files — a doc isn't "linked" just because another doc's
|
|
111
|
+
// name matches the glob (e.g. SECURITY's `guard` matching docguard.guard.md);
|
|
112
|
+
// that masked genuinely unlinked docs (field report).
|
|
105
113
|
let hasSource = false;
|
|
106
114
|
for (const pattern of traceInfo.sourcePatterns) {
|
|
107
|
-
if (projectFiles.some(f => pattern.glob.test(f))) {
|
|
115
|
+
if (projectFiles.some(f => isTraceableSource(f) && pattern.glob.test(f))) {
|
|
108
116
|
hasSource = true;
|
|
109
117
|
break;
|
|
110
118
|
}
|
package/cli/writers/sections.mjs
CHANGED
|
@@ -36,8 +36,20 @@ function parseAttrs(attrStr) {
|
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Parse all well-formed sections in a document.
|
|
39
|
-
* A section is
|
|
40
|
-
*
|
|
39
|
+
* A section is an open-marker line, then content lines, then a close-marker
|
|
40
|
+
* line. Sections are FLAT — there is no nesting.
|
|
41
|
+
*
|
|
42
|
+
* Two malformed cases are handled so that a hand-edit mistake can never cause
|
|
43
|
+
* a later regenerate to overwrite human prose:
|
|
44
|
+
* - An open with no matching close (reached EOF) is dropped, not returned.
|
|
45
|
+
* - A NEW open encountered while a previous one is still unclosed means the
|
|
46
|
+
* previous open was malformed (its close was deleted or typo'd). We ABANDON
|
|
47
|
+
* the previous open and restart at the new one. The earlier, broken
|
|
48
|
+
* approach kept the first open and paired it with the *next* section's
|
|
49
|
+
* close — swallowing the human prose and the next section into one body,
|
|
50
|
+
* which a subsequent replaceSection() would then silently overwrite.
|
|
51
|
+
* A dropped section simply isn't returned, so replaceSection() no-ops on it
|
|
52
|
+
* (returns content unchanged) instead of corrupting the surrounding document.
|
|
41
53
|
* @returns {Array<{ id, source, attrs, openLine, closeLine, body }>}
|
|
42
54
|
*/
|
|
43
55
|
export function parseSections(content) {
|
|
@@ -47,25 +59,26 @@ export function parseSections(content) {
|
|
|
47
59
|
|
|
48
60
|
for (let i = 0; i < lines.length; i++) {
|
|
49
61
|
const line = lines[i];
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
62
|
+
if (CLOSE_RE.test(line)) {
|
|
63
|
+
if (open !== null) {
|
|
64
|
+
sections.push({
|
|
65
|
+
id: open.attrs.id || '',
|
|
66
|
+
source: open.attrs.source || 'code',
|
|
67
|
+
attrs: open.attrs,
|
|
68
|
+
openLine: open.openLine,
|
|
69
|
+
closeLine: i,
|
|
70
|
+
body: lines.slice(open.openLine + 1, i).join('\n'),
|
|
71
|
+
});
|
|
72
|
+
open = null;
|
|
55
73
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
body: lines.slice(open.openLine + 1, i).join('\n'),
|
|
64
|
-
});
|
|
65
|
-
open = null;
|
|
74
|
+
// A close with no matching open is ignored.
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const om = line.match(OPEN_RE);
|
|
78
|
+
if (om) {
|
|
79
|
+
// Abandon any still-open (malformed) section; start fresh here.
|
|
80
|
+
open = { attrs: parseAttrs(om[1] || ''), openLine: i };
|
|
66
81
|
}
|
|
67
|
-
// Note: a second open before a close just extends the search for a close;
|
|
68
|
-
// we keep the FIRST open's start, so malformed nesting can't corrupt content.
|
|
69
82
|
}
|
|
70
83
|
return sections;
|
|
71
84
|
}
|
package/docs/commands.md
CHANGED
package/docs/configuration.md
CHANGED
|
@@ -73,6 +73,17 @@ See [Profiles](./profiles.md) for details.
|
|
|
73
73
|
| `environment` | `true` | Setup steps, env vars, prerequisites, .env.example |
|
|
74
74
|
| `freshness` | varies | Docs updated recently relative to code changes (git-based) |
|
|
75
75
|
|
|
76
|
+
## Muting a validator
|
|
77
|
+
|
|
78
|
+
Two ways to turn a validator off, for two different intents:
|
|
79
|
+
|
|
80
|
+
| Intent | How | Renders as |
|
|
81
|
+
|--------|-----|-----------|
|
|
82
|
+
| Operational toggle (CI speed, not relevant *right now*) | `.docguard.json` → `"validators": { "testSpec": false }` | silent — disabled |
|
|
83
|
+
| **Intentional non-applicability** (POC with no tests, library with no auth) | inline marker in a canonical doc or `AGENTS.md`:<br>`<!-- docguard:validator testSpec n/a — POC, no automated tests yet -->` | `➖ Test-Spec [N/A] (declared N/A: …)` — visible, git-tracked |
|
|
84
|
+
|
|
85
|
+
The marker is preferred when the validator genuinely does not apply: the rationale lives next to the declaration, travels with the repo, and shows up honestly as N/A rather than a hidden skip or a fake green check. The key is the validator key from the table above (case/separator tolerant — `test-spec` works too); a mistyped key is reported as a warning rather than silently ignored. A no-tests POC typically marks both `testSpec` and `traceability` N/A.
|
|
86
|
+
|
|
76
87
|
## Project Type Detection
|
|
77
88
|
|
|
78
89
|
DocGuard auto-detects your project type from `package.json`:
|
package/docs/faq.md
CHANGED
|
@@ -129,7 +129,7 @@ Only if you install hooks (`docguard hooks`). Without hooks, it's advisory only.
|
|
|
129
129
|
|
|
130
130
|
### Does DocGuard have dependencies?
|
|
131
131
|
|
|
132
|
-
**
|
|
132
|
+
**One.** `@babel/parser` (exact-pinned), for AST-accurate JS/TS parsing — and it's loaded *optionally*, so DocGuard still runs (on the regex fallback tier) if it's ever missing. Everything else is pure Node.js built-ins. Works with Node.js 18+. Python parsing optionally shells out to the project's own `python3`.
|
|
133
133
|
|
|
134
134
|
### Does it work with non-JavaScript projects?
|
|
135
135
|
|
|
@@ -12,7 +12,7 @@ Enterprise-grade Canonical-Driven Development (CDD) enforcement and **AI-readabl
|
|
|
12
12
|
- **5 AI Skills** — docguard-fix, docguard-guard, docguard-sync, docguard-review, docguard-score (enterprise-grade behavior protocols, not just step-lists)
|
|
13
13
|
- **Workflow Chaining** — YAML handoffs enable guard → sync → fix → review → score flows
|
|
14
14
|
- **Spec Kit Hooks** — Quality gate integrations at implement, tasks, and review phases
|
|
15
|
-
- **
|
|
15
|
+
- **Minimal Dependencies** — one pinned, optional-load parser (`@babel/parser`); Node.js built-ins otherwise
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
@@ -3,8 +3,8 @@ schema_version: "1.0"
|
|
|
3
3
|
extension:
|
|
4
4
|
id: "docguard"
|
|
5
5
|
name: "DocGuard — CDD Enforcement"
|
|
6
|
-
version: "0.
|
|
7
|
-
description: "Canonical-Driven Development enforcement as a true spec-kit extension. LLM-first design with
|
|
6
|
+
version: "0.25.0"
|
|
7
|
+
description: "Canonical-Driven Development enforcement as a true spec-kit extension. LLM-first design with automated validators, 4 AI behavior skills, spec-kit skill chaining, and workflow hooks. One pinned runtime dependency (@babel/parser); pure Node.js otherwise."
|
|
8
8
|
author: "Ricardo Accioly"
|
|
9
9
|
repository: "https://github.com/raccioly/docguard"
|
|
10
10
|
license: "MIT"
|
|
@@ -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.25.0
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-fix
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.
|
|
12
|
+
<!-- docguard:version: 0.25.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.25.0
|
|
11
11
|
source: extensions/spec-kit-docguard/skills/docguard-guard
|
|
12
12
|
---
|
|
13
|
-
<!-- docguard:version: 0.
|
|
13
|
+
<!-- docguard:version: 0.25.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.25.0
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-review
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.
|
|
12
|
+
<!-- docguard:version: 0.25.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.25.0
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-score
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.
|
|
12
|
+
<!-- docguard:version: 0.25.0 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Score Skill
|
|
15
15
|
|
|
@@ -4,9 +4,10 @@ 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.25.0
|
|
8
8
|
source: extensions/spec-kit-docguard/skills/docguard-sync
|
|
9
9
|
---
|
|
10
|
+
<!-- docguard:version: 0.25.0 -->
|
|
10
11
|
|
|
11
12
|
# DocGuard Sync Skill
|
|
12
13
|
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
# Setup:
|
|
14
14
|
# 1. Copy this file to .github/workflows/docguard-autofix.yml
|
|
15
15
|
# 2. Ensure the workflow has the permissions block below (write access).
|
|
16
|
-
# 3.
|
|
16
|
+
# 3. Pinned to the `@v0.25.0` release tag for reproducible CI. Change it to a
|
|
17
|
+
# newer tag to upgrade, or to `@main` to always track the latest (unpinned).
|
|
17
18
|
#
|
|
18
19
|
# Security note: this workflow makes commits back to the PR branch. It refuses
|
|
19
20
|
# to run on PRs from forks (where pushing back is impossible by design).
|
|
@@ -43,7 +44,7 @@ jobs:
|
|
|
43
44
|
fetch-depth: 0
|
|
44
45
|
|
|
45
46
|
- name: Run DocGuard fix --write + auto-commit + PR comment
|
|
46
|
-
uses: raccioly/docguard@
|
|
47
|
+
uses: raccioly/docguard@v0.25.0
|
|
47
48
|
with:
|
|
48
49
|
command: fix
|
|
49
50
|
auto-commit: 'true'
|
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
fetch-depth: 0
|
|
29
29
|
|
|
30
30
|
- name: Run all validators
|
|
31
|
-
uses: raccioly/docguard@
|
|
31
|
+
uses: raccioly/docguard@v0.25.0
|
|
32
32
|
with:
|
|
33
33
|
command: guard
|
|
34
34
|
# Flip to 'true' once your repo is clean — turns warnings into hard failures.
|
|
@@ -42,7 +42,7 @@ jobs:
|
|
|
42
42
|
- uses: actions/checkout@v4
|
|
43
43
|
|
|
44
44
|
- name: Score & comment
|
|
45
|
-
uses: raccioly/docguard@
|
|
45
|
+
uses: raccioly/docguard@v0.25.0
|
|
46
46
|
with:
|
|
47
47
|
command: score
|
|
48
48
|
format: json
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "docguard-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
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": {
|
|
@@ -45,6 +45,9 @@
|
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=18.0.0"
|
|
47
47
|
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@babel/parser": "7.29.7"
|
|
50
|
+
},
|
|
48
51
|
"files": [
|
|
49
52
|
"cli/",
|
|
50
53
|
"templates/",
|
|
@@ -56,6 +59,5 @@
|
|
|
56
59
|
"PHILOSOPHY.md",
|
|
57
60
|
"README.md",
|
|
58
61
|
"LICENSE"
|
|
59
|
-
]
|
|
60
|
-
"dependencies": {}
|
|
62
|
+
]
|
|
61
63
|
}
|