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
|
@@ -761,6 +761,59 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
761
761
|
},
|
|
762
762
|
scannedAt: Date.now(),
|
|
763
763
|
};
|
|
764
|
+
// Enrich with monograph graph stats for LLM context injection.
|
|
765
|
+
// Lazy-import to avoid hard dependency; silently skip on any error.
|
|
766
|
+
try {
|
|
767
|
+
const { openDb, closeDb, countNodes, countEdges } = await import('@monoes/monograph');
|
|
768
|
+
const dbPath = join(this.projectRoot, '.monomind', 'monograph.db');
|
|
769
|
+
if (existsSync(dbPath)) {
|
|
770
|
+
const db = openDb(dbPath);
|
|
771
|
+
try {
|
|
772
|
+
// Node/edge counts
|
|
773
|
+
map['graph'] = {
|
|
774
|
+
nodes: countNodes(db),
|
|
775
|
+
edges: countEdges(db),
|
|
776
|
+
};
|
|
777
|
+
// Top 3 god nodes (high degree internal files) — same SQL as monograph_god_nodes tool
|
|
778
|
+
const excluded = ['File', 'Folder', 'Community', 'Concept'];
|
|
779
|
+
const rows = db.prepare(`
|
|
780
|
+
SELECT n.name, n.file_path, n.start_line,
|
|
781
|
+
COUNT(DISTINCT e1.id) + COUNT(DISTINCT e2.id) AS degree
|
|
782
|
+
FROM nodes n
|
|
783
|
+
LEFT JOIN edges e1 ON e1.source_id = n.id
|
|
784
|
+
LEFT JOIN edges e2 ON e2.target_id = n.id
|
|
785
|
+
WHERE n.label NOT IN (${excluded.map(() => '?').join(',')})
|
|
786
|
+
GROUP BY n.id HAVING degree > 0
|
|
787
|
+
ORDER BY degree DESC LIMIT 3
|
|
788
|
+
`).all(...excluded);
|
|
789
|
+
if (rows.length > 0) {
|
|
790
|
+
map['topFiles'] = rows.map(r => ({
|
|
791
|
+
ref: r.file_path
|
|
792
|
+
? (r.start_line != null ? `${r.file_path}:${r.start_line}` : r.file_path)
|
|
793
|
+
: r.name,
|
|
794
|
+
degree: r.degree,
|
|
795
|
+
}));
|
|
796
|
+
}
|
|
797
|
+
// Index staleness via git — same approach as monograph_health tool
|
|
798
|
+
try {
|
|
799
|
+
const { execSync } = await import('child_process');
|
|
800
|
+
const lastHash = db.prepare("SELECT value FROM meta WHERE key = 'last_commit_hash' LIMIT 1").get()?.value;
|
|
801
|
+
if (lastHash) {
|
|
802
|
+
const countOut = execSync(`git -C ${JSON.stringify(this.projectRoot)} rev-list --count ${lastHash}..HEAD`, { timeout: 5000 }).toString().trim();
|
|
803
|
+
const commitsBehind = parseInt(countOut, 10);
|
|
804
|
+
if (!isNaN(commitsBehind)) {
|
|
805
|
+
map['graphStaleness'] = { commitsBehind };
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
catch { /* git unavailable — skip staleness */ }
|
|
810
|
+
}
|
|
811
|
+
finally {
|
|
812
|
+
closeDb(db);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
catch { /* monograph unavailable — skip graph enrichment */ }
|
|
764
817
|
const metricsFileTmp1 = metricsFile + '.tmp';
|
|
765
818
|
writeFileSync(metricsFileTmp1, JSON.stringify(map, null, 2));
|
|
766
819
|
renameSync(metricsFileTmp1, metricsFile);
|
|
@@ -788,6 +841,68 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
788
841
|
recommendations: [],
|
|
789
842
|
note: 'Install Claude Code CLI for AI-powered security analysis',
|
|
790
843
|
};
|
|
844
|
+
// Enrich with monograph high-centrality files and surprising cross-community edges.
|
|
845
|
+
// God-node files are high-value targets for security review: they are imported by
|
|
846
|
+
// many consumers, so a vulnerability there has the largest blast radius.
|
|
847
|
+
// Cross-community edges reveal unexpected coupling that may indicate hidden attack surfaces.
|
|
848
|
+
try {
|
|
849
|
+
const { openDb, closeDb } = await import('@monoes/monograph');
|
|
850
|
+
const dbPath = join(this.projectRoot, '.monomind', 'monograph.db');
|
|
851
|
+
if (existsSync(dbPath)) {
|
|
852
|
+
const db = openDb(dbPath);
|
|
853
|
+
try {
|
|
854
|
+
const godFileRows = db.prepare(`
|
|
855
|
+
SELECT n.file_path,
|
|
856
|
+
COUNT(DISTINCT e1.id) + COUNT(DISTINCT e2.id) AS degree
|
|
857
|
+
FROM nodes n
|
|
858
|
+
LEFT JOIN edges e1 ON e1.source_id = n.id
|
|
859
|
+
LEFT JOIN edges e2 ON e2.target_id = n.id
|
|
860
|
+
WHERE n.label NOT IN ('File','Folder','Community','Concept')
|
|
861
|
+
AND n.file_path IS NOT NULL
|
|
862
|
+
AND n.file_path NOT LIKE '%node_modules%'
|
|
863
|
+
AND n.file_path NOT LIKE '%/dist/%'
|
|
864
|
+
AND n.file_path NOT LIKE '%.test.%'
|
|
865
|
+
AND n.file_path NOT LIKE '%.spec.%'
|
|
866
|
+
GROUP BY n.file_path
|
|
867
|
+
ORDER BY degree DESC
|
|
868
|
+
LIMIT 5
|
|
869
|
+
`).all();
|
|
870
|
+
const surpriseRows = db.prepare(`
|
|
871
|
+
SELECT n1.name as src_name, n2.name as tgt_name, e.relation, e.confidence_score,
|
|
872
|
+
n1.file_path as src_file, n2.file_path as tgt_file
|
|
873
|
+
FROM edges e
|
|
874
|
+
JOIN nodes n1 ON n1.id = e.source_id
|
|
875
|
+
JOIN nodes n2 ON n2.id = e.target_id
|
|
876
|
+
WHERE e.confidence != 'EXTRACTED'
|
|
877
|
+
AND n1.community_id IS NOT NULL
|
|
878
|
+
AND n2.community_id IS NOT NULL
|
|
879
|
+
AND n1.community_id != n2.community_id
|
|
880
|
+
ORDER BY e.confidence_score ASC
|
|
881
|
+
LIMIT 5
|
|
882
|
+
`).all();
|
|
883
|
+
if (godFileRows.length > 0) {
|
|
884
|
+
audit['priorityScanTargets'] = godFileRows.map(r => ({
|
|
885
|
+
file: r.file_path.replace(this.projectRoot + '/', '').replace(this.projectRoot + '\\', ''),
|
|
886
|
+
degree: r.degree,
|
|
887
|
+
reason: 'high-centrality: vulnerability here affects the most consumers',
|
|
888
|
+
}));
|
|
889
|
+
}
|
|
890
|
+
if (surpriseRows.length > 0) {
|
|
891
|
+
audit['unexpectedCoupling'] = surpriseRows.map(r => ({
|
|
892
|
+
edge: `${r.src_name} --${r.relation}--> ${r.tgt_name}`,
|
|
893
|
+
srcFile: r.src_file ?? '(unknown)',
|
|
894
|
+
tgtFile: r.tgt_file ?? '(unknown)',
|
|
895
|
+
confidenceScore: r.confidence_score,
|
|
896
|
+
reason: 'cross-community edge: may indicate hidden dependency or attack surface',
|
|
897
|
+
}));
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
finally {
|
|
901
|
+
closeDb(db);
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
catch { /* monograph unavailable — skip graph enrichment */ }
|
|
791
906
|
const auditFileTmp = auditFile + '.tmp';
|
|
792
907
|
writeFileSync(auditFileTmp, JSON.stringify(audit, null, 2));
|
|
793
908
|
renameSync(auditFileTmp, auditFile);
|
|
@@ -927,13 +1042,95 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
927
1042
|
* Local ultralearn worker (fallback when headless unavailable)
|
|
928
1043
|
*/
|
|
929
1044
|
async runUltralearnWorkerLocal() {
|
|
930
|
-
|
|
1045
|
+
const result = {
|
|
931
1046
|
timestamp: new Date().toISOString(),
|
|
932
1047
|
mode: 'local',
|
|
933
1048
|
patternsLearned: 0,
|
|
934
1049
|
insightsGained: [],
|
|
935
|
-
note: 'Install Claude Code CLI for AI-powered deep learning',
|
|
936
1050
|
};
|
|
1051
|
+
// Enrich with monograph community clusters and bridge-node patterns for LLM context injection.
|
|
1052
|
+
// Bridge nodes (symbols that cross community boundaries) are architecturally significant —
|
|
1053
|
+
// they represent coupling points an LLM should be aware of when reasoning about change impact.
|
|
1054
|
+
try {
|
|
1055
|
+
const { openDb, closeDb, countNodes, countEdges } = await import('@monoes/monograph');
|
|
1056
|
+
const dbPath = join(this.projectRoot, '.monomind', 'monograph.db');
|
|
1057
|
+
if (existsSync(dbPath)) {
|
|
1058
|
+
const db = openDb(dbPath);
|
|
1059
|
+
try {
|
|
1060
|
+
const nodeCount = countNodes(db);
|
|
1061
|
+
const edgeCount = countEdges(db);
|
|
1062
|
+
const communityRows = db.prepare(`
|
|
1063
|
+
SELECT community_id, COUNT(*) AS member_count
|
|
1064
|
+
FROM nodes
|
|
1065
|
+
WHERE community_id IS NOT NULL
|
|
1066
|
+
AND label NOT IN ('File','Folder','Community','Concept')
|
|
1067
|
+
GROUP BY community_id
|
|
1068
|
+
ORDER BY member_count DESC
|
|
1069
|
+
LIMIT 5
|
|
1070
|
+
`).all();
|
|
1071
|
+
const bridgeRows = db.prepare(`
|
|
1072
|
+
SELECT n.name, n.label, n.file_path, n.start_line,
|
|
1073
|
+
COUNT(DISTINCT e.id) AS cross_edges
|
|
1074
|
+
FROM nodes n
|
|
1075
|
+
JOIN edges e ON (e.source_id = n.id OR e.target_id = n.id)
|
|
1076
|
+
JOIN nodes n2 ON (
|
|
1077
|
+
CASE WHEN e.source_id = n.id THEN e.target_id ELSE e.source_id END = n2.id
|
|
1078
|
+
)
|
|
1079
|
+
WHERE n.community_id IS NOT NULL
|
|
1080
|
+
AND n2.community_id IS NOT NULL
|
|
1081
|
+
AND n.community_id != n2.community_id
|
|
1082
|
+
AND n.label NOT IN ('File','Folder','Community','Concept')
|
|
1083
|
+
AND (n.file_path IS NULL OR (
|
|
1084
|
+
n.file_path NOT LIKE '%node_modules%'
|
|
1085
|
+
AND n.file_path NOT LIKE '%/dist/%'
|
|
1086
|
+
AND n.file_path NOT LIKE '%.test.%'
|
|
1087
|
+
AND n.file_path NOT LIKE '%.spec.%'
|
|
1088
|
+
))
|
|
1089
|
+
GROUP BY n.id
|
|
1090
|
+
ORDER BY cross_edges DESC
|
|
1091
|
+
LIMIT 5
|
|
1092
|
+
`).all();
|
|
1093
|
+
const insights = result['insightsGained'];
|
|
1094
|
+
if (communityRows.length > 0) {
|
|
1095
|
+
insights.push({
|
|
1096
|
+
category: 'community_clusters',
|
|
1097
|
+
description: `Top ${communityRows.length} community clusters by size`,
|
|
1098
|
+
items: communityRows.map(r => ({
|
|
1099
|
+
communityId: r.community_id,
|
|
1100
|
+
memberCount: r.member_count,
|
|
1101
|
+
})),
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
if (bridgeRows.length > 0) {
|
|
1105
|
+
insights.push({
|
|
1106
|
+
category: 'bridge_nodes',
|
|
1107
|
+
description: `Top ${bridgeRows.length} bridge nodes crossing community boundaries (high coupling risk)`,
|
|
1108
|
+
items: bridgeRows.map(r => {
|
|
1109
|
+
const loc = r.file_path
|
|
1110
|
+
? (r.start_line != null ? `${r.file_path}:${r.start_line}` : r.file_path)
|
|
1111
|
+
: '(unknown)';
|
|
1112
|
+
return {
|
|
1113
|
+
name: r.name,
|
|
1114
|
+
label: r.label,
|
|
1115
|
+
location: loc,
|
|
1116
|
+
crossCommunityEdges: r.cross_edges,
|
|
1117
|
+
};
|
|
1118
|
+
}),
|
|
1119
|
+
});
|
|
1120
|
+
result['patternsLearned'] = bridgeRows.length + communityRows.length;
|
|
1121
|
+
}
|
|
1122
|
+
result['graph'] = { nodes: nodeCount, edges: edgeCount };
|
|
1123
|
+
}
|
|
1124
|
+
finally {
|
|
1125
|
+
closeDb(db);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
result['note'] = 'Monograph index not built — run `monomind monograph build` for deep learning';
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
catch { /* monograph unavailable — return minimal result */ }
|
|
1133
|
+
return result;
|
|
937
1134
|
}
|
|
938
1135
|
/**
|
|
939
1136
|
* Local refactor worker (fallback when headless unavailable)
|
|
@@ -951,13 +1148,106 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
951
1148
|
* Local deepdive worker (fallback when headless unavailable)
|
|
952
1149
|
*/
|
|
953
1150
|
async runDeepdiveWorkerLocal() {
|
|
954
|
-
|
|
1151
|
+
const deepdiveFile = join(this.projectRoot, '.monomind', 'metrics', 'deepdive.json');
|
|
1152
|
+
const metricsDir = join(this.projectRoot, '.monomind', 'metrics');
|
|
1153
|
+
if (!existsSync(metricsDir)) {
|
|
1154
|
+
mkdirSync(metricsDir, { recursive: true });
|
|
1155
|
+
}
|
|
1156
|
+
const result = {
|
|
955
1157
|
timestamp: new Date().toISOString(),
|
|
956
1158
|
mode: 'local',
|
|
957
|
-
analysisDepth: '
|
|
1159
|
+
analysisDepth: 'graph',
|
|
958
1160
|
findings: [],
|
|
959
|
-
note: 'Install Claude Code CLI for AI-powered deep code analysis',
|
|
960
1161
|
};
|
|
1162
|
+
// Enrich with monograph god nodes and high-degree file analysis for LLM context injection.
|
|
1163
|
+
// Lazy-import to avoid hard dependency; silently skip on any error.
|
|
1164
|
+
try {
|
|
1165
|
+
const { openDb, closeDb, countNodes, countEdges } = await import('@monoes/monograph');
|
|
1166
|
+
const dbPath = join(this.projectRoot, '.monomind', 'monograph.db');
|
|
1167
|
+
if (existsSync(dbPath)) {
|
|
1168
|
+
const db = openDb(dbPath);
|
|
1169
|
+
try {
|
|
1170
|
+
const nodeCount = countNodes(db);
|
|
1171
|
+
const edgeCount = countEdges(db);
|
|
1172
|
+
const godNodeRows = db.prepare(`
|
|
1173
|
+
SELECT n.name, n.file_path, n.label,
|
|
1174
|
+
COUNT(DISTINCT e1.id) + COUNT(DISTINCT e2.id) AS degree
|
|
1175
|
+
FROM nodes n
|
|
1176
|
+
LEFT JOIN edges e1 ON e1.source_id = n.id
|
|
1177
|
+
LEFT JOIN edges e2 ON e2.target_id = n.id
|
|
1178
|
+
WHERE n.label NOT IN ('File','Folder','Community','Concept')
|
|
1179
|
+
AND (n.file_path IS NULL OR (
|
|
1180
|
+
n.file_path NOT LIKE '%node_modules%'
|
|
1181
|
+
AND n.file_path NOT LIKE '%/dist/%'
|
|
1182
|
+
AND n.file_path NOT LIKE '%.test.%'
|
|
1183
|
+
AND n.file_path NOT LIKE '%.spec.%'
|
|
1184
|
+
))
|
|
1185
|
+
GROUP BY n.id
|
|
1186
|
+
ORDER BY degree DESC
|
|
1187
|
+
LIMIT 5
|
|
1188
|
+
`).all();
|
|
1189
|
+
const godNodes = godNodeRows.map(r => ({
|
|
1190
|
+
name: r.name,
|
|
1191
|
+
label: r.label,
|
|
1192
|
+
file: r.file_path ?? '(unknown)',
|
|
1193
|
+
degree: r.degree,
|
|
1194
|
+
}));
|
|
1195
|
+
const fileRows = db.prepare(`
|
|
1196
|
+
SELECT n.file_path,
|
|
1197
|
+
COUNT(DISTINCT e2.id) AS in_deg,
|
|
1198
|
+
COUNT(DISTINCT e1.id) AS out_deg
|
|
1199
|
+
FROM nodes n
|
|
1200
|
+
LEFT JOIN edges e1 ON e1.source_id = n.id
|
|
1201
|
+
LEFT JOIN edges e2 ON e2.target_id = n.id
|
|
1202
|
+
WHERE n.label = 'File'
|
|
1203
|
+
AND n.file_path NOT LIKE '%node_modules%'
|
|
1204
|
+
AND n.file_path NOT LIKE '%/dist/%'
|
|
1205
|
+
AND n.file_path NOT LIKE '%.test.%'
|
|
1206
|
+
AND n.file_path NOT LIKE '%.spec.%'
|
|
1207
|
+
GROUP BY n.id
|
|
1208
|
+
ORDER BY (in_deg + out_deg) DESC
|
|
1209
|
+
LIMIT 5
|
|
1210
|
+
`).all();
|
|
1211
|
+
const highDegFiles = fileRows.map(r => ({
|
|
1212
|
+
file: r.file_path
|
|
1213
|
+
.replace(this.projectRoot + '/', '')
|
|
1214
|
+
.replace(this.projectRoot + '\\', ''),
|
|
1215
|
+
inDegree: r.in_deg,
|
|
1216
|
+
outDegree: r.out_deg,
|
|
1217
|
+
totalDegree: r.in_deg + r.out_deg,
|
|
1218
|
+
}));
|
|
1219
|
+
const findings = [];
|
|
1220
|
+
if (godNodes.length > 0) {
|
|
1221
|
+
findings.push({
|
|
1222
|
+
category: 'god_nodes',
|
|
1223
|
+
description: `Top ${godNodes.length} high-centrality symbols (most imported/referenced)`,
|
|
1224
|
+
items: godNodes,
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1227
|
+
if (highDegFiles.length > 0) {
|
|
1228
|
+
findings.push({
|
|
1229
|
+
category: 'high_degree_files',
|
|
1230
|
+
description: `Top ${highDegFiles.length} files by total edge degree (import + export connections)`,
|
|
1231
|
+
items: highDegFiles,
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
result['graph'] = { nodes: nodeCount, edges: edgeCount };
|
|
1235
|
+
result['findings'] = findings;
|
|
1236
|
+
result['analysisDepth'] = 'graph';
|
|
1237
|
+
}
|
|
1238
|
+
finally {
|
|
1239
|
+
closeDb(db);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
else {
|
|
1243
|
+
result['note'] = 'Monograph index not built — run `monomind monograph build` for deep analysis';
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
catch { /* monograph unavailable — return minimal result */ }
|
|
1247
|
+
const deepdiveFileTmp = deepdiveFile + '.tmp';
|
|
1248
|
+
writeFileSync(deepdiveFileTmp, JSON.stringify(result, null, 2));
|
|
1249
|
+
renameSync(deepdiveFileTmp, deepdiveFile);
|
|
1250
|
+
return result;
|
|
961
1251
|
}
|
|
962
1252
|
/**
|
|
963
1253
|
* Local benchmark worker
|
|
@@ -137,7 +137,7 @@ export function deserializeCFP(data) {
|
|
|
137
137
|
export function validateCFP(cfp) {
|
|
138
138
|
const errors = [];
|
|
139
139
|
if (cfp.magic !== 'CFP1') {
|
|
140
|
-
errors.push(
|
|
140
|
+
errors.push(`Invalid magic bytes: ${cfp.magic}`);
|
|
141
141
|
}
|
|
142
142
|
if (!cfp.version) {
|
|
143
143
|
errors.push('Missing version');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
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",
|