@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 +1 -1
- package/scripts/understand-analyze.mjs +117 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "1.10.
|
|
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
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
-
|
|
371
|
-
if (!
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
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(
|
|
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)}║`);
|