docguard-cli 0.28.0 → 0.29.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.es.md +102 -0
- package/README.md +64 -31
- package/README.pt-BR.md +101 -0
- package/STANDARD.md +20 -10
- package/cli/commands/agents.mjs +149 -0
- package/cli/commands/diff.mjs +6 -15
- package/cli/commands/generate.mjs +14 -1001
- package/cli/commands/guard.mjs +136 -8
- package/cli/commands/llms.mjs +67 -5
- package/cli/commands/mcp.mjs +263 -0
- package/cli/commands/memory.mjs +115 -0
- package/cli/commands/score.mjs +76 -12
- package/cli/docguard.mjs +31 -2
- package/cli/findings.mjs +499 -0
- package/cli/scanners/agent-readability.mjs +202 -0
- package/cli/scanners/semantic-claims.mjs +7 -1
- package/cli/scanners/speckit.mjs +98 -28
- package/cli/shared-ignore.mjs +148 -16
- package/cli/shared.mjs +45 -1
- package/cli/validators/api-surface.mjs +113 -26
- package/cli/validators/architecture.mjs +66 -43
- package/cli/validators/canonical-sync.mjs +59 -28
- package/cli/validators/changelog.mjs +41 -17
- package/cli/validators/cross-reference.mjs +28 -11
- package/cli/validators/doc-quality.mjs +78 -44
- package/cli/validators/docs-coverage.mjs +90 -63
- package/cli/validators/docs-diff.mjs +63 -64
- package/cli/validators/docs-sync.mjs +48 -33
- package/cli/validators/drift.mjs +40 -34
- package/cli/validators/environment.mjs +67 -27
- package/cli/validators/freshness.mjs +12 -5
- package/cli/validators/generated-staleness.mjs +26 -10
- package/cli/validators/metadata-sync.mjs +28 -25
- package/cli/validators/metrics-consistency.mjs +89 -47
- package/cli/validators/schema-sync.mjs +37 -32
- package/cli/validators/security.mjs +7 -20
- package/cli/validators/spec-kit.mjs +3 -0
- package/cli/validators/structure.mjs +58 -23
- package/cli/validators/surface-sync.mjs +34 -15
- package/cli/validators/test-spec.mjs +87 -29
- package/cli/validators/todo-tracking.mjs +83 -74
- package/cli/validators/traceability.mjs +67 -39
- package/cli/writers/doc-generators.mjs +853 -0
- package/cli/writers/generate-io.mjs +142 -0
- package/cli/writers/sarif.mjs +129 -0
- package/commands/docguard.fix.md +56 -53
- package/commands/docguard.guard.md +53 -47
- package/commands/docguard.review.md +49 -31
- package/docs/ai-integration.md +133 -134
- package/docs/commands.md +49 -3
- package/docs/configuration.md +38 -0
- package/docs/faq.md +15 -0
- package/extensions/spec-kit-docguard/extension.yml +1 -1
- 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 -2
- package/package.json +1 -1
- package/schemas/docguard-config.schema.json +17 -0
- package/templates/commands/docguard.fix.md +33 -10
- package/templates/commands/docguard.guard.md +40 -26
- package/templates/commands/docguard.init.md +23 -11
- package/templates/commands/docguard.review.md +25 -8
- package/templates/commands/docguard.update.md +14 -4
package/cli/commands/memory.mjs
CHANGED
|
@@ -21,8 +21,12 @@
|
|
|
21
21
|
* Zero NPM dependencies. Pure orchestration of existing diff helpers.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import { existsSync, readFileSync, readdirSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
25
|
+
import { resolve } from 'node:path';
|
|
24
26
|
import { c } from '../shared.mjs';
|
|
25
27
|
import { diffRoutes, diffEntities, diffEnvVars, diffTechStack } from './diff.mjs';
|
|
28
|
+
import { buildMemoryPlan } from '../scanners/memory-plan.mjs';
|
|
29
|
+
import { runGuardInternal } from './guard.mjs';
|
|
26
30
|
|
|
27
31
|
/**
|
|
28
32
|
* Compute an accuracy score for a single domain. Returns:
|
|
@@ -47,7 +51,118 @@ function _domainAccuracy(d) {
|
|
|
47
51
|
};
|
|
48
52
|
}
|
|
49
53
|
|
|
54
|
+
// ── Context pack (v0.29) ─────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
/** H2 sections of AGENTS.md whose heading reads like rules/conventions/workflow. */
|
|
57
|
+
function extractConventions(agentsMd, capLines = 60) {
|
|
58
|
+
const out = [];
|
|
59
|
+
const lines = agentsMd.split('\n');
|
|
60
|
+
let taking = false;
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
const h2 = line.match(/^##\s+(.+)$/);
|
|
63
|
+
if (h2) taking = /rules|conventions|workflow/i.test(h2[1]);
|
|
64
|
+
if (taking) {
|
|
65
|
+
out.push(line);
|
|
66
|
+
if (out.length >= capLines) {
|
|
67
|
+
out.push('<!-- truncated — read AGENTS.md for the full rules -->');
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* `docguard memory --pack` — write .docguard/context-pack.md: a compact,
|
|
77
|
+
* code-truth-stamped session-start context for an AI agent. Everything in it
|
|
78
|
+
* is derived from scanners (buildMemoryPlan) and guard — numbers, not prose —
|
|
79
|
+
* so it can't hallucinate and is always regenerable.
|
|
80
|
+
*/
|
|
81
|
+
function runMemoryPack(projectDir, config, flags) {
|
|
82
|
+
const plan = buildMemoryPlan(projectDir, config);
|
|
83
|
+
const guard = runGuardInternal(projectDir, config);
|
|
84
|
+
const lines = [];
|
|
85
|
+
|
|
86
|
+
lines.push(`# Context Pack — ${config.projectName}`);
|
|
87
|
+
lines.push('');
|
|
88
|
+
lines.push(`<!-- Generated by \`docguard memory --pack\` ${new Date().toISOString()} — regenerate, don't edit -->`);
|
|
89
|
+
lines.push('');
|
|
90
|
+
lines.push(`**Guard:** ${guard.status} — ${guard.passed}/${guard.total} checks (${guard.errors} error(s), ${guard.warnings} warning(s))`);
|
|
91
|
+
lines.push('');
|
|
92
|
+
|
|
93
|
+
lines.push('## Code-truth surface');
|
|
94
|
+
lines.push('');
|
|
95
|
+
lines.push(`- Stack: ${plan.profile.languages.join(', ') || 'unknown'}${plan.profile.frameworks.length ? ` · ${plan.profile.frameworks.join(', ')}` : ''} · kind: ${plan.profile.kind}`);
|
|
96
|
+
lines.push(`- Modules: ${plan.surface.modules.length} · Endpoints: ${plan.surface.endpoints.length} · Entities: ${plan.surface.entities.length} · Env vars: ${plan.surface.envVars.length}`);
|
|
97
|
+
lines.push(`- Tests: ${plan.surface.tests.totalFiles} files, ${plan.surface.tests.totalCases} cases`);
|
|
98
|
+
lines.push('');
|
|
99
|
+
|
|
100
|
+
const docsDir = resolve(projectDir, 'docs-canonical');
|
|
101
|
+
if (existsSync(docsDir)) {
|
|
102
|
+
lines.push('## Canonical docs');
|
|
103
|
+
lines.push('');
|
|
104
|
+
let entries = [];
|
|
105
|
+
try { entries = readdirSync(docsDir).filter(f => f.endsWith('.md')).sort(); } catch { /* ignore */ }
|
|
106
|
+
for (const doc of entries) {
|
|
107
|
+
let reviewed = '';
|
|
108
|
+
try {
|
|
109
|
+
const m = readFileSync(resolve(docsDir, doc), 'utf-8').match(/docguard:last-reviewed\s+(\d{4}-\d{2}-\d{2})/);
|
|
110
|
+
if (m) reviewed = ` (last-reviewed ${m[1]})`;
|
|
111
|
+
} catch { /* ignore */ }
|
|
112
|
+
lines.push(`- docs-canonical/${doc}${reviewed}`);
|
|
113
|
+
}
|
|
114
|
+
lines.push('');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const agentsPath = resolve(projectDir, 'AGENTS.md');
|
|
118
|
+
if (existsSync(agentsPath)) {
|
|
119
|
+
let conventions = [];
|
|
120
|
+
try { conventions = extractConventions(readFileSync(agentsPath, 'utf-8')); } catch { /* ignore */ }
|
|
121
|
+
if (conventions.length > 0) {
|
|
122
|
+
lines.push('## Project rules (from AGENTS.md)');
|
|
123
|
+
lines.push('');
|
|
124
|
+
lines.push(...conventions);
|
|
125
|
+
lines.push('');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const driftPath = resolve(projectDir, 'DRIFT-LOG.md');
|
|
130
|
+
if (existsSync(driftPath)) {
|
|
131
|
+
try {
|
|
132
|
+
const drift = readFileSync(driftPath, 'utf-8');
|
|
133
|
+
const entries = drift.match(/^##\s+.+$/gm) || [];
|
|
134
|
+
if (entries.length > 0) {
|
|
135
|
+
lines.push('## Known drift');
|
|
136
|
+
lines.push('');
|
|
137
|
+
lines.push(`- ${entries.length} logged deviation(s); latest: ${entries[entries.length - 1].replace(/^##\s+/, '')}`);
|
|
138
|
+
lines.push('');
|
|
139
|
+
}
|
|
140
|
+
} catch { /* ignore */ }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
lines.push('---');
|
|
144
|
+
lines.push('Verify claims: `docguard verify --semantic` · Full docs: `llms-full.txt`');
|
|
145
|
+
lines.push('');
|
|
146
|
+
const content = lines.join('\n');
|
|
147
|
+
|
|
148
|
+
if (flags.stdout) {
|
|
149
|
+
console.log(content);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const outDir = resolve(projectDir, '.docguard');
|
|
153
|
+
if (!existsSync(outDir)) mkdirSync(outDir, { recursive: true });
|
|
154
|
+
const outPath = resolve(outDir, 'context-pack.md');
|
|
155
|
+
writeFileSync(outPath, content, 'utf-8');
|
|
156
|
+
console.log(`${c.bold}🧠 DocGuard Context Pack${c.reset}`);
|
|
157
|
+
console.log(`${c.green}✅ Wrote ${outPath}${c.reset} ${c.dim}(${lines.length} lines — load at agent session start)${c.reset}`);
|
|
158
|
+
console.log('');
|
|
159
|
+
}
|
|
160
|
+
|
|
50
161
|
export function runMemory(projectDir, config, flags) {
|
|
162
|
+
// v0.29: --pack writes the agent context pack and exits — a separate output
|
|
163
|
+
// artifact, not a display mode of the accuracy drill-down below.
|
|
164
|
+
if (flags.pack) return runMemoryPack(projectDir, config, flags);
|
|
165
|
+
|
|
51
166
|
const isJson = flags.format === 'json';
|
|
52
167
|
const wantsDiff = flags.diff || (flags.args || []).includes('--diff');
|
|
53
168
|
|
package/cli/commands/score.mjs
CHANGED
|
@@ -9,6 +9,8 @@ import { execSync } from 'node:child_process';
|
|
|
9
9
|
import { c, docHasSection } from '../shared.mjs';
|
|
10
10
|
import { validateSecurity } from '../validators/security.mjs';
|
|
11
11
|
import { runGuardInternal } from './guard.mjs';
|
|
12
|
+
import { extractSemanticClaims } from '../scanners/semantic-claims.mjs';
|
|
13
|
+
import { assessAgentReadability } from '../scanners/agent-readability.mjs';
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* Detect whether the project configures a test runner (the "Check 3" of the
|
|
@@ -299,11 +301,17 @@ export function runScore(projectDir, config, flags) {
|
|
|
299
301
|
console.log(` ${c.dim}─────────────────────────────────${c.reset}`);
|
|
300
302
|
|
|
301
303
|
for (const attr of alcoa.attributes) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
304
|
+
// `unverified` is a third state: not a green pass, but not a red gap either —
|
|
305
|
+
// "checked the structure, can't confirm the facts." Render it neutrally (🔍,
|
|
306
|
+
// cyan) so it reads as a to-do, not a failure.
|
|
307
|
+
const unverified = attr.status === 'unverified';
|
|
308
|
+
const icon = attr.met ? `${c.green}✅` : unverified ? `${c.cyan}🔍` : `${c.yellow}⚠️`;
|
|
309
|
+
const tone = attr.met ? c.green : unverified ? c.cyan : c.yellow;
|
|
310
|
+
const body = attr.met ? attr.evidence : attr.gap;
|
|
311
|
+
console.log(` ${icon} ${attr.name.padEnd(16)}${c.reset} — ${tone}${body}${c.reset}`);
|
|
305
312
|
if (!attr.met && attr.fix) {
|
|
306
|
-
|
|
313
|
+
const verb = unverified ? 'Verify' : 'Fix';
|
|
314
|
+
console.log(` ${c.dim} ${verb}: ${attr.fix}${c.reset}`);
|
|
307
315
|
}
|
|
308
316
|
}
|
|
309
317
|
|
|
@@ -314,6 +322,22 @@ export function runScore(projectDir, config, flags) {
|
|
|
314
322
|
}
|
|
315
323
|
console.log('');
|
|
316
324
|
|
|
325
|
+
// ── Agent Readability (v0.29) ──
|
|
326
|
+
// Display-only, like ALCOA+ — never feeds the gating CDD grade. Answers the
|
|
327
|
+
// 2026 question: can an AI consumer FIND, QUOTE, and TRUST these docs?
|
|
328
|
+
const agentRead = assessAgentReadability(projectDir, config);
|
|
329
|
+
console.log(` ${c.bold}🤖 Agent Readability${c.reset} ${c.dim}(how well AI consumers can read this repo)${c.reset}`);
|
|
330
|
+
console.log(` ${c.dim}─────────────────────────────────${c.reset}`);
|
|
331
|
+
for (const m of agentRead.metrics) {
|
|
332
|
+
const icon = m.score >= 60 ? `${c.green}✅` : `${c.yellow}⚠️`;
|
|
333
|
+
const tone = m.score >= 60 ? c.green : c.yellow;
|
|
334
|
+
console.log(` ${icon} ${m.label.padEnd(28)}${c.reset} — ${tone}${m.detail}${c.reset}`);
|
|
335
|
+
if (m.fix) console.log(` ${c.dim} Fix: ${m.fix}${c.reset}`);
|
|
336
|
+
}
|
|
337
|
+
const arColor = agentRead.score >= 75 ? c.green : agentRead.score >= 40 ? c.yellow : c.red;
|
|
338
|
+
console.log(`\n ${arColor}${c.bold}Agent Readability: ${agentRead.score}% (${agentRead.grade})${c.reset}`);
|
|
339
|
+
console.log('');
|
|
340
|
+
|
|
317
341
|
// Badge snippet
|
|
318
342
|
const bColor = totalScore >= 90 ? 'brightgreen' : totalScore >= 80 ? 'green' : totalScore >= 70 ? 'yellowgreen' : totalScore >= 60 ? 'yellow' : totalScore >= 50 ? 'orange' : 'red';
|
|
319
343
|
const badgeUrl = `https://img.shields.io/badge/CDD_Score-${totalScore}%2F100_(${grade})-${bColor}`;
|
|
@@ -411,14 +435,54 @@ function computeAlcoaCompliance(projectDir, config, scores) {
|
|
|
411
435
|
});
|
|
412
436
|
|
|
413
437
|
// 5. Accurate — Do docs match the code?
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
438
|
+
//
|
|
439
|
+
// Field report #6: this attribute used to read `met` purely from structural
|
|
440
|
+
// signals (drift markers + prose quality). That let it show ✅ "100%" while a
|
|
441
|
+
// watched doc stated a factually wrong number — the confidence-inverting false
|
|
442
|
+
// negative the field report is about. Structure passing is necessary but NOT
|
|
443
|
+
// sufficient for "accurate"; the factual claims (counts/limits/enums) have to be
|
|
444
|
+
// verified against code, and DocGuard's deterministic core can't do that — only
|
|
445
|
+
// an agent via `verify --semantic` can. So we add a third, honest state:
|
|
446
|
+
// met — structure sound AND no unverified factual claims exist
|
|
447
|
+
// unverified — structure sound BUT documented claims remain unchecked vs code
|
|
448
|
+
// unmet — structural drift/quality below bar
|
|
449
|
+
// `unverified` counts as not-met for the ALCOA percentage (so the score stops
|
|
450
|
+
// overclaiming), but renders as a neutral 🔍 (not a ⚠️ failure) — "I haven't
|
|
451
|
+
// confirmed this," not "this is wrong." This is display-only: it never touches
|
|
452
|
+
// the gating CDD grade (totalScore), which CI thresholds read.
|
|
453
|
+
const structurallyAccurate = scores.drift >= 80 && scores.docQuality >= 50;
|
|
454
|
+
let unverifiedClaims = 0;
|
|
455
|
+
if (structurallyAccurate) {
|
|
456
|
+
try { unverifiedClaims = extractSemanticClaims(projectDir, config).length; } catch { /* extractor best-effort */ }
|
|
457
|
+
}
|
|
458
|
+
if (!structurallyAccurate) {
|
|
459
|
+
attributes.push({
|
|
460
|
+
name: 'Accurate',
|
|
461
|
+
met: false,
|
|
462
|
+
status: 'unmet',
|
|
463
|
+
evidence: null,
|
|
464
|
+
gap: `Drift: ${scores.drift}%, doc quality: ${scores.docQuality}% — docs may be inaccurate`,
|
|
465
|
+
fix: 'Run docguard diagnose to find doc/code mismatches',
|
|
466
|
+
});
|
|
467
|
+
} else if (unverifiedClaims > 0) {
|
|
468
|
+
attributes.push({
|
|
469
|
+
name: 'Accurate',
|
|
470
|
+
met: false,
|
|
471
|
+
status: 'unverified',
|
|
472
|
+
evidence: null,
|
|
473
|
+
gap: `Structure sound (drift ${scores.drift}%, quality ${scores.docQuality}%), but ${unverifiedClaims} documented claim(s) (counts/limits/enums) are unverified against code`,
|
|
474
|
+
fix: 'Run docguard verify --semantic to check the documented values against the code',
|
|
475
|
+
});
|
|
476
|
+
} else {
|
|
477
|
+
attributes.push({
|
|
478
|
+
name: 'Accurate',
|
|
479
|
+
met: true,
|
|
480
|
+
status: 'met',
|
|
481
|
+
evidence: `Drift: ${scores.drift}%, doc quality: ${scores.docQuality}%, no unverified factual claims`,
|
|
482
|
+
gap: null,
|
|
483
|
+
fix: null,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
422
486
|
|
|
423
487
|
// 6. Complete — Are all required docs present?
|
|
424
488
|
const complete = scores.structure >= 80;
|
package/cli/docguard.mjs
CHANGED
|
@@ -48,6 +48,7 @@ import { runVerify } from './commands/verify.mjs';
|
|
|
48
48
|
import { runMemory } from './commands/memory.mjs';
|
|
49
49
|
import { runDemo } from './commands/demo.mjs';
|
|
50
50
|
import { runAgent } from './commands/agent.mjs';
|
|
51
|
+
import { runMcp } from './commands/mcp.mjs';
|
|
51
52
|
import { ensureSkills } from './ensure-skills.mjs';
|
|
52
53
|
|
|
53
54
|
// ── Shared constants (imported to break circular dependencies) ──────────
|
|
@@ -90,6 +91,7 @@ ${c.bold}Tools (situational, but day-to-day useful)${c.reset}
|
|
|
90
91
|
${c.green}explain${c.reset} Explain a validator key, warning text, or finding code (${c.cyan}docguard explain SEC001${c.reset})
|
|
91
92
|
${c.green}verify${c.reset} Extract documented numbers/limits/enums for an agent to check vs code (${c.cyan}--semantic${c.reset})
|
|
92
93
|
${c.green}feedback${c.reset} Report likely false positives back to DocGuard (local-first + 1-click prefilled issue)
|
|
94
|
+
${c.green}mcp${c.reset} MCP server over stdio — guard/score/explain/verify/diagnose as agent tools
|
|
93
95
|
${c.green}memory${c.reset} Show what DocGuard remembers (${c.cyan}--diff${c.reset} drills into drift)
|
|
94
96
|
${c.green}trace${c.reset} Requirements traceability matrix (${c.cyan}--reverse${c.reset} for code→doc map)
|
|
95
97
|
${c.green}upgrade${c.reset} Migrate ${c.cyan}.docguard.json${c.reset} schema + CLI (${c.cyan}--apply --pr${c.reset} for team-wide PR)
|
|
@@ -375,6 +377,23 @@ async function main() {
|
|
|
375
377
|
// v0.28 (field report #5): `docguard verify --semantic` extracts
|
|
376
378
|
// documented numbers/enums/limits for the agent to check against code.
|
|
377
379
|
flags.semantic = true;
|
|
380
|
+
} else if (args[i] === '--full') {
|
|
381
|
+
// v0.29: `docguard llms --full` emits llms-full.txt (inline doc bodies,
|
|
382
|
+
// the Mintlify-popularized companion to the llms.txt index).
|
|
383
|
+
flags.full = true;
|
|
384
|
+
} else if (args[i] === '--pack') {
|
|
385
|
+
// v0.29: `docguard memory --pack` writes .docguard/context-pack.md — a
|
|
386
|
+
// compact code-truth-stamped session-start context for AI agents.
|
|
387
|
+
flags.pack = true;
|
|
388
|
+
} else if (args[i] === '--sync') {
|
|
389
|
+
// v0.29: `docguard agents --sync` regenerates the agent-file family
|
|
390
|
+
// (CLAUDE.md, GEMINI.md, copilot-instructions, .cursor rules) from
|
|
391
|
+
// AGENTS.md — the canonical source. Kills hand-duplication drift.
|
|
392
|
+
flags.sync = true;
|
|
393
|
+
} else if (args[i] === '--check') {
|
|
394
|
+
// v0.29: `docguard agents --check` — CI staleness gate for the synced
|
|
395
|
+
// agent-file family (exit 2 when a variant is missing or stale).
|
|
396
|
+
flags.check = true;
|
|
378
397
|
} else if (args[i] === '--plan') {
|
|
379
398
|
flags.plan = true;
|
|
380
399
|
} else if (args[i] === '--since' && args[i + 1]) {
|
|
@@ -512,10 +531,13 @@ async function main() {
|
|
|
512
531
|
// touch" — so it joins the club to suppress the banner AND ensureSkills'
|
|
513
532
|
// .agent/.specify writes, which were a surprising side effect of a bare
|
|
514
533
|
// `generate --plan` (and were already suppressed for `--plan --write`).
|
|
515
|
-
|
|
534
|
+
// v0.29: 'sarif' joins 'json' — any machine format where stdout IS the
|
|
535
|
+
// artifact belongs here, or the banner corrupts the payload.
|
|
536
|
+
const jsonMode = flags.format === 'json' || flags.format === 'sarif';
|
|
516
537
|
// `agent` emits a machine task graph (JSON by default) — it must be banner-
|
|
517
538
|
// free and side-effect-free like the other read-only commands.
|
|
518
|
-
|
|
539
|
+
// `mcp`: stdout IS the JSON-RPC transport — any banner byte corrupts the stream.
|
|
540
|
+
const headless = jsonMode || flags.write || flags.checkOnly || flags.changedOnly || flags.quiet || flags.plan || command === 'agent' || command === 'mcp';
|
|
519
541
|
|
|
520
542
|
if (!headless) printBanner();
|
|
521
543
|
|
|
@@ -540,6 +562,8 @@ async function main() {
|
|
|
540
562
|
'feedback',
|
|
541
563
|
// verify only reads docs and emits a task list — pure report.
|
|
542
564
|
'verify',
|
|
565
|
+
// mcp serves read-only tools over stdio — scaffolding writes are off-limits.
|
|
566
|
+
'mcp',
|
|
543
567
|
]);
|
|
544
568
|
|
|
545
569
|
// Silent auto-check: install skills/commands if missing. Skip entirely in
|
|
@@ -691,6 +715,11 @@ async function main() {
|
|
|
691
715
|
// drift — the class regex/AST can't see). Read-only.
|
|
692
716
|
runVerify(projectDir, config, flags);
|
|
693
717
|
break;
|
|
718
|
+
case 'mcp':
|
|
719
|
+
// MCP stdio server — guard/score/explain/verify-claims/diagnose as tools
|
|
720
|
+
// for MCP clients. Long-lived; resolves when stdin closes.
|
|
721
|
+
await runMcp(projectDir, config, flags);
|
|
722
|
+
break;
|
|
694
723
|
case 'memory':
|
|
695
724
|
runMemory(projectDir, config, flags);
|
|
696
725
|
break;
|