docguard-cli 0.9.8 → 0.9.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/commands/diagnose.mjs +64 -24
- package/cli/commands/fix.mjs +1 -1
- package/cli/commands/guard.mjs +12 -1
- package/cli/commands/hooks.mjs +2 -2
- package/cli/commands/init.mjs +94 -73
- package/cli/commands/setup.mjs +60 -30
- package/cli/docguard.mjs +5 -5
- package/cli/ensure-skills.mjs +231 -13
- package/cli/scanners/speckit.mjs +1 -1
- package/cli/validators/doc-quality.mjs +1 -1
- package/cli/validators/schema-sync.mjs +1 -1
- package/cli/validators/todo-tracking.mjs +1 -1
- package/extensions/spec-kit-docguard/extension.yml +6 -2
- package/extensions/spec-kit-docguard/skills/docguard-fix/SKILL.md +2 -1
- package/extensions/spec-kit-docguard/skills/docguard-guard/SKILL.md +9 -4
- package/extensions/spec-kit-docguard/skills/docguard-review/SKILL.md +5 -1
- package/extensions/spec-kit-docguard/skills/docguard-score/SKILL.md +2 -1
- package/package.json +1 -1
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { c } from '../shared.mjs';
|
|
17
17
|
import { runGuardInternal } from './guard.mjs';
|
|
18
18
|
import { runScoreInternal } from './score.mjs';
|
|
19
|
+
import { detectAgentMode, isSpecKitInitialized } from '../ensure-skills.mjs';
|
|
19
20
|
import { existsSync, readFileSync } from 'node:fs';
|
|
20
21
|
import { resolve, dirname } from 'node:path';
|
|
21
22
|
import { fileURLToPath } from 'node:url';
|
|
@@ -35,23 +36,26 @@ const VALIDATOR_TO_DOC = {
|
|
|
35
36
|
'Freshness': null, // freshness — maps to stale doc
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
// Actionable fix instructions per validator
|
|
39
|
+
// Actionable fix instructions per validator (LLM-first: includes both skill and CLI commands)
|
|
39
40
|
const FIX_INSTRUCTIONS = {
|
|
40
41
|
'Structure': {
|
|
41
42
|
action: 'Create missing files',
|
|
42
43
|
command: 'docguard init',
|
|
44
|
+
llmCommand: '/docguard.init',
|
|
43
45
|
description: 'Run init to create missing documentation templates.',
|
|
44
46
|
autoFixable: true,
|
|
45
47
|
},
|
|
46
48
|
'Doc Sections': {
|
|
47
49
|
action: 'Fill document sections',
|
|
48
50
|
command: 'docguard fix --doc',
|
|
49
|
-
|
|
51
|
+
llmCommand: '/docguard.fix --doc',
|
|
52
|
+
description: 'Documents exist but have missing or placeholder sections. Use the docguard-fix skill to generate content.',
|
|
50
53
|
autoFixable: false,
|
|
51
54
|
},
|
|
52
55
|
'Docs-Sync': {
|
|
53
56
|
action: 'Sync documentation references',
|
|
54
57
|
command: 'docguard fix --doc architecture',
|
|
58
|
+
llmCommand: '/docguard.fix --doc architecture',
|
|
55
59
|
description: 'Documentation references are out of sync with code. Review and update component maps.',
|
|
56
60
|
autoFixable: false,
|
|
57
61
|
},
|
|
@@ -68,35 +72,44 @@ const FIX_INSTRUCTIONS = {
|
|
|
68
72
|
'Test-Spec': {
|
|
69
73
|
action: 'Update TEST-SPEC.md',
|
|
70
74
|
command: 'docguard fix --doc test-spec',
|
|
75
|
+
llmCommand: '/docguard.fix --doc test-spec',
|
|
71
76
|
description: 'Test documentation needs updating to match actual test structure.',
|
|
72
77
|
autoFixable: false,
|
|
73
78
|
},
|
|
74
79
|
'Environment': {
|
|
75
80
|
action: 'Update ENVIRONMENT.md',
|
|
76
81
|
command: 'docguard fix --doc environment',
|
|
82
|
+
llmCommand: '/docguard.fix --doc environment',
|
|
77
83
|
description: 'Environment documentation is missing or incomplete.',
|
|
78
84
|
autoFixable: false,
|
|
79
85
|
},
|
|
80
86
|
'Security': {
|
|
81
87
|
action: 'Update SECURITY.md',
|
|
82
88
|
command: 'docguard fix --doc security',
|
|
89
|
+
llmCommand: '/docguard.fix --doc security',
|
|
83
90
|
description: 'Security documentation needs updating.',
|
|
84
91
|
autoFixable: false,
|
|
85
92
|
},
|
|
86
93
|
'Architecture': {
|
|
87
94
|
action: 'Update ARCHITECTURE.md',
|
|
88
95
|
command: 'docguard fix --doc architecture',
|
|
96
|
+
llmCommand: '/docguard.fix --doc architecture',
|
|
89
97
|
description: 'Architecture documentation doesn\'t match the codebase.',
|
|
90
98
|
autoFixable: false,
|
|
91
99
|
},
|
|
92
100
|
'Freshness': {
|
|
93
101
|
action: 'Review stale documents',
|
|
102
|
+
command: 'docguard fix --doc',
|
|
103
|
+
llmCommand: '/docguard.fix --doc',
|
|
94
104
|
description: 'Documents haven\'t been reviewed since recent code changes. Re-run fix --doc for each stale doc.',
|
|
95
105
|
autoFixable: false,
|
|
96
106
|
},
|
|
97
107
|
};
|
|
98
108
|
|
|
99
109
|
export function runDiagnose(projectDir, config, flags) {
|
|
110
|
+
// ── Step 0: Detect agent mode (LLM-first) ──
|
|
111
|
+
const agentMode = detectAgentMode(projectDir);
|
|
112
|
+
|
|
100
113
|
// ── Step 1: Run guard internally ──
|
|
101
114
|
let guardData = runGuardInternal(projectDir, config);
|
|
102
115
|
const scoreData = runScoreInternal(projectDir, config);
|
|
@@ -140,10 +153,15 @@ export function runDiagnose(projectDir, config, flags) {
|
|
|
140
153
|
}
|
|
141
154
|
}
|
|
142
155
|
} else if (!shouldAutoFix && (hasStructural || autoFixable.length > 0) && (!flags.format || flags.format === 'text')) {
|
|
143
|
-
// Suggest-only mode: tell user what they can do
|
|
156
|
+
// Suggest-only mode: tell user what they can do (LLM-first)
|
|
144
157
|
console.log(` ${c.yellow}💡 ${autoFixable.length + (hasStructural ? 1 : 0)} issue(s) can be auto-fixed.${c.reset} Run with ${c.cyan}--auto${c.reset} to create/regenerate docs, or manually:`);
|
|
145
|
-
if (
|
|
146
|
-
|
|
158
|
+
if (agentMode === 'llm') {
|
|
159
|
+
if (hasStructural) console.log(` ${c.dim}/docguard.init${c.reset}`);
|
|
160
|
+
if (autoFixable.length > 0) console.log(` ${c.dim}/docguard.fix${c.reset}`);
|
|
161
|
+
} else {
|
|
162
|
+
if (hasStructural) console.log(` ${c.dim}docguard init --dir .${c.reset}`);
|
|
163
|
+
if (autoFixable.length > 0) console.log(` ${c.dim}docguard generate --dir . --force${c.reset}`);
|
|
164
|
+
}
|
|
147
165
|
console.log('');
|
|
148
166
|
}
|
|
149
167
|
}
|
|
@@ -157,7 +175,9 @@ export function runDiagnose(projectDir, config, flags) {
|
|
|
157
175
|
const docMap = { 'architecture': 'architecture', 'data-model': 'data-model', 'security': 'security', 'test-spec': 'test-spec', 'environment': 'environment' };
|
|
158
176
|
issue.docTarget = docMap[docName] || null;
|
|
159
177
|
if (issue.docTarget) {
|
|
160
|
-
issue.command =
|
|
178
|
+
issue.command = agentMode === 'llm'
|
|
179
|
+
? `/docguard.fix --doc ${issue.docTarget}`
|
|
180
|
+
: `docguard fix --doc ${issue.docTarget}`;
|
|
161
181
|
}
|
|
162
182
|
}
|
|
163
183
|
}
|
|
@@ -167,9 +187,9 @@ export function runDiagnose(projectDir, config, flags) {
|
|
|
167
187
|
if (flags.format === 'json') {
|
|
168
188
|
outputJSON(guardData, scoreData, issues);
|
|
169
189
|
} else if (flags.format === 'prompt') {
|
|
170
|
-
outputPrompt(projectDir, guardData, scoreData, issues, flags);
|
|
190
|
+
outputPrompt(projectDir, guardData, scoreData, issues, flags, agentMode);
|
|
171
191
|
} else {
|
|
172
|
-
outputText(projectDir, guardData, scoreData, issues, flags);
|
|
192
|
+
outputText(projectDir, guardData, scoreData, issues, flags, agentMode);
|
|
173
193
|
}
|
|
174
194
|
}
|
|
175
195
|
|
|
@@ -191,6 +211,7 @@ function collectIssues(guardData) {
|
|
|
191
211
|
message: err,
|
|
192
212
|
action: fixInfo.action,
|
|
193
213
|
command: fixInfo.command || null,
|
|
214
|
+
llmCommand: fixInfo.llmCommand || null,
|
|
194
215
|
docTarget,
|
|
195
216
|
autoFixable: fixInfo.autoFixable || false,
|
|
196
217
|
});
|
|
@@ -202,6 +223,7 @@ function collectIssues(guardData) {
|
|
|
202
223
|
message: warn,
|
|
203
224
|
action: fixInfo.action,
|
|
204
225
|
command: fixInfo.command || null,
|
|
226
|
+
llmCommand: fixInfo.llmCommand || null,
|
|
205
227
|
docTarget,
|
|
206
228
|
autoFixable: fixInfo.autoFixable || false,
|
|
207
229
|
});
|
|
@@ -233,14 +255,18 @@ function outputJSON(guardData, scoreData, issues) {
|
|
|
233
255
|
console.log(JSON.stringify(result, null, 2));
|
|
234
256
|
}
|
|
235
257
|
|
|
236
|
-
function outputText(projectDir, guardData, scoreData, issues, flags) {
|
|
258
|
+
function outputText(projectDir, guardData, scoreData, issues, flags, agentMode = 'llm') {
|
|
237
259
|
console.log(`${c.bold}🔍 DocGuard Diagnose — ${guardData.project}${c.reset}`);
|
|
238
|
-
console.log(`${c.dim} Profile: ${guardData.profile} | Score: ${scoreData.score}/100 (${scoreData.grade})${c.reset}`);
|
|
260
|
+
console.log(`${c.dim} Profile: ${guardData.profile} | Score: ${scoreData.score}/100 (${scoreData.grade}) | Mode: ${agentMode.toUpperCase()}${c.reset}`);
|
|
239
261
|
console.log(`${c.dim} Guard: ${guardData.passed}/${guardData.total} passed | Status: ${guardData.status}${c.reset}\n`);
|
|
240
262
|
|
|
241
263
|
if (issues.length === 0) {
|
|
242
264
|
console.log(` ${c.green}${c.bold}✅ All clear!${c.reset} No issues found.\n`);
|
|
243
|
-
|
|
265
|
+
if (agentMode === 'llm') {
|
|
266
|
+
console.log(` ${c.dim}Your documentation is healthy. Use ${c.cyan}/docguard.guard${c.dim} to re-validate after changes.${c.reset}\n`);
|
|
267
|
+
} else {
|
|
268
|
+
console.log(` ${c.dim}Your documentation is healthy. Run \`docguard score --tax\` to see maintenance estimate.${c.reset}\n`);
|
|
269
|
+
}
|
|
244
270
|
return;
|
|
245
271
|
}
|
|
246
272
|
|
|
@@ -252,7 +278,8 @@ function outputText(projectDir, guardData, scoreData, issues, flags) {
|
|
|
252
278
|
console.log(` ${c.red}${c.bold}Errors (${errors.length}):${c.reset}`);
|
|
253
279
|
for (const e of errors) {
|
|
254
280
|
console.log(` ${c.red}✗${c.reset} [${e.validator}] ${e.message}`);
|
|
255
|
-
|
|
281
|
+
const cmd = agentMode === 'llm' && e.llmCommand ? e.llmCommand : e.command;
|
|
282
|
+
if (cmd) console.log(` ${c.dim}Fix: ${cmd}${c.reset}`);
|
|
256
283
|
}
|
|
257
284
|
console.log('');
|
|
258
285
|
}
|
|
@@ -261,19 +288,21 @@ function outputText(projectDir, guardData, scoreData, issues, flags) {
|
|
|
261
288
|
console.log(` ${c.yellow}${c.bold}Warnings (${warnings.length}):${c.reset}`);
|
|
262
289
|
for (const w of warnings) {
|
|
263
290
|
console.log(` ${c.yellow}⚠${c.reset} [${w.validator}] ${w.message}`);
|
|
264
|
-
|
|
291
|
+
const cmd = agentMode === 'llm' && w.llmCommand ? w.llmCommand : w.command;
|
|
292
|
+
if (cmd) console.log(` ${c.dim}Fix: ${cmd}${c.reset}`);
|
|
265
293
|
}
|
|
266
294
|
console.log('');
|
|
267
295
|
}
|
|
268
296
|
|
|
269
|
-
// ── Remediation Plan ──
|
|
297
|
+
// ── Remediation Plan (LLM-first) ──
|
|
270
298
|
const commands = [...new Set(issues.filter(i => i.command).map(i => i.command))];
|
|
271
299
|
if (commands.length > 0) {
|
|
272
300
|
console.log(` ${c.bold}📋 Remediation Plan:${c.reset}`);
|
|
273
301
|
for (let i = 0; i < commands.length; i++) {
|
|
274
302
|
console.log(` ${c.cyan}${i + 1}. ${commands[i]}${c.reset}`);
|
|
275
303
|
}
|
|
276
|
-
|
|
304
|
+
const verifyCmd = agentMode === 'llm' ? '/docguard.guard' : 'docguard guard';
|
|
305
|
+
console.log(` ${c.cyan}${commands.length + 1}. ${verifyCmd}${c.reset} ${c.dim}← verify fixes${c.reset}`);
|
|
277
306
|
console.log('');
|
|
278
307
|
}
|
|
279
308
|
|
|
@@ -282,15 +311,15 @@ function outputText(projectDir, guardData, scoreData, issues, flags) {
|
|
|
282
311
|
// Multi-perspective debate prompts (AITPG/TRACE-inspired)
|
|
283
312
|
console.log(` ${c.bold}🤖 Multi-Perspective AI Debate Prompt:${c.reset}`);
|
|
284
313
|
console.log(` ${c.dim}Copy everything below and paste to your AI agent:${c.reset}\n`);
|
|
285
|
-
outputDebatePrompt(projectDir, guardData, scoreData, issues);
|
|
314
|
+
outputDebatePrompt(projectDir, guardData, scoreData, issues, agentMode);
|
|
286
315
|
} else {
|
|
287
316
|
console.log(` ${c.bold}🤖 AI-Ready Prompt:${c.reset}`);
|
|
288
317
|
console.log(` ${c.dim}Copy everything below and paste to your AI agent:${c.reset}\n`);
|
|
289
|
-
outputPrompt(undefined, guardData, scoreData, issues, flags);
|
|
318
|
+
outputPrompt(undefined, guardData, scoreData, issues, flags, agentMode);
|
|
290
319
|
}
|
|
291
320
|
}
|
|
292
321
|
|
|
293
|
-
function outputPrompt(projectDir, guardData, scoreData, issues, flags) {
|
|
322
|
+
function outputPrompt(projectDir, guardData, scoreData, issues, flags, agentMode = 'llm') {
|
|
294
323
|
if (issues.length === 0) {
|
|
295
324
|
console.log('No issues to fix. Documentation is healthy.');
|
|
296
325
|
return;
|
|
@@ -345,7 +374,11 @@ function outputPrompt(projectDir, guardData, scoreData, issues, flags) {
|
|
|
345
374
|
|
|
346
375
|
lines.push('');
|
|
347
376
|
lines.push('VALIDATION:');
|
|
348
|
-
|
|
377
|
+
if (agentMode === 'llm') {
|
|
378
|
+
lines.push('After making all fixes, use the /docguard.guard skill to verify');
|
|
379
|
+
} else {
|
|
380
|
+
lines.push('After making all fixes, run: docguard guard');
|
|
381
|
+
}
|
|
349
382
|
lines.push('Expected result: All checks pass (0 errors, 0 warnings)');
|
|
350
383
|
lines.push(`Target score: ≥${Math.min(scoreData.score + 5, 100)}/100`);
|
|
351
384
|
|
|
@@ -354,9 +387,15 @@ function outputPrompt(projectDir, guardData, scoreData, issues, flags) {
|
|
|
354
387
|
lines.push('');
|
|
355
388
|
lines.push('VERIFICATION CHECKLIST (complete each step):');
|
|
356
389
|
lines.push('□ Read each file in docs-canonical/ before editing');
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
390
|
+
if (agentMode === 'llm') {
|
|
391
|
+
lines.push('□ Run /docguard.guard after each file change');
|
|
392
|
+
lines.push('□ Confirm 0 errors before moving to next issue');
|
|
393
|
+
lines.push('□ Run /docguard.score to confirm improvement');
|
|
394
|
+
} else {
|
|
395
|
+
lines.push('□ Run `docguard guard` after each file change');
|
|
396
|
+
lines.push('□ Confirm 0 errors before moving to next issue');
|
|
397
|
+
lines.push('□ Run `docguard score` to confirm improvement');
|
|
398
|
+
}
|
|
360
399
|
}
|
|
361
400
|
|
|
362
401
|
console.log(lines.join('\n'));
|
|
@@ -368,7 +407,7 @@ function outputPrompt(projectDir, guardData, scoreData, issues, flags) {
|
|
|
368
407
|
* and TRACE adversarial debate (Advocate/Challenger/Mediator/Explainer).
|
|
369
408
|
* Lopez et al., IEEE TSE/TMLCN 2026.
|
|
370
409
|
*/
|
|
371
|
-
function outputDebatePrompt(projectDir, guardData, scoreData, issues) {
|
|
410
|
+
function outputDebatePrompt(projectDir, guardData, scoreData, issues, agentMode = 'llm') {
|
|
372
411
|
const lines = [];
|
|
373
412
|
|
|
374
413
|
lines.push('═══════════════════════════════════════════════════════');
|
|
@@ -427,7 +466,8 @@ function outputDebatePrompt(projectDir, guardData, scoreData, issues) {
|
|
|
427
466
|
lines.push(' a. Which file to edit');
|
|
428
467
|
lines.push(' b. What section to add or modify');
|
|
429
468
|
lines.push(' c. What content to write (be specific, not vague)');
|
|
430
|
-
|
|
469
|
+
const verifyCmd = agentMode === 'llm' ? '/docguard.guard' : 'docguard guard';
|
|
470
|
+
lines.push(`4. After all fixes, verify with: ${verifyCmd}`);
|
|
431
471
|
lines.push(`5. Target score: ≥${Math.min(scoreData.score + 10, 100)}/100`);
|
|
432
472
|
lines.push('');
|
|
433
473
|
lines.push('═══════════════════════════════════════════════════════');
|
package/cli/commands/fix.mjs
CHANGED
|
@@ -453,7 +453,7 @@ function generateDocPrompt(projectDir, config, docName) {
|
|
|
453
453
|
|
|
454
454
|
console.log(expectations.aiResearchInstructions.trim());
|
|
455
455
|
|
|
456
|
-
console.log(`\nVALIDATION: After writing, run \`npx docguard guard\` to verify the document passes all checks.`);
|
|
456
|
+
console.log(`\nVALIDATION: After writing, run \`npx docguard-cli guard\` to verify the document passes all checks.`);
|
|
457
457
|
console.log(`The document should have NO <!-- TODO --> or <!-- e.g. --> placeholders.`);
|
|
458
458
|
console.log(`Set the docguard:status header to 'active' (not 'draft').`);
|
|
459
459
|
}
|
package/cli/commands/guard.mjs
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { c } from '../shared.mjs';
|
|
11
|
+
import { detectAgentMode, isSpecKitInitialized } from '../ensure-skills.mjs';
|
|
11
12
|
import { validateStructure, validateDocSections } from '../validators/structure.mjs';
|
|
12
13
|
import { validateDrift } from '../validators/drift.mjs';
|
|
13
14
|
import { validateChangelog } from '../validators/changelog.mjs';
|
|
@@ -197,7 +198,12 @@ export function runGuard(projectDir, config, flags) {
|
|
|
197
198
|
|
|
198
199
|
// Next step hint — always point to diagnose when issues exist
|
|
199
200
|
if (data.status !== 'PASS') {
|
|
200
|
-
|
|
201
|
+
const agentMode = detectAgentMode(projectDir);
|
|
202
|
+
if (agentMode === 'llm') {
|
|
203
|
+
console.log(` ${c.dim}Use ${c.cyan}/docguard.diagnose${c.dim} to get AI fix prompts.${c.reset}`);
|
|
204
|
+
} else {
|
|
205
|
+
console.log(` ${c.dim}Run ${c.cyan}docguard diagnose${c.dim} to get AI fix prompts.${c.reset}`);
|
|
206
|
+
}
|
|
201
207
|
}
|
|
202
208
|
|
|
203
209
|
// Badge snippet
|
|
@@ -206,6 +212,11 @@ export function runGuard(projectDir, config, flags) {
|
|
|
206
212
|
const badgeUrl = `https://img.shields.io/badge/CDD_Guard-${data.passed}%2F${data.total}_passed-${bColor}`;
|
|
207
213
|
console.log(`\n ${c.dim}📎 Badge: ${c.reset}`);
|
|
208
214
|
|
|
215
|
+
// Spec-kit reminder — persistent nudge if not initialized
|
|
216
|
+
if (!isSpecKitInitialized(projectDir)) {
|
|
217
|
+
console.log(`\n ${c.yellow}💡${c.reset} ${c.dim}Enhance DocGuard with Spec Kit: ${c.cyan}uv tool install specify-cli --from git+https://github.com/github/spec-kit.git${c.reset}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
209
220
|
console.log('');
|
|
210
221
|
|
|
211
222
|
if (data.errors > 0) process.exit(1);
|
package/cli/commands/hooks.mjs
CHANGED
|
@@ -20,7 +20,7 @@ echo "🛡️ Running DocGuard guard..."
|
|
|
20
20
|
|
|
21
21
|
# Check if docguard is available
|
|
22
22
|
if command -v npx &> /dev/null; then
|
|
23
|
-
npx docguard guard
|
|
23
|
+
npx docguard-cli guard
|
|
24
24
|
EXIT_CODE=$?
|
|
25
25
|
elif command -v docguard &> /dev/null; then
|
|
26
26
|
docguard guard
|
|
@@ -60,7 +60,7 @@ echo "📊 Running DocGuard score check (minimum: $MIN_SCORE)..."
|
|
|
60
60
|
|
|
61
61
|
# Get score as JSON
|
|
62
62
|
if command -v npx &> /dev/null; then
|
|
63
|
-
RESULT=$(npx docguard score --format json 2>/dev/null)
|
|
63
|
+
RESULT=$(npx docguard-cli score --format json 2>/dev/null)
|
|
64
64
|
elif command -v docguard &> /dev/null; then
|
|
65
65
|
RESULT=$(docguard score --format json 2>/dev/null)
|
|
66
66
|
else
|
package/cli/commands/init.mjs
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Init Command — Initialize CDD documentation from templates
|
|
3
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Extension-First: When `specify` CLI is available, DocGuard delegates
|
|
5
|
+
* LLM/IDE detection and spec-kit skill installation to spec-kit.
|
|
6
|
+
* DocGuard then layers CDD-specific docs on top.
|
|
7
|
+
*
|
|
8
|
+
* Fallback: When `specify` is not available, DocGuard runs standalone
|
|
9
|
+
* with a warning suggesting spec-kit installation.
|
|
4
10
|
*/
|
|
5
11
|
|
|
6
12
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
7
13
|
import { resolve, dirname } from 'node:path';
|
|
8
14
|
import { fileURLToPath } from 'node:url';
|
|
9
15
|
import { createInterface } from 'node:readline';
|
|
16
|
+
import { execSync } from 'node:child_process';
|
|
10
17
|
import { c, PROFILES } from '../shared.mjs';
|
|
11
|
-
import { ensureSkills } from '../ensure-skills.mjs';
|
|
18
|
+
import { ensureSkills, detectAgentMode, detectAIAgent, isSpecKitAvailable, isSpecKitInitialized, getDetectedAgent } from '../ensure-skills.mjs';
|
|
12
19
|
|
|
13
20
|
function detectProjectType(dir) {
|
|
14
21
|
const pkgPath = resolve(dir, 'package.json');
|
|
@@ -189,61 +196,57 @@ export async function runInit(projectDir, config, flags) {
|
|
|
189
196
|
console.log(` ${c.yellow}⏭️${c.reset} .docguard.json ${c.dim}(already exists)${c.reset}`);
|
|
190
197
|
}
|
|
191
198
|
|
|
192
|
-
// ──
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const agentDirs = [
|
|
198
|
-
{ name: 'GitHub Copilot', path: '.github/commands' },
|
|
199
|
-
{ name: 'Cursor', path: '.cursor/rules' },
|
|
200
|
-
{ name: 'Google Gemini', path: '.gemini/commands' },
|
|
201
|
-
{ name: 'Claude Code', path: '.claude/commands' },
|
|
202
|
-
{ name: 'Antigravity', path: '.agents/workflows' },
|
|
203
|
-
];
|
|
204
|
-
|
|
205
|
-
const detected = agentDirs.filter(a =>
|
|
206
|
-
existsSync(resolve(projectDir, a.path.split('/')[0]))
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
const targets = detected.length > 0
|
|
210
|
-
? detected
|
|
211
|
-
: [{ name: 'GitHub (default)', path: '.github/commands' }];
|
|
212
|
-
|
|
213
|
-
let totalCreated = 0;
|
|
214
|
-
const installedLocations = [];
|
|
215
|
-
|
|
216
|
-
for (const target of targets) {
|
|
217
|
-
const destDir = resolve(projectDir, target.path);
|
|
218
|
-
if (!existsSync(destDir)) {
|
|
219
|
-
mkdirSync(destDir, { recursive: true });
|
|
220
|
-
}
|
|
199
|
+
// ── Spec-Kit Integration (Extension-First) ────────────────────────────
|
|
200
|
+
// Delegate LLM/IDE detection and spec-kit skill install to `specify init`
|
|
201
|
+
const specKitAvailable = isSpecKitAvailable();
|
|
202
|
+
const specKitInitialized = isSpecKitInitialized(projectDir);
|
|
221
203
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const destPath = resolve(destDir, file);
|
|
225
|
-
if (!existsSync(destPath)) {
|
|
226
|
-
const content = readFileSync(resolve(commandsSourceDir, file), 'utf-8');
|
|
227
|
-
writeFileSync(destPath, content, 'utf-8');
|
|
228
|
-
dirCreated++;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
204
|
+
if (specKitAvailable && !specKitInitialized) {
|
|
205
|
+
console.log(`\n ${c.bold}🌱 Spec Kit Integration${c.reset}`);
|
|
231
206
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
207
|
+
// Detect which AI agent is in use (matches spec-kit's --ai flag)
|
|
208
|
+
const detectedAgent = detectAIAgent(projectDir);
|
|
209
|
+
const aiFlag = detectedAgent
|
|
210
|
+
? `--ai ${detectedAgent}`
|
|
211
|
+
: '--ai generic --ai-commands-dir .agent/commands/';
|
|
237
212
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
console.log(` ${c.
|
|
213
|
+
console.log(` ${c.dim}Running specify init (agent: ${detectedAgent || 'generic'})...${c.reset}`);
|
|
214
|
+
try {
|
|
215
|
+
const scriptFlag = process.platform === 'win32' ? '--script ps' : '--script sh';
|
|
216
|
+
execSync(
|
|
217
|
+
`specify init --here --force ${aiFlag} --ai-skills --ignore-agent-tools --no-git ${scriptFlag}`,
|
|
218
|
+
{ cwd: projectDir, encoding: 'utf-8', stdio: 'pipe', timeout: 30000 }
|
|
219
|
+
);
|
|
220
|
+
console.log(` ${c.green}✅${c.reset} Spec Kit initialized ${c.dim}(.specify/, spec-kit skills, agent: ${detectedAgent || 'generic'})${c.reset}`);
|
|
221
|
+
created.push('.specify/ (spec-kit foundation)');
|
|
222
|
+
} catch (err) {
|
|
223
|
+
console.log(` ${c.yellow}⚠️${c.reset} Spec Kit init had issues ${c.dim}(continuing with DocGuard standalone)${c.reset}`);
|
|
224
|
+
if (flags.debug) console.log(` ${c.dim}${err.message}${c.reset}`);
|
|
246
225
|
}
|
|
226
|
+
} else if (specKitInitialized) {
|
|
227
|
+
const agent = getDetectedAgent(projectDir);
|
|
228
|
+
console.log(`\n ${c.green}✅${c.reset} Spec Kit already initialized${agent ? ` ${c.dim}(agent: ${agent})${c.reset}` : ''}`);
|
|
229
|
+
} else {
|
|
230
|
+
console.log(`\n ${c.red}┌─────────────────────────────────────────────────────────────┐${c.reset}`);
|
|
231
|
+
console.log(` ${c.red}│${c.reset} ${c.bold}⚠️ Spec Kit not installed — running in standalone mode${c.reset} ${c.red}│${c.reset}`);
|
|
232
|
+
console.log(` ${c.red}│${c.reset} ${c.red}│${c.reset}`);
|
|
233
|
+
console.log(` ${c.red}│${c.reset} DocGuard is designed as a Spec Kit extension. ${c.red}│${c.reset}`);
|
|
234
|
+
console.log(` ${c.red}│${c.reset} Without Spec Kit, you get ${c.bold}4 skills${c.reset}. With it: ${c.bold}13 skills${c.reset}. ${c.red}│${c.reset}`);
|
|
235
|
+
console.log(` ${c.red}│${c.reset} ${c.red}│${c.reset}`);
|
|
236
|
+
console.log(` ${c.red}│${c.reset} ${c.bold}What you're missing:${c.reset} ${c.red}│${c.reset}`);
|
|
237
|
+
console.log(` ${c.red}│${c.reset} • 9 Spec Kit AI skills (specify, plan, tasks, implement) ${c.red}│${c.reset}`);
|
|
238
|
+
console.log(` ${c.red}│${c.reset} • Project constitution (${c.cyan}constitution.md${c.reset}) ${c.red}│${c.reset}`);
|
|
239
|
+
console.log(` ${c.red}│${c.reset} • Full SDD + CDD integrated workflow ${c.red}│${c.reset}`);
|
|
240
|
+
console.log(` ${c.red}│${c.reset} • AI agent auto-detection and config ${c.red}│${c.reset}`);
|
|
241
|
+
console.log(` ${c.red}│${c.reset} ${c.red}│${c.reset}`);
|
|
242
|
+
console.log(` ${c.red}│${c.reset} ${c.bold}Install with:${c.reset} ${c.red}│${c.reset}`);
|
|
243
|
+
console.log(` ${c.red}│${c.reset} ${c.cyan}uv tool install specify-cli \\${c.reset} ${c.red}│${c.reset}`);
|
|
244
|
+
console.log(` ${c.red}│${c.reset} ${c.cyan} --from git+https://github.com/github/spec-kit.git${c.reset} ${c.red}│${c.reset}`);
|
|
245
|
+
console.log(` ${c.red}│${c.reset} ${c.red}│${c.reset}`);
|
|
246
|
+
console.log(` ${c.red}│${c.reset} ${c.dim}Alternative: ${c.cyan}pip install specify-cli${c.reset} ${c.red}│${c.reset}`);
|
|
247
|
+
console.log(` ${c.red}│${c.reset} ${c.red}│${c.reset}`);
|
|
248
|
+
console.log(` ${c.red}│${c.reset} ${c.dim}Then re-run: ${c.cyan}docguard init${c.reset} ${c.red}│${c.reset}`);
|
|
249
|
+
console.log(` ${c.red}└─────────────────────────────────────────────────────────────┘${c.reset}`);
|
|
247
250
|
}
|
|
248
251
|
|
|
249
252
|
// ── Summary ────────────────────────────────────────────────────────────
|
|
@@ -258,35 +261,53 @@ export async function runInit(projectDir, config, flags) {
|
|
|
258
261
|
console.log(` ${c.dim}Auto-guard on commit:${c.reset} ${c.cyan}docguard hooks --type pre-commit${c.reset}`);
|
|
259
262
|
console.log(` ${c.dim}Auto-guard on push:${c.reset} ${c.cyan}docguard hooks --type pre-push${c.reset}`);
|
|
260
263
|
|
|
261
|
-
// ── Next
|
|
264
|
+
// ── Next Steps (LLM-First) ─────────────────────────────────────────────
|
|
265
|
+
const agentMode = detectAgentMode(projectDir);
|
|
262
266
|
const createdDocs = created.filter(f => f.startsWith('docs-canonical/'));
|
|
263
267
|
|
|
264
268
|
if (createdDocs.length > 0) {
|
|
265
|
-
console.log(`\n ${c.bold}🤖
|
|
269
|
+
console.log(`\n ${c.bold}🤖 Next Steps${c.reset} ${c.dim}(${agentMode === 'llm' ? 'LLM mode' : 'CLI mode'})${c.reset}`);
|
|
266
270
|
console.log(` ${c.dim}The files above are skeleton templates. Your AI agent should fill them.${c.reset}`);
|
|
267
|
-
console.log(` ${c.dim}Run this single command to get a full remediation plan:${c.reset}\n`);
|
|
268
|
-
console.log(` ${c.cyan}${c.bold}docguard diagnose${c.reset}\n`);
|
|
269
|
-
console.log(` ${c.dim}Or generate prompts for individual docs:${c.reset}`);
|
|
270
|
-
|
|
271
|
-
const docNameMap = {
|
|
272
|
-
'docs-canonical/ARCHITECTURE.md': 'architecture',
|
|
273
|
-
'docs-canonical/DATA-MODEL.md': 'data-model',
|
|
274
|
-
'docs-canonical/SECURITY.md': 'security',
|
|
275
|
-
'docs-canonical/TEST-SPEC.md': 'test-spec',
|
|
276
|
-
'docs-canonical/ENVIRONMENT.md': 'environment',
|
|
277
|
-
};
|
|
278
271
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
272
|
+
if (agentMode === 'llm') {
|
|
273
|
+
// LLM-first: show skill commands
|
|
274
|
+
console.log(`\n ${c.bold}Use these skills in your AI agent:${c.reset}`);
|
|
275
|
+
if (isSpecKitInitialized(projectDir)) {
|
|
276
|
+
console.log(` ${c.cyan}1. /speckit.constitution${c.reset} ${c.dim}← establish project principles${c.reset}`);
|
|
277
|
+
}
|
|
278
|
+
console.log(` ${c.cyan}${isSpecKitInitialized(projectDir) ? '2' : '1'}. /docguard.guard${c.reset} ${c.dim}← validate documentation${c.reset}`);
|
|
279
|
+
|
|
280
|
+
const docNameMap = {
|
|
281
|
+
'docs-canonical/ARCHITECTURE.md': 'architecture',
|
|
282
|
+
'docs-canonical/DATA-MODEL.md': 'data-model',
|
|
283
|
+
'docs-canonical/SECURITY.md': 'security',
|
|
284
|
+
'docs-canonical/TEST-SPEC.md': 'test-spec',
|
|
285
|
+
'docs-canonical/ENVIRONMENT.md': 'environment',
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const fixTargets = createdDocs.map(d => docNameMap[d]).filter(Boolean);
|
|
289
|
+
if (fixTargets.length > 0) {
|
|
290
|
+
console.log(`\n ${c.dim}Fix individual docs with the docguard-fix skill:${c.reset}`);
|
|
291
|
+
for (const target of fixTargets) {
|
|
292
|
+
console.log(` ${c.cyan}/docguard.fix --doc ${target}${c.reset}`);
|
|
293
|
+
}
|
|
283
294
|
}
|
|
295
|
+
console.log(`\n ${c.dim}Then verify:${c.reset} ${c.cyan}/docguard.guard${c.reset}`);
|
|
296
|
+
} else {
|
|
297
|
+
// CLI fallback
|
|
298
|
+
console.log(`\n ${c.dim}Get a full remediation plan:${c.reset}`);
|
|
299
|
+
console.log(` ${c.cyan}${c.bold}docguard diagnose${c.reset}\n`);
|
|
300
|
+
console.log(` ${c.dim}Then verify:${c.reset} ${c.cyan}docguard guard${c.reset}`);
|
|
284
301
|
}
|
|
285
|
-
console.log(
|
|
302
|
+
console.log('');
|
|
286
303
|
} else {
|
|
287
|
-
|
|
304
|
+
if (agentMode === 'llm') {
|
|
305
|
+
console.log(`\n ${c.dim}Use${c.reset} ${c.cyan}/docguard.guard${c.reset} ${c.dim}in your AI agent to check for issues.${c.reset}\n`);
|
|
306
|
+
} else {
|
|
307
|
+
console.log(`\n ${c.dim}Run${c.reset} ${c.cyan}docguard diagnose${c.reset} ${c.dim}to check for issues.${c.reset}\n`);
|
|
308
|
+
}
|
|
288
309
|
}
|
|
289
310
|
|
|
290
|
-
// Auto-install skills and commands
|
|
311
|
+
// Auto-install DocGuard skills and commands (spec-kit skills handled by specify init)
|
|
291
312
|
ensureSkills(projectDir, flags);
|
|
292
313
|
}
|
package/cli/commands/setup.mjs
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
* Each step shows current status (✅/⚠️) and offers to fix what's missing.
|
|
14
14
|
* Supports --skip-prompts for non-interactive CI mode.
|
|
15
15
|
*
|
|
16
|
-
* Zero dependencies — pure Node.js built-ins only.
|
|
16
|
+
* Zero NPM runtime dependencies — pure Node.js built-ins only.
|
|
17
|
+
* Framework dependency: spec-kit (convention, not code).
|
|
17
18
|
*/
|
|
18
19
|
|
|
19
20
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
@@ -22,7 +23,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
22
23
|
import { createInterface } from 'node:readline';
|
|
23
24
|
import { execSync } from 'node:child_process';
|
|
24
25
|
import { c } from '../shared.mjs';
|
|
25
|
-
import { ensureSkills } from '../ensure-skills.mjs';
|
|
26
|
+
import { ensureSkills, detectAgentMode, isSpecKitInitialized, getDetectedAgent } from '../ensure-skills.mjs';
|
|
26
27
|
|
|
27
28
|
const __filename = fileURLToPath(import.meta.url);
|
|
28
29
|
const __dirname = dirname(__filename);
|
|
@@ -211,11 +212,17 @@ export async function runSetup(projectDir, config, flags) {
|
|
|
211
212
|
|
|
212
213
|
console.log(` ${c.bold}Step 3/7: AI Skills${c.reset}`);
|
|
213
214
|
|
|
214
|
-
const
|
|
215
|
+
const docguardSkills = ['docguard-guard', 'docguard-fix', 'docguard-review', 'docguard-score'];
|
|
216
|
+
const speckitSkills = [
|
|
217
|
+
'speckit-specify', 'speckit-plan', 'speckit-tasks', 'speckit-implement',
|
|
218
|
+
'speckit-analyze', 'speckit-clarify', 'speckit-checklist', 'speckit-constitution',
|
|
219
|
+
'speckit-taskstoissues',
|
|
220
|
+
];
|
|
221
|
+
const allSkillNames = [...docguardSkills, ...speckitSkills];
|
|
215
222
|
const skillsDest = resolve(projectDir, '.agent/skills');
|
|
216
223
|
let missingSkills = [];
|
|
217
224
|
|
|
218
|
-
for (const skill of
|
|
225
|
+
for (const skill of allSkillNames) {
|
|
219
226
|
const skillPath = resolve(skillsDest, skill, 'SKILL.md');
|
|
220
227
|
if (existsSync(skillPath)) {
|
|
221
228
|
console.log(` ${c.green}✅${c.reset} ${skill}`);
|
|
@@ -227,19 +234,28 @@ export async function runSetup(projectDir, config, flags) {
|
|
|
227
234
|
}
|
|
228
235
|
|
|
229
236
|
if (missingSkills.length > 0) {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
: true;
|
|
237
|
+
const hasSpeckitMissing = missingSkills.some(s => s.startsWith('speckit-'));
|
|
238
|
+
const hasDocguardMissing = missingSkills.some(s => s.startsWith('docguard-'));
|
|
233
239
|
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
240
|
+
if (hasSpeckitMissing) {
|
|
241
|
+
console.log(` ${c.dim} Spec-kit skills installed via: ${c.cyan}specify init --here --force --ai-skills${c.reset}`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (hasDocguardMissing) {
|
|
245
|
+
const install = interactive
|
|
246
|
+
? await askYesNo(` → Install ${missingSkills.filter(s => s.startsWith('docguard-')).length} DocGuard skill(s) to .agent/skills/?`)
|
|
247
|
+
: true;
|
|
248
|
+
|
|
249
|
+
if (install) {
|
|
250
|
+
for (const skill of missingSkills.filter(s => s.startsWith('docguard-'))) {
|
|
251
|
+
const srcSkill = resolve(SKILLS_SOURCE, skill, 'SKILL.md');
|
|
252
|
+
const destDir = resolve(skillsDest, skill);
|
|
253
|
+
if (existsSync(srcSkill)) {
|
|
254
|
+
if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
|
|
255
|
+
writeFileSync(resolve(destDir, 'SKILL.md'), readFileSync(srcSkill, 'utf-8'), 'utf-8');
|
|
256
|
+
console.log(` ${c.green}✅ Installed ${skill}${c.reset}`);
|
|
257
|
+
configured++;
|
|
258
|
+
}
|
|
243
259
|
}
|
|
244
260
|
}
|
|
245
261
|
}
|
|
@@ -354,16 +370,20 @@ export async function runSetup(projectDir, config, flags) {
|
|
|
354
370
|
|
|
355
371
|
console.log(` ${c.bold}Step 6/7: Integrations${c.reset}`);
|
|
356
372
|
|
|
357
|
-
// Check spec-kit framework
|
|
358
|
-
const
|
|
359
|
-
const
|
|
360
|
-
if (
|
|
361
|
-
|
|
373
|
+
// Check spec-kit framework (Extension-First: detect .specify/ directory)
|
|
374
|
+
const specKitInitialized = isSpecKitInitialized(projectDir);
|
|
375
|
+
const specifyDir = resolve(projectDir, '.specify');
|
|
376
|
+
if (specKitInitialized) {
|
|
377
|
+
const agent = getDetectedAgent(projectDir);
|
|
378
|
+
console.log(` ${c.green}✅${c.reset} spec-kit ${c.dim}(SDD configured${agent ? `, agent: ${agent}` : ''})${c.reset}`);
|
|
362
379
|
alreadyGood++;
|
|
380
|
+
} else if (existsSync(specifyDir)) {
|
|
381
|
+
console.log(` ${c.yellow}⚠️${c.reset} spec-kit ${c.dim}(.specify/ exists but not fully initialized)${c.reset}`);
|
|
382
|
+
console.log(` ${c.dim} Run: ${c.cyan}specify init --here --force --ai-skills${c.reset}`);
|
|
363
383
|
} else {
|
|
364
|
-
console.log(` ${c.dim}──${c.reset} spec-kit ${c.dim}(not configured —
|
|
365
|
-
console.log(` ${c.dim}
|
|
366
|
-
console.log(` ${c.dim}
|
|
384
|
+
console.log(` ${c.dim}──${c.reset} spec-kit ${c.dim}(not configured — recommended for full SDD+CDD workflow)${c.reset}`);
|
|
385
|
+
console.log(` ${c.dim} Install: ${c.cyan}uv tool install specify-cli --from git+https://github.com/github/spec-kit.git${c.reset}`);
|
|
386
|
+
console.log(` ${c.dim} Then: ${c.cyan}docguard init${c.reset} ${c.dim}(will auto-run specify init)${c.reset}`);
|
|
367
387
|
}
|
|
368
388
|
|
|
369
389
|
// Check for spec-kit extensions
|
|
@@ -421,8 +441,8 @@ export async function runSetup(projectDir, config, flags) {
|
|
|
421
441
|
if (!existsSync(hooksDir)) mkdirSync(hooksDir, { recursive: true });
|
|
422
442
|
|
|
423
443
|
const hookContent = existsSync(preCommitHook)
|
|
424
|
-
? readFileSync(preCommitHook, 'utf-8') + '\n\n# DocGuard CDD validation\nnpx docguard guard --fail-on-warning\n'
|
|
425
|
-
: '#!/bin/sh\n\n# DocGuard CDD validation\nnpx docguard guard --fail-on-warning\n';
|
|
444
|
+
? readFileSync(preCommitHook, 'utf-8') + '\n\n# DocGuard CDD validation\nnpx docguard-cli guard --fail-on-warning\n'
|
|
445
|
+
: '#!/bin/sh\n\n# DocGuard CDD validation\nnpx docguard-cli guard --fail-on-warning\n';
|
|
426
446
|
|
|
427
447
|
writeFileSync(preCommitHook, hookContent, { mode: 0o755 });
|
|
428
448
|
console.log(` ${c.green}✅ Pre-commit hook installed${c.reset}`);
|
|
@@ -447,9 +467,19 @@ export async function runSetup(projectDir, config, flags) {
|
|
|
447
467
|
console.log(` ${c.dim}No changes made.${c.reset}`);
|
|
448
468
|
}
|
|
449
469
|
|
|
450
|
-
|
|
451
|
-
console.log(
|
|
452
|
-
|
|
453
|
-
|
|
470
|
+
const agentMode = detectAgentMode(projectDir);
|
|
471
|
+
console.log(`\n ${c.bold}Next steps:${c.reset} ${c.dim}(${agentMode === 'llm' ? 'LLM mode' : 'CLI mode'})${c.reset}`);
|
|
472
|
+
if (agentMode === 'llm') {
|
|
473
|
+
if (!isSpecKitInitialized(projectDir)) {
|
|
474
|
+
console.log(` ${c.dim}Bootstrap:${c.reset} ${c.cyan}/speckit.constitution${c.reset}`);
|
|
475
|
+
}
|
|
476
|
+
console.log(` ${c.dim}Fill docs:${c.reset} ${c.cyan}/docguard.guard${c.reset}`);
|
|
477
|
+
console.log(` ${c.dim}Fix issues:${c.reset} ${c.cyan}/docguard.fix${c.reset}`);
|
|
478
|
+
console.log(` ${c.dim}Review:${c.reset} ${c.cyan}/docguard.review${c.reset}`);
|
|
479
|
+
} else {
|
|
480
|
+
console.log(` ${c.dim}Fill docs:${c.reset} ${c.cyan}docguard diagnose${c.reset}`);
|
|
481
|
+
console.log(` ${c.dim}Validate:${c.reset} ${c.cyan}docguard guard${c.reset}`);
|
|
482
|
+
console.log(` ${c.dim}Check score:${c.reset} ${c.cyan}docguard score${c.reset}`);
|
|
483
|
+
}
|
|
454
484
|
console.log('');
|
|
455
485
|
}
|
package/cli/docguard.mjs
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* DocGuard CLI — The enforcement tool for Canonical-Driven Development (CDD)
|
|
5
5
|
*
|
|
6
|
-
* Zero dependencies. Pure Node.js.
|
|
6
|
+
* Zero NPM runtime dependencies. Pure Node.js.
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
|
-
* npx docguard audit — Scan project, report what docs exist/missing
|
|
10
|
-
* npx docguard init — Initialize CDD docs from templates
|
|
11
|
-
* npx docguard guard — Validate project against its canonical docs
|
|
12
|
-
* npx docguard --help — Show help
|
|
9
|
+
* npx docguard-cli audit — Scan project, report what docs exist/missing
|
|
10
|
+
* npx docguard-cli init — Initialize CDD docs from templates
|
|
11
|
+
* npx docguard-cli guard — Validate project against its canonical docs
|
|
12
|
+
* npx docguard-cli --help — Show help
|
|
13
13
|
*
|
|
14
14
|
* @see https://github.com/raccioly/docguard
|
|
15
15
|
*/
|
package/cli/ensure-skills.mjs
CHANGED
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
* Called before every command execution. If skills or commands are missing,
|
|
5
5
|
* copies them from the package's bundled assets into the project directory.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* Also provides agent mode detection (LLM vs CLI) and spec-kit availability.
|
|
8
|
+
*
|
|
9
|
+
* Zero npm dependencies — pure Node.js built-ins only.
|
|
10
|
+
* Framework dependency: spec-kit (convention, not code).
|
|
8
11
|
*/
|
|
9
12
|
|
|
10
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync
|
|
11
|
-
import { resolve, dirname
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
14
|
+
import { resolve, dirname } from 'node:path';
|
|
12
15
|
import { fileURLToPath } from 'node:url';
|
|
16
|
+
import { execSync } from 'node:child_process';
|
|
13
17
|
import { c } from './shared.mjs';
|
|
14
18
|
|
|
15
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -23,23 +27,224 @@ const COMMANDS_SOURCE = resolve(__dirname, '..', 'commands');
|
|
|
23
27
|
const SKILLS_DEST = '.agent/skills';
|
|
24
28
|
const COMMANDS_DEST = 'commands';
|
|
25
29
|
|
|
30
|
+
// ── Agent Mode Detection ────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Detect if user is in LLM mode (AI agent) or CLI mode (terminal).
|
|
34
|
+
* DocGuard is LLM-first — defaults to 'llm' when any agent signal detected.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} projectDir - The project root directory
|
|
37
|
+
* @returns {'llm' | 'cli'}
|
|
38
|
+
*/
|
|
39
|
+
export function detectAgentMode(projectDir) {
|
|
40
|
+
// First check .specify/init-options.json for explicit AI agent selection
|
|
41
|
+
const initOptions = resolve(projectDir, '.specify', 'init-options.json');
|
|
42
|
+
if (existsSync(initOptions)) {
|
|
43
|
+
try {
|
|
44
|
+
const opts = JSON.parse(readFileSync(initOptions, 'utf-8'));
|
|
45
|
+
if (opts.ai) return 'llm'; // spec-kit was initialized with an AI agent
|
|
46
|
+
} catch { /* ignore */ }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check for LLM signal directories/files
|
|
50
|
+
const llmSignals = [
|
|
51
|
+
'.agent/skills',
|
|
52
|
+
'.cursor',
|
|
53
|
+
'.claude',
|
|
54
|
+
'.specify',
|
|
55
|
+
'.github/copilot-instructions.md',
|
|
56
|
+
'CLAUDE.md',
|
|
57
|
+
'.gemini',
|
|
58
|
+
'.agents',
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
for (const signal of llmSignals) {
|
|
62
|
+
if (existsSync(resolve(projectDir, signal))) return 'llm';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return 'cli';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get the detected AI agent name from spec-kit init options.
|
|
70
|
+
* Returns null if spec-kit hasn't been initialized.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} projectDir - The project root directory
|
|
73
|
+
* @returns {string | null}
|
|
74
|
+
*/
|
|
75
|
+
export function getDetectedAgent(projectDir) {
|
|
76
|
+
const initOptions = resolve(projectDir, '.specify', 'init-options.json');
|
|
77
|
+
if (existsSync(initOptions)) {
|
|
78
|
+
try {
|
|
79
|
+
const opts = JSON.parse(readFileSync(initOptions, 'utf-8'));
|
|
80
|
+
return opts.ai || null;
|
|
81
|
+
} catch { /* ignore */ }
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Detect which AI agent is in use, returning the spec-kit --ai flag value.
|
|
88
|
+
* Matches spec-kit's supported agents: agy, claude, copilot, cursor-agent,
|
|
89
|
+
* gemini, windsurf, codex, roo, etc.
|
|
90
|
+
*
|
|
91
|
+
* Priority: .specify/init-options.json > filesystem signals > null
|
|
92
|
+
*
|
|
93
|
+
* @param {string} projectDir - The project root directory
|
|
94
|
+
* @returns {string | null} - spec-kit --ai flag value, or null if unknown
|
|
95
|
+
*/
|
|
96
|
+
export function detectAIAgent(projectDir) {
|
|
97
|
+
// 1. Check spec-kit init options (already initialized — trust it)
|
|
98
|
+
const existing = getDetectedAgent(projectDir);
|
|
99
|
+
if (existing) return existing;
|
|
100
|
+
|
|
101
|
+
// 2. Map filesystem signals to spec-kit agent IDs
|
|
102
|
+
// Order matters: more specific signals first
|
|
103
|
+
const agentSignals = [
|
|
104
|
+
{ signal: '.cursor', agent: 'cursor-agent' },
|
|
105
|
+
{ signal: '.claude', agent: 'claude' },
|
|
106
|
+
{ signal: 'CLAUDE.md', agent: 'claude' },
|
|
107
|
+
{ signal: '.gemini', agent: 'gemini' },
|
|
108
|
+
{ signal: '.agents', agent: 'agy' }, // Antigravity
|
|
109
|
+
{ signal: '.github/copilot-instructions.md', agent: 'copilot' },
|
|
110
|
+
{ signal: '.windsurf', agent: 'windsurf' },
|
|
111
|
+
{ signal: '.codex', agent: 'codex' },
|
|
112
|
+
{ signal: '.roo', agent: 'roo' },
|
|
113
|
+
{ signal: '.amp', agent: 'amp' },
|
|
114
|
+
{ signal: '.kiro', agent: 'kiro-cli' },
|
|
115
|
+
{ signal: '.tabnine', agent: 'tabnine' },
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
for (const { signal, agent } of agentSignals) {
|
|
119
|
+
if (existsSync(resolve(projectDir, signal))) return agent;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 3. No signal found — return null (caller decides: interactive vs generic)
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check if the specify CLI (spec-kit) is available on PATH.
|
|
128
|
+
*
|
|
129
|
+
* @returns {boolean}
|
|
130
|
+
*/
|
|
131
|
+
export function isSpecKitAvailable() {
|
|
132
|
+
try {
|
|
133
|
+
const cmd = process.platform === 'win32' ? 'where specify' : 'which specify';
|
|
134
|
+
execSync(cmd, { encoding: 'utf-8', stdio: 'pipe', timeout: 3000 });
|
|
135
|
+
return true;
|
|
136
|
+
} catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
26
141
|
/**
|
|
27
|
-
*
|
|
142
|
+
* Check if spec-kit has been initialized in this project.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} projectDir - The project root directory
|
|
145
|
+
* @returns {boolean}
|
|
146
|
+
*/
|
|
147
|
+
export function isSpecKitInitialized(projectDir) {
|
|
148
|
+
return existsSync(resolve(projectDir, '.specify', 'init-options.json'));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ── Spec-Kit Integration Gate ───────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
// Read DocGuard package version (for skill auto-update)
|
|
154
|
+
const PKG_VERSION = (() => {
|
|
155
|
+
try {
|
|
156
|
+
const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'));
|
|
157
|
+
return pkg.version || '0.0.0';
|
|
158
|
+
} catch { return '0.0.0'; }
|
|
159
|
+
})();
|
|
160
|
+
|
|
161
|
+
const SPEC_KIT_INSTALL_CMD = 'uv tool install specify-cli --from git+https://github.com/github/spec-kit.git';
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Ensure spec-kit is initialized in the project.
|
|
165
|
+
* Called on every command run — this is the persistent nudge.
|
|
166
|
+
*
|
|
167
|
+
* - If .specify/ exists → do nothing
|
|
168
|
+
* - If specify CLI available → auto-run specify init with detected agent
|
|
169
|
+
* - If specify CLI not available → show prominent install reminder (every time)
|
|
170
|
+
*
|
|
171
|
+
* @param {string} projectDir - The project root directory
|
|
172
|
+
* @param {object} flags - CLI flags
|
|
173
|
+
* @returns {{ specKitReady: boolean }}
|
|
174
|
+
*/
|
|
175
|
+
export function ensureSpecKit(projectDir, flags = {}) {
|
|
176
|
+
const silent = flags.format === 'json';
|
|
177
|
+
|
|
178
|
+
// Already initialized — nothing to do
|
|
179
|
+
if (isSpecKitInitialized(projectDir)) {
|
|
180
|
+
return { specKitReady: true };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Spec-kit CLI available — auto-initialize
|
|
184
|
+
if (isSpecKitAvailable()) {
|
|
185
|
+
if (!silent) {
|
|
186
|
+
console.log(` ${c.cyan}🌱 Spec Kit detected — auto-initializing SDD workflow...${c.reset}`);
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const detectedAgent = detectAIAgent(projectDir);
|
|
190
|
+
const aiFlag = detectedAgent
|
|
191
|
+
? `--ai ${detectedAgent}`
|
|
192
|
+
: '--ai generic --ai-commands-dir .agent/commands/';
|
|
193
|
+
const scriptFlag = process.platform === 'win32' ? '--script ps' : '--script sh';
|
|
194
|
+
execSync(
|
|
195
|
+
`specify init --here --force ${aiFlag} --ai-skills --ignore-agent-tools --no-git ${scriptFlag}`,
|
|
196
|
+
{ cwd: projectDir, encoding: 'utf-8', stdio: 'pipe', timeout: 30000 }
|
|
197
|
+
);
|
|
198
|
+
if (!silent) {
|
|
199
|
+
console.log(` ${c.green}✅ Spec Kit initialized${c.reset} ${c.dim}(agent: ${detectedAgent || 'generic'}, 9 skills installed)${c.reset}\n`);
|
|
200
|
+
}
|
|
201
|
+
return { specKitReady: true };
|
|
202
|
+
} catch {
|
|
203
|
+
// Failed silently — will show reminder instead
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// No specify CLI — show prominent reminder (every time, no dismiss)
|
|
208
|
+
if (!silent) {
|
|
209
|
+
console.log(` ${c.yellow}┌─────────────────────────────────────────────────────────┐${c.reset}`);
|
|
210
|
+
console.log(` ${c.yellow}│${c.reset} ${c.bold}💡 Spec Kit not installed${c.reset} ${c.yellow}│${c.reset}`);
|
|
211
|
+
console.log(` ${c.yellow}│${c.reset} ${c.yellow}│${c.reset}`);
|
|
212
|
+
console.log(` ${c.yellow}│${c.reset} DocGuard is a Spec Kit extension. Install Spec Kit ${c.yellow}│${c.reset}`);
|
|
213
|
+
console.log(` ${c.yellow}│${c.reset} for the full experience: 13 AI skills, SDD workflow, ${c.yellow}│${c.reset}`);
|
|
214
|
+
console.log(` ${c.yellow}│${c.reset} project constitution, and seamless agent integration. ${c.yellow}│${c.reset}`);
|
|
215
|
+
console.log(` ${c.yellow}│${c.reset} ${c.yellow}│${c.reset}`);
|
|
216
|
+
console.log(` ${c.yellow}│${c.reset} ${c.cyan}${SPEC_KIT_INSTALL_CMD}${c.reset}`);
|
|
217
|
+
console.log(` ${c.yellow}│${c.reset} ${c.yellow}│${c.reset}`);
|
|
218
|
+
console.log(` ${c.yellow}│${c.reset} ${c.dim}Then run: ${c.cyan}docguard init${c.reset} ${c.yellow}│${c.reset}`);
|
|
219
|
+
console.log(` ${c.yellow}└─────────────────────────────────────────────────────────┘${c.reset}\n`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return { specKitReady: false };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ── Skill Installation ──────────────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Silently ensure DocGuard skills and commands are installed in the project.
|
|
229
|
+
* Also checks spec-kit integration and auto-updates stale skills.
|
|
28
230
|
*
|
|
29
231
|
* @param {string} projectDir - The project root directory
|
|
30
232
|
* @param {object} flags - CLI flags (format, etc.)
|
|
31
|
-
* @returns {{ skillsInstalled: boolean, commandsInstalled: boolean }}
|
|
233
|
+
* @returns {{ skillsInstalled: boolean, commandsInstalled: boolean, specKitReady: boolean }}
|
|
32
234
|
*/
|
|
33
235
|
export function ensureSkills(projectDir, flags = {}) {
|
|
34
|
-
const result = { skillsInstalled: false, commandsInstalled: false };
|
|
236
|
+
const result = { skillsInstalled: false, commandsInstalled: false, specKitReady: false };
|
|
35
237
|
const silent = flags.format === 'json';
|
|
36
238
|
|
|
37
|
-
// ──
|
|
38
|
-
const
|
|
39
|
-
|
|
239
|
+
// ── Spec-Kit Gate (runs on every command) ─────────────────────────────
|
|
240
|
+
const specKitResult = ensureSpecKit(projectDir, flags);
|
|
241
|
+
result.specKitReady = specKitResult.specKitReady;
|
|
242
|
+
|
|
243
|
+
// ── DocGuard Skills (install + auto-update) ───────────────────────────
|
|
244
|
+
if (existsSync(SKILLS_SOURCE)) {
|
|
40
245
|
try {
|
|
41
246
|
const skillDirs = readdirSync(SKILLS_SOURCE).filter(d =>
|
|
42
|
-
existsSync(resolve(SKILLS_SOURCE, d, 'SKILL.md'))
|
|
247
|
+
d.startsWith('docguard-') && existsSync(resolve(SKILLS_SOURCE, d, 'SKILL.md'))
|
|
43
248
|
);
|
|
44
249
|
|
|
45
250
|
for (const skillDir of skillDirs) {
|
|
@@ -49,14 +254,26 @@ export function ensureSkills(projectDir, flags = {}) {
|
|
|
49
254
|
}
|
|
50
255
|
const srcSkill = resolve(SKILLS_SOURCE, skillDir, 'SKILL.md');
|
|
51
256
|
const destSkill = resolve(destDir, 'SKILL.md');
|
|
257
|
+
|
|
52
258
|
if (!existsSync(destSkill)) {
|
|
259
|
+
// New install
|
|
53
260
|
writeFileSync(destSkill, readFileSync(srcSkill, 'utf-8'), 'utf-8');
|
|
261
|
+
result.skillsInstalled = true;
|
|
262
|
+
} else {
|
|
263
|
+
// Auto-update: check if package version is newer than installed
|
|
264
|
+
const installedContent = readFileSync(destSkill, 'utf-8');
|
|
265
|
+
const versionMatch = installedContent.match(/docguard:version:\s*(\S+)/);
|
|
266
|
+
const installedVersion = versionMatch ? versionMatch[1] : '0.0.0';
|
|
267
|
+
|
|
268
|
+
if (installedVersion !== PKG_VERSION) {
|
|
269
|
+
writeFileSync(destSkill, readFileSync(srcSkill, 'utf-8'), 'utf-8');
|
|
270
|
+
result.skillsInstalled = true;
|
|
271
|
+
}
|
|
54
272
|
}
|
|
55
273
|
}
|
|
56
274
|
|
|
57
|
-
result.skillsInstalled
|
|
58
|
-
|
|
59
|
-
console.log(` ${c.cyan}✨ DocGuard AI skills installed → ${SKILLS_DEST}/${c.reset}`);
|
|
275
|
+
if (result.skillsInstalled && !silent) {
|
|
276
|
+
console.log(` ${c.cyan}✨ DocGuard AI skills installed/updated → ${SKILLS_DEST}/${c.reset}`);
|
|
60
277
|
}
|
|
61
278
|
} catch {
|
|
62
279
|
// Silent failure — skills are optional enhancement
|
|
@@ -94,3 +311,4 @@ export function ensureSkills(projectDir, flags = {}) {
|
|
|
94
311
|
|
|
95
312
|
return result;
|
|
96
313
|
}
|
|
314
|
+
|
package/cli/scanners/speckit.mjs
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* Credit: Integration with GitHub's Spec Kit framework
|
|
20
20
|
* (github.com/github/spec-kit)
|
|
21
21
|
*
|
|
22
|
-
* Zero dependencies — pure Node.js built-ins only.
|
|
22
|
+
* Zero NPM runtime dependencies — pure Node.js built-ins only.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { existsSync, readFileSync, readdirSync, statSync, copyFileSync, writeFileSync } from 'node:fs';
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*
|
|
19
19
|
* Optional: If `understanding` CLI is installed, runs a full 31-metric deep scan.
|
|
20
20
|
*
|
|
21
|
-
* Zero dependencies — pure Node.js built-ins only.
|
|
21
|
+
* Zero NPM runtime dependencies — pure Node.js built-ins only.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Supported: Prisma, Drizzle, Sequelize, TypeORM, Knex, Django, Rails
|
|
8
8
|
*
|
|
9
|
-
* Zero dependencies — pure Node.js built-ins only.
|
|
9
|
+
* Zero NPM runtime dependencies — pure Node.js built-ins only.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Inspired by spec-kit-cleanup (github.com/dsrednicki/spec-kit-cleanup)
|
|
10
10
|
* which uses tiered issue classification for code hygiene.
|
|
11
11
|
*
|
|
12
|
-
* Zero dependencies — pure Node.js built-ins only.
|
|
12
|
+
* Zero NPM runtime dependencies — pure Node.js built-ins only.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
@@ -3,8 +3,8 @@ schema_version: "1.0"
|
|
|
3
3
|
extension:
|
|
4
4
|
id: "docguard"
|
|
5
5
|
name: "DocGuard — CDD Enforcement"
|
|
6
|
-
version: "0.9.
|
|
7
|
-
description: "Canonical-Driven Development enforcement
|
|
6
|
+
version: "0.9.9"
|
|
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"
|
|
10
10
|
license: "MIT"
|
|
@@ -12,12 +12,16 @@ extension:
|
|
|
12
12
|
|
|
13
13
|
requires:
|
|
14
14
|
speckit_version: ">=0.1.0"
|
|
15
|
+
framework: "spec-kit" # DocGuard builds on spec-kit conventions (.specify/, skills, constitution)
|
|
15
16
|
tools:
|
|
16
17
|
- name: "node"
|
|
17
18
|
required: true
|
|
18
19
|
version: ">=18.0.0"
|
|
19
20
|
- name: "npx"
|
|
20
21
|
required: true
|
|
22
|
+
- name: "specify"
|
|
23
|
+
required: false
|
|
24
|
+
description: "Spec Kit CLI — enables auto-initialization of SDD workflow during docguard init"
|
|
21
25
|
|
|
22
26
|
provides:
|
|
23
27
|
commands:
|
|
@@ -6,9 +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.
|
|
9
|
+
version: 0.9.9
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-fix
|
|
11
11
|
---
|
|
12
|
+
<!-- docguard:version: 0.9.9 -->
|
|
12
13
|
|
|
13
14
|
# DocGuard Fix Skill
|
|
14
15
|
|
|
@@ -7,9 +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.9.
|
|
10
|
+
version: 0.9.9
|
|
11
11
|
source: extensions/spec-kit-docguard/skills/docguard-guard
|
|
12
12
|
---
|
|
13
|
+
<!-- docguard:version: 0.9.9 -->
|
|
13
14
|
|
|
14
15
|
# DocGuard Guard Skill
|
|
15
16
|
|
|
@@ -155,12 +156,16 @@ Present the user with options:
|
|
|
155
156
|
- **Track progress** — if user runs guard multiple times, compare before/after
|
|
156
157
|
- If user provides `$ARGUMENTS` like "just structure" or "only security", filter report to those validators
|
|
157
158
|
|
|
158
|
-
## Integration with Spec Kit
|
|
159
|
+
## Integration with Spec Kit (Extension-First)
|
|
159
160
|
|
|
160
|
-
|
|
161
|
+
DocGuard is a spec-kit extension. When this project has a `.specify/` directory:
|
|
162
|
+
- Read `.specify/memory/constitution.md` for project principles that constrain documentation
|
|
161
163
|
- Include Spec-Kit validator results in the triage
|
|
162
164
|
- Cross-reference spec quality issues with `specs/*/spec.md` file paths
|
|
163
|
-
-
|
|
165
|
+
- When specification issues found → suggest `/speckit.specify` or `/speckit.clarify`
|
|
166
|
+
- When architecture gaps found → suggest `/speckit.plan`
|
|
167
|
+
- When cross-artifact inconsistencies exceed 3 → suggest `/speckit.analyze`
|
|
168
|
+
- When no constitution exists → suggest `/speckit.constitution` as first step
|
|
164
169
|
|
|
165
170
|
## Context
|
|
166
171
|
|
|
@@ -6,9 +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.
|
|
9
|
+
version: 0.9.9
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-review
|
|
11
11
|
---
|
|
12
|
+
<!-- docguard:version: 0.9.9 -->
|
|
12
13
|
|
|
13
14
|
# DocGuard Review Skill
|
|
14
15
|
|
|
@@ -163,7 +164,10 @@ Output a structured markdown report (do NOT write to disk):
|
|
|
163
164
|
|
|
164
165
|
Based on findings:
|
|
165
166
|
- **If CRITICAL issues**: "Run `/docguard.fix --doc [name]` to resolve blocking issues"
|
|
167
|
+
- **If spec-related gaps**: "Run `/speckit.specify` to update specifications" or "/speckit.clarify to resolve ambiguities"
|
|
168
|
+
- **If architecture drift**: "Run `/speckit.plan` to realign implementation plan with codebase"
|
|
166
169
|
- **If only LOW/MEDIUM**: "Documentation is healthy. Consider `/docguard.fix` for polish"
|
|
170
|
+
- **If constitution missing**: "Run `/speckit.constitution` to establish project principles"
|
|
167
171
|
- **If all clean**: "Documentation is excellent. No action needed."
|
|
168
172
|
|
|
169
173
|
Ask: "Would you like me to fix the top N issues? (I'll show you what I plan to change before applying)"
|
|
@@ -6,9 +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.
|
|
9
|
+
version: 0.9.9
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-score
|
|
11
11
|
---
|
|
12
|
+
<!-- docguard:version: 0.9.9 -->
|
|
12
13
|
|
|
13
14
|
# DocGuard Score Skill
|
|
14
15
|
|