docguard-cli 0.30.0 → 0.30.1

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 CHANGED
@@ -13,6 +13,8 @@
13
13
  [![Node.js](https://img.shields.io/badge/Node.js-18%2B-green)](https://nodejs.org)
14
14
  [![Runtime deps](https://img.shields.io/badge/runtime_deps-1_(pinned)-green)](package.json)
15
15
  [![Spec Kit Extension](https://img.shields.io/badge/Spec_Kit-Extension-blueviolet)](https://github.com/github/spec-kit)
16
+ [![Glama](https://glama.ai/mcp/servers/raccioly/docguard/badges/score.svg)](https://glama.ai/mcp/servers/raccioly/docguard)
17
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-0a7ea4)](https://registry.modelcontextprotocol.io/)
16
18
 
17
19
  ---
18
20
 
@@ -53,25 +53,40 @@ const PROJECT_DIR_PROP = {
53
53
  },
54
54
  };
55
55
 
56
+ // Every DocGuard MCP tool is READ-ONLY: it inspects local project files and
57
+ // never writes, mutates, or reaches the network. These MCP tool hints let
58
+ // clients (and directory scanners like Glama) surface that safety to users.
59
+ const READONLY_ANNOTATIONS = {
60
+ readOnlyHint: true,
61
+ destructiveHint: false,
62
+ idempotentHint: true,
63
+ openWorldHint: false,
64
+ };
65
+
56
66
  const TOOLS = [
57
67
  {
58
68
  name: 'docguard_guard',
69
+ title: 'Guard docs against code',
59
70
  description: 'Run every enabled DocGuard validator against the project\'s canonical docs. Returns the full guard JSON contract: status (PASS/WARN/FAIL), structured findings with stable codes and suggestions, nextStep, doc coverage map, semantic-claim count, and per-validator results.',
60
71
  inputSchema: {
61
72
  type: 'object',
62
73
  properties: { ...PROJECT_DIR_PROP },
63
74
  },
75
+ annotations: READONLY_ANNOTATIONS,
64
76
  },
65
77
  {
66
78
  name: 'docguard_score',
79
+ title: 'CDD maturity score',
67
80
  description: 'Compute the project\'s CDD maturity score (0-100) with letter grade and per-category breakdown.',
68
81
  inputSchema: {
69
82
  type: 'object',
70
83
  properties: { ...PROJECT_DIR_PROP },
71
84
  },
85
+ annotations: READONLY_ANNOTATIONS,
72
86
  },
73
87
  {
74
88
  name: 'docguard_explain',
89
+ title: 'Explain a finding code',
75
90
  description: 'Explain a stable DocGuard finding code (e.g. STR001, ENV003): what it means, which validator emits it, and the inline suppression to use if it\'s a confirmed false positive.',
76
91
  inputSchema: {
77
92
  type: 'object',
@@ -83,22 +98,27 @@ const TOOLS = [
83
98
  },
84
99
  required: ['code'],
85
100
  },
101
+ annotations: READONLY_ANNOTATIONS,
86
102
  },
87
103
  {
88
104
  name: 'docguard_verify_claims',
105
+ title: 'Extract claims to verify',
89
106
  description: 'Extract the semantic claims in the project\'s canonical docs — documented numbers, limits, and enums — as a verification task list. Deterministic discovery, LLM judgment — the caller verifies each claim against the code.',
90
107
  inputSchema: {
91
108
  type: 'object',
92
109
  properties: { ...PROJECT_DIR_PROP },
93
110
  },
111
+ annotations: READONLY_ANNOTATIONS,
94
112
  },
95
113
  {
96
114
  name: 'docguard_diagnose',
115
+ title: 'Diagnose what to fix',
97
116
  description: 'Run guard and return only what needs fixing: failing/warning validators with their messages, structured findings, and suggested next actions — shaped for an agent to act on.',
98
117
  inputSchema: {
99
118
  type: 'object',
100
119
  properties: { ...PROJECT_DIR_PROP },
101
120
  },
121
+ annotations: READONLY_ANNOTATIONS,
102
122
  },
103
123
  ];
104
124
 
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
7
7
  import { resolve, join, extname } from 'node:path';
8
- import { execSync } from 'node:child_process';
8
+ import { execFileSync } 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';
@@ -1039,11 +1039,16 @@ function estimateDocTax(projectDir, config, scores) {
1039
1039
  // Estimate code churn (commits in last 30 days)
1040
1040
  let recentCommits = 0;
1041
1041
  try {
1042
- const output = execSync('git log --oneline --since="30 days ago" 2>/dev/null | wc -l', {
1042
+ // execFileSync (argv array) + count in JS avoids the shell `| wc -l` pipe,
1043
+ // which isn't portable to Windows (no `wc`) and needs a shell at all. Same
1044
+ // pattern freshness.mjs already uses for commit counting.
1045
+ const output = execFileSync('git', ['log', '--oneline', '--since=30 days ago'], {
1043
1046
  cwd: projectDir,
1044
1047
  encoding: 'utf-8',
1045
- }).trim();
1046
- recentCommits = parseInt(output, 10) || 0;
1048
+ stdio: ['pipe', 'pipe', 'ignore'],
1049
+ maxBuffer: 1024 * 1024 * 5,
1050
+ });
1051
+ recentCommits = output.trim() ? output.trim().split('\n').length : 0;
1047
1052
  } catch {
1048
1053
  recentCommits = 10; // Default assumption
1049
1054
  }
@@ -21,7 +21,6 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from
21
21
  import { resolve, dirname, basename } from 'node:path';
22
22
  import { fileURLToPath } from 'node:url';
23
23
  import { createInterface } from 'node:readline';
24
- import { execSync } from 'node:child_process';
25
24
  import { c, CURRENT_SCHEMA_VERSION } from '../shared.mjs';
26
25
  import { ensureSkills, detectAgentMode, isSpecKitInitialized, getDetectedAgent } from '../ensure-skills.mjs';
27
26
 
@@ -70,18 +69,6 @@ function detectProjectType(dir) {
70
69
  return 'unknown';
71
70
  }
72
71
 
73
- // ── CLI Detection ───────────────────────────────────────────────────────
74
-
75
- function isCliAvailable(name) {
76
- try {
77
- const cmd = process.platform === 'win32' ? `where ${name}` : `which ${name}`;
78
- execSync(`${cmd} 2>/dev/null`, { encoding: 'utf-8', timeout: 3000 });
79
- return true;
80
- } catch {
81
- return false;
82
- }
83
- }
84
-
85
72
  function detectAgentDirs(projectDir) {
86
73
  const agentDirs = [
87
74
  { name: 'GitHub Copilot', dir: '.github', commandsPath: '.github/commands' },
@@ -167,7 +167,14 @@ export function getHooksDir(dir) {
167
167
  ).trim();
168
168
  // --git-path returns a path relative to `dir` (cwd) or an absolute path;
169
169
  // resolve() handles both.
170
- if (out) return resolve(dir, out);
170
+ //
171
+ // Guard `/dev/null`: when `core.hooksPath` is set to /dev/null (a common
172
+ // "disable all hooks" convention — and what Jules's sandbox VM does),
173
+ // git returns the literal `/dev/null`. resolve()-ing it and then writing
174
+ // `<hooksDir>/pre-commit` gives `ENOTDIR: /dev/null/pre-commit`. Treat it
175
+ // as "no usable hooks dir" and fall through to the `.git/hooks` check so
176
+ // hook install/list still works in that environment. (bug-200)
177
+ if (out && out !== '/dev/null') return resolve(dir, out);
171
178
  } catch {
172
179
  // git unavailable or not a repo — fall through to the literal-path check.
173
180
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docguard-cli",
3
- "version": "0.30.0",
3
+ "version": "0.30.1",
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": {