@monoes/monomindcli 1.10.2 → 1.10.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monomindcli",
3
- "version": "1.10.2",
3
+ "version": "1.10.4",
4
4
  "type": "module",
5
5
  "description": "Monomind CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",
@@ -134,26 +134,42 @@ const ANTHROPIC_URL = 'https://api.anthropic.com/v1/messages';
134
134
  const MODEL = 'claude-haiku-4-5-20251001'; // cheapest for bulk analysis
135
135
 
136
136
  async function callClaude(systemPrompt, userPrompt, maxTokens = 1024) {
137
- const resp = await fetch(ANTHROPIC_URL, {
138
- method: 'POST',
139
- headers: {
140
- 'Content-Type': 'application/json',
141
- 'x-api-key': ANTHROPIC_API_KEY,
142
- 'anthropic-version': '2023-06-01',
143
- },
144
- body: JSON.stringify({
145
- model: MODEL,
146
- max_tokens: maxTokens,
147
- system: systemPrompt,
148
- messages: [{ role: 'user', content: userPrompt }],
149
- }),
137
+ const body = JSON.stringify({
138
+ model: MODEL,
139
+ max_tokens: maxTokens,
140
+ system: systemPrompt,
141
+ messages: [{ role: 'user', content: userPrompt }],
150
142
  });
151
- if (!resp.ok) {
152
- const text = await resp.text();
153
- throw new Error(`Anthropic API ${resp.status}: ${text.slice(0, 200)}`);
143
+ const headers = {
144
+ 'Content-Type': 'application/json',
145
+ 'x-api-key': ANTHROPIC_API_KEY,
146
+ 'anthropic-version': '2023-06-01',
147
+ };
148
+ let lastError;
149
+ for (let attempt = 0; attempt < 3; attempt++) {
150
+ try {
151
+ const resp = await fetch(ANTHROPIC_URL, { method: 'POST', headers, body });
152
+ if (resp.ok) {
153
+ const data = await resp.json();
154
+ return data.content?.[0]?.text ?? '';
155
+ }
156
+ // Retry on 429 (rate limit) and 5xx; fail fast on 4xx
157
+ if (resp.status === 429 || resp.status >= 500) {
158
+ const retryAfter = parseInt(resp.headers.get('retry-after') || '0', 10);
159
+ const backoff = retryAfter > 0 ? retryAfter * 1000 : Math.min(2 ** attempt * 1000, 8000);
160
+ await new Promise(r => setTimeout(r, backoff));
161
+ lastError = new Error(`Anthropic API ${resp.status} (attempt ${attempt + 1}/3)`);
162
+ continue;
163
+ }
164
+ const text = await resp.text();
165
+ throw new Error(`Anthropic API ${resp.status}: ${text.slice(0, 200)}`);
166
+ } catch (e) {
167
+ lastError = e;
168
+ if (attempt === 2) break;
169
+ await new Promise(r => setTimeout(r, Math.min(2 ** attempt * 1000, 4000)));
170
+ }
154
171
  }
155
- const data = await resp.json();
156
- return data.content?.[0]?.text ?? '';
172
+ throw lastError || new Error('Anthropic API failed after 3 attempts');
157
173
  }
158
174
 
159
175
  function parseJson(text) {
@@ -285,14 +301,25 @@ const isIgnoredByUser = makeIgnoreMatcher(_ignorePatterns);
285
301
 
286
302
  // ── Incremental mode helpers (ported from staleness.ts) ──────────────────────
287
303
  function getChangedFiles(dir, lastHash) {
288
- try {
289
- const out = execFileSync('git', ['diff', `${lastHash}..HEAD`, '--name-only'], {
290
- cwd: dir, encoding: 'utf-8',
291
- });
292
- return out.split('\n').map(l => l.trim()).filter(l => l.length > 0);
293
- } catch {
294
- return [];
304
+ const files = new Set();
305
+ function runGit(args) {
306
+ try {
307
+ const out = execFileSync('git', args, { cwd: dir, encoding: 'utf-8' });
308
+ for (const line of out.split('\n')) {
309
+ const trimmed = line.trim();
310
+ if (!trimmed) continue;
311
+ // `git status --porcelain` lines look like " M path/to/file" or "?? path"
312
+ // Strip the leading status code if present
313
+ const path = trimmed.length > 3 && trimmed[2] === ' ' ? trimmed.slice(3) : trimmed;
314
+ files.add(path);
315
+ }
316
+ } catch { /* git not available or no diff */ }
295
317
  }
318
+ // Committed changes since last run
319
+ runGit(['diff', `${lastHash}..HEAD`, '--name-only']);
320
+ // Uncommitted working tree changes (staged + unstaged + untracked)
321
+ runGit(['status', '--porcelain']);
322
+ return [...files];
296
323
  }
297
324
 
298
325
  function getCurrentCommitHash(dir) {
@@ -362,20 +389,47 @@ const FRAMEWORK_SIGNATURES = [
362
389
  ['Gin', 'go.mod', ['gin-gonic/gin']],
363
390
  ];
364
391
 
392
+ function findManifests(dir, manifestName, maxDepth = 4) {
393
+ const fs = createRequire(import.meta.url)('node:fs');
394
+ const results = [];
395
+ function walk(d, depth) {
396
+ if (depth > maxDepth) return;
397
+ let entries;
398
+ try { entries = fs.readdirSync(d, { withFileTypes: true }); }
399
+ catch { return; }
400
+ for (const e of entries) {
401
+ if (e.name === 'node_modules' || e.name === 'dist' || e.name.startsWith('.')) continue;
402
+ const full = join(d, e.name);
403
+ if (e.isDirectory()) walk(full, depth + 1);
404
+ else if (e.name === manifestName) results.push(full);
405
+ }
406
+ }
407
+ walk(dir, 0);
408
+ return results;
409
+ }
410
+
365
411
  function detectFrameworks(dir) {
366
412
  const detected = [];
367
413
  const seen = new Set();
414
+ // Cache manifest contents by name to avoid re-reading
415
+ const manifestCache = new Map();
368
416
  for (const [name, manifest, keywords] of FRAMEWORK_SIGNATURES) {
369
417
  if (seen.has(name)) continue;
370
- const p = join(dir, manifest);
371
- if (!existsSync(p)) continue;
372
- try {
373
- const content = readFileSync(p, 'utf-8').toLowerCase();
374
- if (keywords.some(k => content.includes(k.toLowerCase()))) {
375
- detected.push(name);
376
- seen.add(name);
377
- }
378
- } catch {}
418
+ let manifestPaths = manifestCache.get(manifest);
419
+ if (!manifestPaths) {
420
+ manifestPaths = findManifests(dir, manifest);
421
+ manifestCache.set(manifest, manifestPaths);
422
+ }
423
+ for (const p of manifestPaths) {
424
+ try {
425
+ const content = readFileSync(p, 'utf-8').toLowerCase();
426
+ if (keywords.some(k => content.includes(k.toLowerCase()))) {
427
+ detected.push(name);
428
+ seen.add(name);
429
+ break;
430
+ }
431
+ } catch {}
432
+ }
379
433
  }
380
434
  return detected;
381
435
  }
@@ -436,13 +490,20 @@ function buildOnboardingGuide(graphJson) {
436
490
  }
437
491
  }
438
492
 
493
+ const FILE_MAP_LIMIT = 50;
494
+ const HOTSPOT_LIMIT = 20;
495
+
439
496
  const fileNodes = nodes.filter(n => n.type === 'file' && n.filePath && n.summary);
440
497
  if (fileNodes.length > 0) {
441
498
  lines.push('## File Map');
442
499
  lines.push('');
500
+ if (fileNodes.length > FILE_MAP_LIMIT) {
501
+ lines.push(`Showing ${FILE_MAP_LIMIT} of ${fileNodes.length} analyzed files. See \`.understand/knowledge-graph.json\` for the full list.`);
502
+ lines.push('');
503
+ }
443
504
  lines.push('| File | Purpose | Complexity |');
444
505
  lines.push('|------|---------|------------|');
445
- for (const node of fileNodes) {
506
+ for (const node of fileNodes.slice(0, FILE_MAP_LIMIT)) {
446
507
  const summary = (node.summary || '').replace(/\|/g, '\\|');
447
508
  lines.push(`| \`${node.filePath}\` | ${summary} | ${node.complexity || 'moderate'} |`);
448
509
  }
@@ -454,8 +515,12 @@ function buildOnboardingGuide(graphJson) {
454
515
  lines.push('## Complexity Hotspots');
455
516
  lines.push('');
456
517
  lines.push('These components are the most complex and deserve extra attention:');
518
+ if (complexNodes.length > HOTSPOT_LIMIT) {
519
+ lines.push('');
520
+ lines.push(`Showing top ${HOTSPOT_LIMIT} of ${complexNodes.length} complex components.`);
521
+ }
457
522
  lines.push('');
458
- for (const node of complexNodes) {
523
+ for (const node of complexNodes.slice(0, HOTSPOT_LIMIT)) {
459
524
  lines.push(`- **${node.name}** (${node.type}): ${node.summary || ''}`);
460
525
  }
461
526
  lines.push('');
@@ -508,7 +573,18 @@ async function main() {
508
573
  process.exit(1);
509
574
  }
510
575
 
511
- const db = mg.openDb(dbPathArg);
576
+ // Retry openDb against SQLite BUSY when monograph is building in the background
577
+ let db;
578
+ for (let attempt = 0; attempt < 5; attempt++) {
579
+ try { db = mg.openDb(dbPathArg); break; }
580
+ catch (e) {
581
+ if (attempt === 4) throw e;
582
+ const msg = String(e?.message || e);
583
+ if (!/busy|locked/i.test(msg)) throw e;
584
+ console.log(`[understand] monograph.db busy, retrying in ${(attempt + 1) * 2}s...`);
585
+ await new Promise(r => setTimeout(r, (attempt + 1) * 2000));
586
+ }
587
+ }
512
588
 
513
589
  // Ensure properties column exists
514
590
  try { db.prepare(`ALTER TABLE nodes ADD COLUMN properties TEXT`).run(); } catch {}
@@ -702,8 +778,11 @@ async function main() {
702
778
  }
703
779
 
704
780
  // ── Final report ─────────────────────────────────────────────────────────
781
+ const title = dryRun
782
+ ? '║ /monomind:understand — DRY RUN (no writes) ║'
783
+ : '║ /monomind:understand — Enrichment Complete ║';
705
784
  console.log('\n╔══════════════════════════════════════════════════╗');
706
- console.log('║ /monomind:understand — Enrichment Complete ║');
785
+ console.log(title);
707
786
  console.log('╠══════════════════════════════════════════════════╣');
708
787
  console.log(`║ DB: ${relative(CWD, dbPathArg).padEnd(31)}║`);
709
788
  console.log(`║ Nodes enriched: ${String(written).padEnd(31)}║`);