monomind 1.17.0 → 1.17.2

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.
Files changed (94) hide show
  1. package/.claude/agents/engineering/engineering-security-engineer.md +1 -1
  2. package/.claude/commands/mastermind/_repeat.md +4 -0
  3. package/.claude/commands/mastermind/master.md +52 -1
  4. package/.claude/scheduled_tasks.lock +1 -1
  5. package/.claude/skills/mastermind/_repeat.md +2 -0
  6. package/package.json +1 -1
  7. package/packages/@monomind/cli/.claude/agents/engineering/engineering-security-engineer.md +1 -1
  8. package/packages/@monomind/cli/.claude/commands/mastermind/_repeat.md +4 -0
  9. package/packages/@monomind/cli/.claude/commands/mastermind/master.md +52 -1
  10. package/packages/@monomind/cli/.claude/skills/mastermind/_repeat.md +2 -0
  11. package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.js +42 -59
  12. package/packages/@monomind/cli/dist/src/agents/registry-builder.d.ts +8 -0
  13. package/packages/@monomind/cli/dist/src/agents/registry-builder.js +22 -0
  14. package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +18 -0
  15. package/packages/@monomind/cli/dist/src/browser/dashboard/ui.html +37 -125
  16. package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.d.ts +17 -0
  17. package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +320 -0
  18. package/packages/@monomind/cli/dist/src/commands/agent-ops.d.ts +9 -0
  19. package/packages/@monomind/cli/dist/src/commands/agent-ops.js +329 -0
  20. package/packages/@monomind/cli/dist/src/commands/agent.js +5 -907
  21. package/packages/@monomind/cli/dist/src/commands/analyze-ast.d.ts +26 -0
  22. package/packages/@monomind/cli/dist/src/commands/analyze-ast.js +284 -0
  23. package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.d.ts +14 -0
  24. package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.js +295 -0
  25. package/packages/@monomind/cli/dist/src/commands/analyze-diff.d.ts +8 -0
  26. package/packages/@monomind/cli/dist/src/commands/analyze-diff.js +395 -0
  27. package/packages/@monomind/cli/dist/src/commands/analyze-graph.d.ts +14 -0
  28. package/packages/@monomind/cli/dist/src/commands/analyze-graph.js +304 -0
  29. package/packages/@monomind/cli/dist/src/commands/analyze-imports.d.ts +11 -0
  30. package/packages/@monomind/cli/dist/src/commands/analyze-imports.js +287 -0
  31. package/packages/@monomind/cli/dist/src/commands/analyze-symbols.d.ts +14 -0
  32. package/packages/@monomind/cli/dist/src/commands/analyze-symbols.js +302 -0
  33. package/packages/@monomind/cli/dist/src/commands/analyze.d.ts +38 -0
  34. package/packages/@monomind/cli/dist/src/commands/analyze.js +12 -1827
  35. package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.d.ts +26 -0
  36. package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +189 -0
  37. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.d.ts +20 -0
  38. package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +432 -0
  39. package/packages/@monomind/cli/dist/src/commands/doctor.js +54 -943
  40. package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.d.ts +11 -0
  41. package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.js +242 -0
  42. package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.d.ts +35 -0
  43. package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.js +203 -0
  44. package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.d.ts +8 -0
  45. package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.js +233 -0
  46. package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.d.ts +12 -0
  47. package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.js +274 -0
  48. package/packages/@monomind/cli/dist/src/commands/hive-mind.js +10 -1129
  49. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +4 -4
  50. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +19 -819
  51. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +7 -0
  52. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +334 -0
  53. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.d.ts +7 -0
  54. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.js +399 -0
  55. package/packages/@monomind/cli/dist/src/commands/init-subcommands.d.ts +8 -0
  56. package/packages/@monomind/cli/dist/src/commands/init-subcommands.js +156 -0
  57. package/packages/@monomind/cli/dist/src/commands/init-upgrade.d.ts +6 -0
  58. package/packages/@monomind/cli/dist/src/commands/init-upgrade.js +203 -0
  59. package/packages/@monomind/cli/dist/src/commands/init-wizard.d.ts +6 -0
  60. package/packages/@monomind/cli/dist/src/commands/init-wizard.js +246 -0
  61. package/packages/@monomind/cli/dist/src/commands/init.js +6 -623
  62. package/packages/@monomind/cli/dist/src/commands/memory-admin.d.ts +10 -0
  63. package/packages/@monomind/cli/dist/src/commands/memory-admin.js +433 -0
  64. package/packages/@monomind/cli/dist/src/commands/memory-crud.d.ts +9 -0
  65. package/packages/@monomind/cli/dist/src/commands/memory-crud.js +342 -0
  66. package/packages/@monomind/cli/dist/src/commands/memory-list.d.ts +10 -0
  67. package/packages/@monomind/cli/dist/src/commands/memory-list.js +321 -0
  68. package/packages/@monomind/cli/dist/src/commands/memory-transfer.d.ts +9 -0
  69. package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +372 -0
  70. package/packages/@monomind/cli/dist/src/commands/memory.d.ts +6 -0
  71. package/packages/@monomind/cli/dist/src/commands/memory.js +10 -1441
  72. package/packages/@monomind/cli/dist/src/commands/neural-core.d.ts +8 -0
  73. package/packages/@monomind/cli/dist/src/commands/neural-core.js +274 -0
  74. package/packages/@monomind/cli/dist/src/commands/neural-optimize.d.ts +7 -0
  75. package/packages/@monomind/cli/dist/src/commands/neural-optimize.js +332 -0
  76. package/packages/@monomind/cli/dist/src/commands/neural-registry.d.ts +7 -0
  77. package/packages/@monomind/cli/dist/src/commands/neural-registry.js +290 -0
  78. package/packages/@monomind/cli/dist/src/commands/neural.js +3 -974
  79. package/packages/@monomind/cli/dist/src/commands/platforms.js +327 -7
  80. package/packages/@monomind/cli/dist/src/commands/security-cve.d.ts +6 -0
  81. package/packages/@monomind/cli/dist/src/commands/security-cve.js +310 -0
  82. package/packages/@monomind/cli/dist/src/commands/security-misc.d.ts +9 -0
  83. package/packages/@monomind/cli/dist/src/commands/security-misc.js +293 -0
  84. package/packages/@monomind/cli/dist/src/commands/security-scan.d.ts +18 -0
  85. package/packages/@monomind/cli/dist/src/commands/security-scan.js +328 -0
  86. package/packages/@monomind/cli/dist/src/commands/security.js +3 -958
  87. package/packages/@monomind/cli/dist/src/commands/session.js +1 -1
  88. package/packages/@monomind/cli/dist/src/commands/swarm.js +23 -17
  89. package/packages/@monomind/cli/dist/src/index.js +8 -37
  90. package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +77 -0
  91. package/packages/@monomind/cli/dist/src/parser.js +11 -6
  92. package/packages/@monomind/cli/dist/src/routing/llm-caller.js +1 -2
  93. package/packages/@monomind/cli/package.json +2 -3
  94. package/packages/@monomind/cli/scripts/understand-analyze.mjs +1 -1
@@ -0,0 +1,432 @@
1
+ /**
2
+ * Doctor — project/monomind health checks
3
+ * Config, daemon, memory, API keys, MCP, monograph, helpers, routing, gates, gitignore
4
+ */
5
+ import { existsSync, readFileSync, statSync, mkdirSync } from 'fs';
6
+ import { join, dirname } from 'path';
7
+ import { fileURLToPath } from 'url';
8
+ import { execSync } from 'child_process';
9
+ import { homedir } from 'os';
10
+ import { MAX_DOCTOR_PKG_BYTES, MAX_DOCTOR_CONFIG_BYTES, MAX_DOCTOR_GITIGNORE_BYTES, MAX_DOCTOR_PID_BYTES, MAX_DOCTOR_HELPER_BYTES, } from './doctor-env-checks.js';
11
+ export async function checkConfigFile() {
12
+ const jsonPaths = ['.monomind/config.json', 'monomind.config.json', '.monomind.json'];
13
+ for (const configPath of jsonPaths) {
14
+ if (existsSync(configPath) && statSync(configPath).size <= MAX_DOCTOR_CONFIG_BYTES) {
15
+ try {
16
+ JSON.parse(readFileSync(configPath, 'utf8'));
17
+ return { name: 'Config File', status: 'pass', message: `Found: ${configPath}` };
18
+ }
19
+ catch {
20
+ return { name: 'Config File', status: 'fail', message: `Invalid JSON: ${configPath}`, fix: 'Fix JSON syntax in config file' };
21
+ }
22
+ }
23
+ }
24
+ const yamlPaths = ['.monomind/config.yaml', '.monomind/config.yml', 'monomind.config.yaml'];
25
+ for (const configPath of yamlPaths) {
26
+ if (existsSync(configPath))
27
+ return { name: 'Config File', status: 'pass', message: `Found: ${configPath}` };
28
+ }
29
+ return { name: 'Config File', status: 'warn', message: 'No config file (using defaults)', fix: 'monomind config init' };
30
+ }
31
+ export async function checkDaemonStatus() {
32
+ try {
33
+ const pidFile = '.monomind/daemon.pid';
34
+ if (existsSync(pidFile) && statSync(pidFile).size <= MAX_DOCTOR_PID_BYTES) {
35
+ const pid = readFileSync(pidFile, 'utf8').trim();
36
+ try {
37
+ process.kill(parseInt(pid, 10), 0);
38
+ return { name: 'Daemon Status', status: 'pass', message: `Running (PID: ${pid})` };
39
+ }
40
+ catch {
41
+ return { name: 'Daemon Status', status: 'warn', message: 'Stale PID file', fix: 'rm .monomind/daemon.pid && monomind daemon start' };
42
+ }
43
+ }
44
+ return { name: 'Daemon Status', status: 'warn', message: 'Not running', fix: 'monomind daemon start' };
45
+ }
46
+ catch {
47
+ return { name: 'Daemon Status', status: 'warn', message: 'Unable to check', fix: 'monomind daemon status' };
48
+ }
49
+ }
50
+ export async function checkMemoryDatabase() {
51
+ const dbPaths = ['.monomind/memory.db', '.swarm/memory.db', 'data/memory.db'];
52
+ for (const dbPath of dbPaths) {
53
+ if (existsSync(dbPath)) {
54
+ try {
55
+ const sizeMB = (statSync(dbPath).size / 1024 / 1024).toFixed(2);
56
+ return { name: 'Memory Database', status: 'pass', message: `${dbPath} (${sizeMB} MB)` };
57
+ }
58
+ catch {
59
+ return { name: 'Memory Database', status: 'warn', message: `${dbPath} (unable to stat)` };
60
+ }
61
+ }
62
+ }
63
+ return { name: 'Memory Database', status: 'warn', message: 'Not initialized', fix: 'monomind memory configure --backend hybrid' };
64
+ }
65
+ export async function checkApiKeys() {
66
+ const keys = ['ANTHROPIC_API_KEY', 'CLAUDE_API_KEY', 'OPENAI_API_KEY'];
67
+ const found = keys.filter(k => process.env[k]);
68
+ const inClaudeCode = !!(process.env.CLAUDE_CODE || process.env.CLAUDE_PROJECT_DIR || process.env.MCP_SESSION_ID);
69
+ let claudeCliAvailable = false;
70
+ try {
71
+ execSync('claude --version', { encoding: 'utf-8', stdio: 'pipe', timeout: 5000, windowsHide: true });
72
+ claudeCliAvailable = true;
73
+ }
74
+ catch { /* not on PATH */ }
75
+ if (found.includes('ANTHROPIC_API_KEY') || found.includes('CLAUDE_API_KEY')) {
76
+ return { name: 'API Keys', status: 'pass', message: `Found: ${found.join(', ')}` };
77
+ }
78
+ else if (inClaudeCode) {
79
+ return { name: 'API Keys', status: 'pass', message: 'Claude Code manages auth (no direct API key needed)' };
80
+ }
81
+ else if (claudeCliAvailable) {
82
+ return { name: 'API Keys', status: 'pass', message: 'Using Claude Code CLI auth (no direct API key needed)' };
83
+ }
84
+ else if (found.length > 0) {
85
+ return { name: 'API Keys', status: 'warn', message: `Found: ${found.join(', ')} (no Claude key)`, fix: 'export ANTHROPIC_API_KEY=your_key' };
86
+ }
87
+ return {
88
+ name: 'API Keys',
89
+ status: 'warn',
90
+ message: 'Claude Code CLI not found — monomind works best on top of Claude Code',
91
+ fix: 'npm install -g @anthropic-ai/claude-code # then: claude login',
92
+ };
93
+ }
94
+ export async function checkMcpServers() {
95
+ const mcpConfigPaths = [
96
+ join(homedir(), '.claude/claude_desktop_config.json'),
97
+ join(homedir(), '.config/claude/mcp.json'),
98
+ '.mcp.json',
99
+ '.claude/settings.json',
100
+ '.claude/settings.local.json',
101
+ join(homedir(), '.claude/settings.json'),
102
+ ];
103
+ for (const configPath of mcpConfigPaths) {
104
+ if (existsSync(configPath) && statSync(configPath).size <= MAX_DOCTOR_CONFIG_BYTES) {
105
+ try {
106
+ const content = JSON.parse(readFileSync(configPath, 'utf8'));
107
+ const servers = content.mcpServers || content.servers || {};
108
+ const count = Object.keys(servers).length;
109
+ const hasMonomind = 'monomind' in servers || 'monomind_alpha' in servers;
110
+ if (hasMonomind)
111
+ return { name: 'MCP Servers', status: 'pass', message: `${count} servers (monomind configured)` };
112
+ return { name: 'MCP Servers', status: 'warn', message: `${count} servers (monomind not found)`, fix: 'claude mcp add monomind -- npx -y monomind@latest mcp start' };
113
+ }
114
+ catch { /* try next */ }
115
+ }
116
+ }
117
+ return { name: 'MCP Servers', status: 'warn', message: 'No MCP config found', fix: 'claude mcp add monomind -- npx -y monomind@latest mcp start' };
118
+ }
119
+ export async function checkMonograph() {
120
+ try {
121
+ const __filename = fileURLToPath(import.meta.url);
122
+ const _base = dirname(__filename);
123
+ let _globalRoot = '';
124
+ try {
125
+ _globalRoot = execSync('npm root -g', { encoding: 'utf8', timeout: 3000 }).trim();
126
+ }
127
+ catch { /* no npm */ }
128
+ const candidates = [
129
+ join(_base, '..', '..', 'node_modules', '@monomind', 'monograph', 'package.json'),
130
+ join(_base, '..', '..', '..', '..', 'node_modules', '@monomind', 'monograph', 'package.json'),
131
+ join(_base, '..', '..', 'node_modules', '@monoes', 'monograph', 'package.json'),
132
+ join(_base, '..', '..', '..', '..', 'node_modules', '@monoes', 'monograph', 'package.json'),
133
+ ...(_globalRoot ? [
134
+ join(_globalRoot, '@monomind', 'monograph', 'package.json'),
135
+ join(_globalRoot, '@monoes', 'monograph', 'package.json'),
136
+ ] : []),
137
+ ];
138
+ const found = candidates.find(p => existsSync(p) && statSync(p).size <= MAX_DOCTOR_PKG_BYTES);
139
+ if (found) {
140
+ try {
141
+ const pkg = JSON.parse(readFileSync(found, 'utf-8'));
142
+ return { name: 'Monograph', status: 'pass', message: `v${pkg.version || '?'} available (knowledge graph engine)` };
143
+ }
144
+ catch {
145
+ return { name: 'Monograph', status: 'pass', message: 'available (knowledge graph engine)' };
146
+ }
147
+ }
148
+ return { name: 'Monograph', status: 'warn', message: 'Package not found (knowledge graph disabled)', fix: 'npm install -g monomind@latest' };
149
+ }
150
+ catch {
151
+ return { name: 'Monograph', status: 'warn', message: 'Package check failed', fix: 'npm install -g monomind@latest' };
152
+ }
153
+ }
154
+ export async function checkMonographFreshness() {
155
+ try {
156
+ const cwd = process.cwd();
157
+ const dbPath = join(cwd, '.monomind', 'monograph.db');
158
+ const lockPath = join(cwd, '.monomind', 'graph', '.rebuild-lock');
159
+ const statsPath = join(cwd, '.monomind', 'graph', 'stats.json');
160
+ const hasDb = existsSync(dbPath);
161
+ if (!hasDb && !existsSync(statsPath)) {
162
+ return { name: 'Graph freshness', status: 'warn', message: 'No monograph graph built yet', fix: 'mcp__monomind__monograph_build codeOnly:true' };
163
+ }
164
+ let buildMs = 0;
165
+ if (hasDb) {
166
+ try {
167
+ buildMs = Math.max(buildMs, statSync(dbPath).mtimeMs);
168
+ }
169
+ catch { /* ignore */ }
170
+ }
171
+ try {
172
+ buildMs = Math.max(buildMs, statSync(lockPath).mtimeMs);
173
+ }
174
+ catch { /* ignore */ }
175
+ try {
176
+ buildMs = Math.max(buildMs, statSync(statsPath).mtimeMs);
177
+ }
178
+ catch { /* ignore */ }
179
+ if (buildMs === 0)
180
+ return { name: 'Graph freshness', status: 'warn', message: 'Graph exists but build time unknown' };
181
+ const buildIso = new Date(buildMs).toISOString();
182
+ let commitsBehind = 0;
183
+ try {
184
+ const out = execSync(`git rev-list --count --since='${buildIso}' HEAD 2>/dev/null`, { encoding: 'utf8', timeout: 2000, cwd }).trim();
185
+ commitsBehind = parseInt(out, 10) || 0;
186
+ }
187
+ catch { /* git unavailable */ }
188
+ const ageMinutes = Math.floor((Date.now() - buildMs) / 60000);
189
+ const ageStr = ageMinutes < 60 ? `${ageMinutes}m ago` : `${Math.floor(ageMinutes / 60)}h ago`;
190
+ if (commitsBehind === 0)
191
+ return { name: 'Graph freshness', status: 'pass', message: `FRESH — built ${ageStr}, 0 commits behind` };
192
+ if (commitsBehind <= 5)
193
+ return { name: 'Graph freshness', status: 'warn', message: `${commitsBehind} commit(s) behind — built ${ageStr}`, fix: 'mcp__monomind__monograph_build codeOnly:true' };
194
+ return { name: 'Graph freshness', status: 'fail', message: `STALE — ${commitsBehind} commits behind (built ${ageStr})`, fix: 'mcp__monomind__monograph_build codeOnly:true' };
195
+ }
196
+ catch {
197
+ return { name: 'Graph freshness', status: 'warn', message: 'Could not check graph freshness' };
198
+ }
199
+ }
200
+ export async function checkMonoesMemory() {
201
+ try {
202
+ const __filename = fileURLToPath(import.meta.url);
203
+ const _base = dirname(__filename);
204
+ let _globalRoot = '';
205
+ try {
206
+ _globalRoot = execSync('npm root -g', { encoding: 'utf8', timeout: 3000 }).trim();
207
+ }
208
+ catch { /* no npm */ }
209
+ const candidates = [
210
+ join(_base, '..', '..', 'node_modules', '@monoes', 'memory', 'package.json'),
211
+ join(_base, '..', '..', '..', '..', 'node_modules', '@monoes', 'memory', 'package.json'),
212
+ ...(_globalRoot ? [join(_globalRoot, '@monoes', 'memory', 'package.json')] : []),
213
+ ];
214
+ const found = candidates.find(p => existsSync(p) && statSync(p).size <= MAX_DOCTOR_PKG_BYTES);
215
+ if (found) {
216
+ try {
217
+ const pkg = JSON.parse(readFileSync(found, 'utf-8'));
218
+ return { name: 'Vector Memory', status: 'pass', message: `@monoes/memory v${pkg.version || '?'} (HNSW search enabled)` };
219
+ }
220
+ catch {
221
+ return { name: 'Vector Memory', status: 'pass', message: '@monoes/memory available (HNSW search enabled)' };
222
+ }
223
+ }
224
+ return { name: 'Vector Memory', status: 'warn', message: '@monoes/memory not installed (vector search disabled)', fix: 'npm install @monoes/memory' };
225
+ }
226
+ catch {
227
+ return { name: 'Vector Memory', status: 'warn', message: 'Vector memory check failed' };
228
+ }
229
+ }
230
+ function _resolveBundledHelper(relativePath) {
231
+ try {
232
+ const thisFile = fileURLToPath(import.meta.url);
233
+ let dir = dirname(thisFile);
234
+ for (;;) {
235
+ const candidate = join(dir, 'package.json');
236
+ if (existsSync(candidate) && statSync(candidate).size <= MAX_DOCTOR_PKG_BYTES) {
237
+ try {
238
+ const pkg = JSON.parse(readFileSync(candidate, 'utf8'));
239
+ if (pkg.name === '@monomind/cli' || pkg.name === 'monomind' || pkg.name === '@monoes/monomindcli') {
240
+ const helperPath = join(dir, relativePath);
241
+ return existsSync(helperPath) ? helperPath : null;
242
+ }
243
+ }
244
+ catch { /* keep walking */ }
245
+ }
246
+ const parent = dirname(dir);
247
+ if (parent === dir)
248
+ return null;
249
+ dir = parent;
250
+ }
251
+ }
252
+ catch {
253
+ return null;
254
+ }
255
+ }
256
+ async function _detectStaleHelpers() {
257
+ const stale = [];
258
+ const missing = [];
259
+ const helpers = ['hook-handler.cjs', 'statusline.cjs', 'router.cjs', 'graphify-freshen.cjs'];
260
+ const crypto = await import('node:crypto');
261
+ for (const name of helpers) {
262
+ const local = join(process.cwd(), '.claude', 'helpers', name);
263
+ if (!existsSync(local) || statSync(local).size > MAX_DOCTOR_HELPER_BYTES)
264
+ continue;
265
+ const bundled = _resolveBundledHelper(join('.claude', 'helpers', name));
266
+ if (!bundled) {
267
+ missing.push(name);
268
+ continue;
269
+ }
270
+ if (statSync(bundled).size > MAX_DOCTOR_HELPER_BYTES)
271
+ continue;
272
+ try {
273
+ const hashLocal = crypto.createHash('sha256').update(readFileSync(local)).digest('hex');
274
+ const hashBundled = crypto.createHash('sha256').update(readFileSync(bundled)).digest('hex');
275
+ if (hashLocal !== hashBundled)
276
+ stale.push(name);
277
+ }
278
+ catch { /* skip */ }
279
+ }
280
+ return { stale, missing };
281
+ }
282
+ export async function checkHelpersFresh() {
283
+ try {
284
+ const { stale, missing } = await _detectStaleHelpers();
285
+ if (stale.length === 0 && missing.length === 0) {
286
+ return { name: 'Helper Files', status: 'pass', message: 'Project helpers match bundled version' };
287
+ }
288
+ if (stale.length > 0) {
289
+ return { name: 'Helper Files', status: 'warn', message: `${stale.length} stale helper(s): ${stale.join(', ')}`, fix: 'monomind init upgrade' };
290
+ }
291
+ return { name: 'Helper Files', status: 'warn', message: `Could not locate bundled copies of: ${missing.join(', ')}`, fix: 'Reinstall monomind or run `monomind init upgrade`' };
292
+ }
293
+ catch (e) {
294
+ return { name: 'Helper Files', status: 'warn', message: `check failed: ${e instanceof Error ? e.message : 'unknown'}` };
295
+ }
296
+ }
297
+ function fmtPct(v) {
298
+ return v === null ? 'n/a' : `${Math.round(v * 100)}%`;
299
+ }
300
+ async function routingAccuracyLine() {
301
+ try {
302
+ const { computeRoutingAccuracy, computeAdherence } = await import('../monovector/route-outcomes.js');
303
+ const baseDir = join(process.cwd(), '.monomind');
304
+ const acc = await computeRoutingAccuracy(baseDir, 100);
305
+ const adh = await computeAdherence(baseDir);
306
+ const adhStr = ` | adherence ${fmtPct(adh.adherence)} (n=${adh.sample})`;
307
+ if (acc.accuracy === null)
308
+ return `routing accuracy (last 100): no outcome data yet${adhStr}`;
309
+ const trend = acc.recentVsPrior === null ? '' : ` trend ${acc.recentVsPrior >= 0 ? '+' : ''}${Math.round(acc.recentVsPrior * 100)}%`;
310
+ return `routing accuracy (last ${acc.window}): ${fmtPct(acc.accuracy)} [native ${fmtPct(acc.byMode.native)} / js ${fmtPct(acc.byMode.js)}]${trend}${adhStr}`;
311
+ }
312
+ catch {
313
+ return 'routing accuracy (last 100): no outcome data yet';
314
+ }
315
+ }
316
+ export async function checkMonoesIntegration() {
317
+ try {
318
+ return { name: 'Routing Learning', status: 'pass', message: await routingAccuracyLine() };
319
+ }
320
+ catch (err) {
321
+ return { name: 'Routing Learning', status: 'warn', message: `Could not compute routing accuracy: ${err instanceof Error ? err.message : String(err)}` };
322
+ }
323
+ }
324
+ const REQUIRED_GITIGNORE_PATTERNS = [
325
+ { pattern: '.monomind/sessions/', reason: 'session files contain cwd and machine paths' },
326
+ { pattern: '.monomind/data/', reason: 'intelligence data with edit file paths' },
327
+ { pattern: '.monomind/metrics/', reason: 'metrics with file path references' },
328
+ { pattern: '.monomind/knowledge/', reason: 'knowledge chunks with local file content' },
329
+ { pattern: '.monomind/*.json', reason: 'root-level runtime JSON (control, registry, routing)' },
330
+ { pattern: '.monomind/*.jsonl', reason: 'root-level event logs (decisions, routing-feedback)' },
331
+ { pattern: '**/.monomind/sessions/', reason: 'nested session files in sub-packages' },
332
+ { pattern: '**/.monomind/*.json', reason: 'nested runtime JSON in sub-packages' },
333
+ { pattern: 'data/sessions/', reason: 'session files with machine paths' },
334
+ { pattern: 'data/mastermind-*.json', reason: 'mastermind session data' },
335
+ { pattern: 'data/mastermind-*.jsonl', reason: 'mastermind event logs' },
336
+ { pattern: '**/.claude-flow/', reason: 'claude-flow runtime data with paths' },
337
+ { pattern: '.hive-mind/', reason: 'hive-mind state with session info' },
338
+ { pattern: '.swarm/', reason: 'swarm state files' },
339
+ ];
340
+ export async function checkGitignoreCoverage() {
341
+ const gitignorePath = join(process.cwd(), '.gitignore');
342
+ if (!existsSync(gitignorePath)) {
343
+ return { name: 'Gitignore Coverage', status: 'warn', message: 'No .gitignore found — all monomind runtime paths are unprotected', fix: 'echo ".monomind/\\n**/.monomind/" >> .gitignore' };
344
+ }
345
+ if (statSync(gitignorePath).size > MAX_DOCTOR_GITIGNORE_BYTES) {
346
+ return { name: 'Gitignore Coverage', status: 'warn', message: '.gitignore too large to parse' };
347
+ }
348
+ const lines = readFileSync(gitignorePath, 'utf-8').split('\n').map(l => l.trim()).filter(l => l && !l.startsWith('#'));
349
+ const missing = REQUIRED_GITIGNORE_PATTERNS.filter(({ pattern }) => {
350
+ const base = pattern.replace(/\*\*\//g, '').replace(/\*/g, '');
351
+ return !lines.some(l => l === pattern || l === pattern.replace(/\/$/, '') ||
352
+ (l.includes('**') && base && l.replace(/\*\*\//g, '').replace(/\*/g, '') === base));
353
+ });
354
+ if (missing.length === 0)
355
+ return { name: 'Gitignore Coverage', status: 'pass', message: 'All monomind runtime paths are gitignored' };
356
+ const missingList = missing.map(m => m.pattern).join(', ');
357
+ return { name: 'Gitignore Coverage', status: 'warn', message: `${missing.length} runtime path(s) not in .gitignore: ${missingList}`, fix: `printf "${missing.map(m => m.pattern).join('\\n')}\\n" >> .gitignore` };
358
+ }
359
+ export async function checkAgentRegistry() {
360
+ try {
361
+ const { buildUnifiedRegistry, computeAgentRoots } = await import('../agents/registry-builder.js');
362
+ const cwd = process.cwd();
363
+ const roots = computeAgentRoots(cwd);
364
+ const outDir = join(cwd, '.monomind');
365
+ mkdirSync(outDir, { recursive: true });
366
+ // Rebuilds fresh in-memory (and refreshes .monomind/registry.json on disk)
367
+ // rather than reading a file that a separate, unawaited startup task also
368
+ // writes — avoids reporting stale results from a race between the two.
369
+ const registry = buildUnifiedRegistry(roots, join(outDir, 'registry.json'));
370
+ const entries = registry.agents;
371
+ if (entries.length === 0) {
372
+ return { name: 'Agent Registry', status: 'warn', message: 'No agents found under .claude/agents', fix: 'monomind init (installs agent definitions)' };
373
+ }
374
+ let missingSlug = 0, missingName = 0, missingDescription = 0;
375
+ for (const agent of entries) {
376
+ if (!agent.slug)
377
+ missingSlug++;
378
+ if (!agent.name)
379
+ missingName++;
380
+ if (!agent.description)
381
+ missingDescription++;
382
+ }
383
+ const total = missingSlug + missingName + missingDescription;
384
+ if (total === 0) {
385
+ return { name: 'Agent Registry', status: 'pass', message: `${entries.length} agent(s), all metadata complete` };
386
+ }
387
+ const parts = [
388
+ missingSlug > 0 ? `${missingSlug} missing slug` : null,
389
+ missingName > 0 ? `${missingName} missing name` : null,
390
+ missingDescription > 0 ? `${missingDescription} missing description` : null,
391
+ ].filter(Boolean).join(', ');
392
+ return {
393
+ name: 'Agent Registry',
394
+ status: 'warn',
395
+ message: `${total} metadata issue(s) across ${entries.length} agent(s): ${parts}`,
396
+ fix: 'Add the missing field(s) to frontmatter in .claude/agents/*.md',
397
+ };
398
+ }
399
+ catch {
400
+ return { name: 'Agent Registry', status: 'warn', message: 'Could not build/parse agent registry' };
401
+ }
402
+ }
403
+ export async function checkGuidanceGates() {
404
+ const settingsPath = join(process.cwd(), '.claude', 'settings.json');
405
+ const gatesHandlerPath = join(process.cwd(), '.claude', 'helpers', 'handlers', 'gates-handler.cjs');
406
+ if (!existsSync(gatesHandlerPath)) {
407
+ return { name: 'Guidance Gates', status: 'warn', message: 'gates-handler.cjs not found — enforcement gates not installed', fix: 'monomind init (then monomind guidance setup)' };
408
+ }
409
+ if (!existsSync(settingsPath)) {
410
+ return { name: 'Guidance Gates', status: 'warn', message: 'gates-handler.cjs present but .claude/settings.json missing', fix: 'monomind guidance setup' };
411
+ }
412
+ try {
413
+ if (statSync(settingsPath).size > MAX_DOCTOR_CONFIG_BYTES) {
414
+ return { name: 'Guidance Gates', status: 'warn', message: 'settings.json too large to parse' };
415
+ }
416
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
417
+ const preToolUse = settings?.hooks?.PreToolUse ?? [];
418
+ const hasPreWrite = preToolUse.some(e => e.matcher === 'Write|Edit|MultiEdit' && e.hooks.some(h => h.command?.includes('pre-write')));
419
+ const hasPreBash = preToolUse.some(e => e.matcher === 'Bash' && e.hooks.some(h => h.command?.includes('pre-bash')));
420
+ if (!hasPreWrite && !hasPreBash)
421
+ return { name: 'Guidance Gates', status: 'warn', message: 'gates-handler.cjs present but no gates registered', fix: 'monomind guidance setup' };
422
+ if (!hasPreWrite)
423
+ return { name: 'Guidance Gates', status: 'warn', message: 'pre-write hook not registered — secrets gate inactive', fix: 'monomind guidance setup' };
424
+ if (!hasPreBash)
425
+ return { name: 'Guidance Gates', status: 'warn', message: 'pre-bash hook not registered — destructive-ops gate inactive', fix: 'monomind guidance setup' };
426
+ return { name: 'Guidance Gates', status: 'pass', message: 'destructive-ops (pre-bash) + secrets (pre-write) gates active' };
427
+ }
428
+ catch {
429
+ return { name: 'Guidance Gates', status: 'warn', message: 'Could not parse .claude/settings.json', fix: 'monomind guidance setup --force' };
430
+ }
431
+ }
432
+ //# sourceMappingURL=doctor-project-checks.js.map