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.
@@ -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
- return {
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
- return {
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: 'shallow',
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('Invalid magic bytes');
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.12.0",
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",