monomind 1.12.0 → 1.13.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/.claude/helpers/handlers/route-handler.cjs +11 -4
- package/.claude/helpers/handlers/session-restore-handler.cjs +14 -8
- package/.claude/helpers/hook-handler.cjs +40 -0
- package/.claude/helpers/intelligence.cjs +129 -57
- package/.claude/helpers/memory-palace.cjs +461 -0
- package/.claude/helpers/memory.cjs +134 -15
- package/.claude/helpers/metrics-db.mjs +87 -0
- package/.claude/helpers/router.cjs +296 -41
- package/.claude/helpers/session.cjs +89 -32
- package/.claude/helpers/statusline.cjs +138 -2
- package/.claude/helpers/toggle-statusline.cjs +73 -0
- package/.claude/helpers/token-tracker.cjs +934 -0
- package/.claude/helpers/utils/monograph.cjs +39 -4
- package/.claude/helpers/utils/telemetry.cjs +3 -3
- package/package.json +1 -1
- package/packages/@monomind/cli/dist/src/commands/doctor.js +96 -4
- package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +329 -37
- package/packages/@monomind/cli/dist/src/services/worker-daemon.js +295 -5
- package/packages/@monomind/cli/dist/src/transfer/serialization/cfp.js +1 -1
- package/packages/@monomind/cli/package.json +1 -1
|
@@ -25,6 +25,30 @@ function _requireMonograph() {
|
|
|
25
25
|
// Memoized at module scope — opening monograph.db can take 7-10s.
|
|
26
26
|
// Callers MUST NOT close the returned handle.
|
|
27
27
|
var _cachedMonographDb = undefined;
|
|
28
|
+
|
|
29
|
+
// LRU cache for getMonographSuggestions: avoids re-querying the DB for
|
|
30
|
+
// the same task text within a single hook execution process lifetime.
|
|
31
|
+
// Max 20 entries; evicts the least-recently-used on overflow.
|
|
32
|
+
var _suggestCache = { _map: Object.create(null), _order: [], _max: 20 };
|
|
33
|
+
function _suggestCacheGet(key) {
|
|
34
|
+
if (key in _suggestCache._map) {
|
|
35
|
+
// Move to end (most recently used)
|
|
36
|
+
var idx = _suggestCache._order.indexOf(key);
|
|
37
|
+
if (idx !== -1) { _suggestCache._order.splice(idx, 1); _suggestCache._order.push(key); }
|
|
38
|
+
return _suggestCache._map[key];
|
|
39
|
+
}
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
function _suggestCacheSet(key, value) {
|
|
43
|
+
if (!(key in _suggestCache._map)) {
|
|
44
|
+
if (_suggestCache._order.length >= _suggestCache._max) {
|
|
45
|
+
var evict = _suggestCache._order.shift();
|
|
46
|
+
delete _suggestCache._map[evict];
|
|
47
|
+
}
|
|
48
|
+
_suggestCache._order.push(key);
|
|
49
|
+
}
|
|
50
|
+
_suggestCache._map[key] = value;
|
|
51
|
+
}
|
|
28
52
|
function _openMonographDb() {
|
|
29
53
|
if (_cachedMonographDb !== undefined) return _cachedMonographDb;
|
|
30
54
|
try {
|
|
@@ -39,6 +63,10 @@ function _openMonographDb() {
|
|
|
39
63
|
|
|
40
64
|
function getMonographSuggestions(taskText, limit) {
|
|
41
65
|
if (!taskText || typeof taskText !== 'string') return [];
|
|
66
|
+
// Fast path: return cached result for repeated identical queries.
|
|
67
|
+
var cacheKey = taskText.slice(0, 200) + '|' + (limit || 5);
|
|
68
|
+
var cached = _suggestCacheGet(cacheKey);
|
|
69
|
+
if (cached !== undefined) return cached;
|
|
42
70
|
var db = _openMonographDb();
|
|
43
71
|
if (!db) return [];
|
|
44
72
|
try {
|
|
@@ -56,7 +84,7 @@ function getMonographSuggestions(taskText, limit) {
|
|
|
56
84
|
var rows = [];
|
|
57
85
|
try {
|
|
58
86
|
rows = db.prepare(
|
|
59
|
-
'SELECT n.id, n.name, n.label, n.file_path AS file, ' +
|
|
87
|
+
'SELECT n.id, n.name, n.label, n.file_path AS file, n.start_line AS startLine, ' +
|
|
60
88
|
'bm25(nodes_fts) AS bm25_score, ' +
|
|
61
89
|
'(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg, ' +
|
|
62
90
|
'CASE n.label WHEN \'File\' THEN 3 WHEN \'Function\' THEN 3 WHEN \'Class\' THEN 3 ' +
|
|
@@ -72,7 +100,7 @@ function getMonographSuggestions(taskText, limit) {
|
|
|
72
100
|
var likeFrag = keys.map(function(){ return 'lower(n.name) LIKE ?'; }).join(' OR ');
|
|
73
101
|
var likeArgs = keys.map(function(k){ return '%' + k + '%'; });
|
|
74
102
|
var stmt = db.prepare(
|
|
75
|
-
'SELECT n.id, n.name, n.label, n.file_path AS file, ' +
|
|
103
|
+
'SELECT n.id, n.name, n.label, n.file_path AS file, n.start_line AS startLine, ' +
|
|
76
104
|
'(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg ' +
|
|
77
105
|
'FROM nodes n WHERE (' + likeFrag + ') AND n.file_path IS NOT NULL AND n.file_path != \'\' ' +
|
|
78
106
|
'AND n.label NOT IN (\'Concept\') ' +
|
|
@@ -80,7 +108,9 @@ function getMonographSuggestions(taskText, limit) {
|
|
|
80
108
|
);
|
|
81
109
|
rows = stmt.all.apply(stmt, likeArgs.concat([lim]));
|
|
82
110
|
}
|
|
83
|
-
|
|
111
|
+
var result = rows || [];
|
|
112
|
+
_suggestCacheSet(cacheKey, result);
|
|
113
|
+
return result;
|
|
84
114
|
} catch (e) { return []; }
|
|
85
115
|
finally { /* db is shared/cached; do not close */ }
|
|
86
116
|
}
|
|
@@ -287,8 +317,13 @@ function injectGodNodesContext(CWD) {
|
|
|
287
317
|
// Staleness indicator: compare stored commit hash with current HEAD.
|
|
288
318
|
var staleIndicator = '';
|
|
289
319
|
try {
|
|
320
|
+
// The orchestrator writes 'last_commit_hash'; fall back to legacy keys.
|
|
290
321
|
var lastCommitRow = null;
|
|
291
|
-
try {
|
|
322
|
+
try {
|
|
323
|
+
lastCommitRow = db.prepare("SELECT value FROM index_meta WHERE key='last_commit_hash'").get() ||
|
|
324
|
+
db.prepare("SELECT value FROM index_meta WHERE key='lastCommit'").get() ||
|
|
325
|
+
db.prepare("SELECT value FROM index_meta WHERE key='ua_last_commit'").get();
|
|
326
|
+
} catch (_) {}
|
|
292
327
|
if (lastCommitRow && lastCommitRow.value) {
|
|
293
328
|
var { execFileSync: execSync } = require('child_process');
|
|
294
329
|
var currentHead = '';
|
|
@@ -10,14 +10,14 @@ const CWD = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
|
10
10
|
function _recordRecentEdit(filePath) {
|
|
11
11
|
if (!filePath) return;
|
|
12
12
|
try {
|
|
13
|
-
var
|
|
13
|
+
var storedPath = filePath;
|
|
14
14
|
var f = path.join(CWD, '.monomind', 'metrics', 'recent-edits.json');
|
|
15
15
|
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
16
16
|
var d = { edits: [] };
|
|
17
17
|
try { d = JSON.parse(fs.readFileSync(f, 'utf-8')); } catch (_) {}
|
|
18
18
|
if (!Array.isArray(d.edits)) d.edits = [];
|
|
19
|
-
d.edits = d.edits.filter(function(e) { return e.file !==
|
|
20
|
-
d.edits.unshift({ file:
|
|
19
|
+
d.edits = d.edits.filter(function(e) { return e.file !== storedPath; });
|
|
20
|
+
d.edits.unshift({ file: storedPath, editedAt: Date.now() });
|
|
21
21
|
if (d.edits.length > 10) d.edits = d.edits.slice(0, 10);
|
|
22
22
|
fs.writeFileSync(f, JSON.stringify(d));
|
|
23
23
|
} catch (e) { /* non-fatal */ }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monomind",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Monomind - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -436,6 +436,84 @@ async function checkMonograph() {
|
|
|
436
436
|
};
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
|
+
// Check monograph graph freshness (is the graph built? how stale?)
|
|
440
|
+
async function checkMonographFreshness() {
|
|
441
|
+
try {
|
|
442
|
+
const cwd = process.cwd();
|
|
443
|
+
const dbPath = join(cwd, '.monomind', 'monograph.db');
|
|
444
|
+
const lockPath = join(cwd, '.monomind', 'graph', '.rebuild-lock');
|
|
445
|
+
const statsPath = join(cwd, '.monomind', 'graph', 'stats.json');
|
|
446
|
+
// Check if graph exists at all
|
|
447
|
+
const hasDb = existsSync(dbPath);
|
|
448
|
+
const hasLock = existsSync(lockPath);
|
|
449
|
+
const hasStats = existsSync(statsPath);
|
|
450
|
+
if (!hasDb && !hasStats) {
|
|
451
|
+
return {
|
|
452
|
+
name: 'Graph freshness',
|
|
453
|
+
status: 'warn',
|
|
454
|
+
message: 'No monograph graph built yet',
|
|
455
|
+
fix: 'mcp__monomind__monograph_build codeOnly:true — or run npx monomind@latest hooks graph-status',
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
// Determine last build time
|
|
459
|
+
let buildMs = 0;
|
|
460
|
+
if (hasDb) {
|
|
461
|
+
try {
|
|
462
|
+
buildMs = Math.max(buildMs, statSync(dbPath).mtimeMs);
|
|
463
|
+
}
|
|
464
|
+
catch { /* ignore */ }
|
|
465
|
+
}
|
|
466
|
+
if (hasLock) {
|
|
467
|
+
try {
|
|
468
|
+
buildMs = Math.max(buildMs, statSync(lockPath).mtimeMs);
|
|
469
|
+
}
|
|
470
|
+
catch { /* ignore */ }
|
|
471
|
+
}
|
|
472
|
+
if (hasStats) {
|
|
473
|
+
try {
|
|
474
|
+
buildMs = Math.max(buildMs, statSync(statsPath).mtimeMs);
|
|
475
|
+
}
|
|
476
|
+
catch { /* ignore */ }
|
|
477
|
+
}
|
|
478
|
+
if (buildMs === 0) {
|
|
479
|
+
return { name: 'Graph freshness', status: 'warn', message: 'Graph exists but build time unknown' };
|
|
480
|
+
}
|
|
481
|
+
// Count commits since last build
|
|
482
|
+
const buildIso = new Date(buildMs).toISOString();
|
|
483
|
+
let commitsBehind = 0;
|
|
484
|
+
try {
|
|
485
|
+
const out = execSync(`git rev-list --count --since='${buildIso}' HEAD 2>/dev/null`, {
|
|
486
|
+
encoding: 'utf8', timeout: 2000, cwd,
|
|
487
|
+
}).trim();
|
|
488
|
+
commitsBehind = parseInt(out, 10) || 0;
|
|
489
|
+
}
|
|
490
|
+
catch { /* git not available or not a git repo */ }
|
|
491
|
+
const ageMinutes = Math.floor((Date.now() - buildMs) / 60000);
|
|
492
|
+
const ageStr = ageMinutes < 60 ? `${ageMinutes}m ago` : `${Math.floor(ageMinutes / 60)}h ago`;
|
|
493
|
+
if (commitsBehind === 0) {
|
|
494
|
+
return { name: 'Graph freshness', status: 'pass', message: `FRESH — built ${ageStr}, 0 commits behind` };
|
|
495
|
+
}
|
|
496
|
+
else if (commitsBehind <= 5) {
|
|
497
|
+
return {
|
|
498
|
+
name: 'Graph freshness',
|
|
499
|
+
status: 'warn',
|
|
500
|
+
message: `${commitsBehind} commit(s) behind — built ${ageStr}`,
|
|
501
|
+
fix: 'mcp__monomind__monograph_build codeOnly:true',
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
return {
|
|
506
|
+
name: 'Graph freshness',
|
|
507
|
+
status: 'fail',
|
|
508
|
+
message: `STALE — ${commitsBehind} commits behind (built ${ageStr})`,
|
|
509
|
+
fix: 'mcp__monomind__monograph_build codeOnly:true',
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
return { name: 'Graph freshness', status: 'warn', message: 'Could not check graph freshness' };
|
|
515
|
+
}
|
|
516
|
+
}
|
|
439
517
|
// Check @monoes/memory (optional HNSW vector search package)
|
|
440
518
|
async function checkMonoesMemory() {
|
|
441
519
|
try {
|
|
@@ -798,7 +876,7 @@ export const doctorCommand = {
|
|
|
798
876
|
{
|
|
799
877
|
name: 'component',
|
|
800
878
|
short: 'c',
|
|
801
|
-
description: 'Check specific component (version, node, npm, config, daemon, memory, api, git, mcp, claude, disk, typescript, monograph, memory-pkg, helpers, agentic-flow, monoes, gates, gitignore)',
|
|
879
|
+
description: 'Check specific component (version, node, npm, config, daemon, memory, api, git, mcp, claude, disk, typescript, monograph, graph-freshness, memory-pkg, helpers, agentic-flow, monoes, gates, gitignore)',
|
|
802
880
|
type: 'string'
|
|
803
881
|
},
|
|
804
882
|
{
|
|
@@ -841,6 +919,7 @@ export const doctorCommand = {
|
|
|
841
919
|
checkDiskSpace,
|
|
842
920
|
checkBuildTools,
|
|
843
921
|
checkMonograph,
|
|
922
|
+
checkMonographFreshness,
|
|
844
923
|
checkMonoesMemory,
|
|
845
924
|
checkHelpersFresh,
|
|
846
925
|
checkAgenticFlow,
|
|
@@ -863,6 +942,7 @@ export const doctorCommand = {
|
|
|
863
942
|
'disk': checkDiskSpace,
|
|
864
943
|
'typescript': checkBuildTools,
|
|
865
944
|
'monograph': checkMonograph,
|
|
945
|
+
'graph-freshness': checkMonographFreshness,
|
|
866
946
|
'memory-pkg': checkMonoesMemory,
|
|
867
947
|
'helpers': checkHelpersFresh,
|
|
868
948
|
'agentic-flow': checkAgenticFlow,
|
|
@@ -889,6 +969,14 @@ export const doctorCommand = {
|
|
|
889
969
|
const result = settledResult.value;
|
|
890
970
|
results.push(result);
|
|
891
971
|
output.writeln(formatCheck(result));
|
|
972
|
+
if (result.fix && result.status === 'fail') {
|
|
973
|
+
// Always show fix inline for failures — no flag needed
|
|
974
|
+
output.writeln(output.dim(` Fix: ${result.fix}`));
|
|
975
|
+
}
|
|
976
|
+
else if (result.fix && result.status === 'warn') {
|
|
977
|
+
// Show fix inline for warnings too, so users don't need --fix for common issues
|
|
978
|
+
output.writeln(output.dim(` Hint: ${result.fix}`));
|
|
979
|
+
}
|
|
892
980
|
if (result.fix && (result.status === 'fail' || result.status === 'warn')) {
|
|
893
981
|
fixes.push(`${result.name}: ${result.fix}`);
|
|
894
982
|
}
|
|
@@ -949,9 +1037,13 @@ export const doctorCommand = {
|
|
|
949
1037
|
output.writeln(output.dim(` ${fix}`));
|
|
950
1038
|
}
|
|
951
1039
|
}
|
|
952
|
-
else if (
|
|
953
|
-
|
|
954
|
-
|
|
1040
|
+
else if (!showFix) {
|
|
1041
|
+
// Only nudge about --fix for warnings (failures already showed their fix inline)
|
|
1042
|
+
const warnFixes = results.filter(r => r.status === 'warn' && r.fix).length;
|
|
1043
|
+
if (warnFixes > 0) {
|
|
1044
|
+
output.writeln();
|
|
1045
|
+
output.writeln(output.dim(`Run with --fix to see ${warnFixes} suggested fix${warnFixes > 1 ? 'es' : ''} for warnings`));
|
|
1046
|
+
}
|
|
955
1047
|
}
|
|
956
1048
|
// Overall result
|
|
957
1049
|
if (failed > 0) {
|