monomind 1.12.0 → 1.14.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.
Files changed (43) hide show
  1. package/.claude/agents/generated/churn-analyst.md +53 -0
  2. package/.claude/agents/generated/code-reviewer.md +55 -0
  3. package/.claude/agents/generated/code-validator.md +57 -0
  4. package/.claude/agents/generated/complexity-scanner.md +56 -0
  5. package/.claude/agents/generated/devbot-orchestrator.md +58 -0
  6. package/.claude/agents/generated/devbot-planner.md +63 -0
  7. package/.claude/agents/generated/impact-assessor.md +54 -0
  8. package/.claude/commands/mastermind/master.md +88 -24
  9. package/.claude/helpers/control-start.cjs +60 -1
  10. package/.claude/helpers/event-logger.cjs +43 -2
  11. package/.claude/helpers/handlers/capture-handler.cjs +336 -0
  12. package/.claude/helpers/handlers/route-handler.cjs +20 -13
  13. package/.claude/helpers/handlers/session-restore-handler.cjs +14 -8
  14. package/.claude/helpers/hook-handler.cjs +57 -1
  15. package/.claude/helpers/intelligence.cjs +129 -57
  16. package/.claude/helpers/memory-palace.cjs +461 -0
  17. package/.claude/helpers/memory.cjs +134 -15
  18. package/.claude/helpers/metrics-db.mjs +87 -0
  19. package/.claude/helpers/router.cjs +296 -41
  20. package/.claude/helpers/session.cjs +107 -32
  21. package/.claude/helpers/statusline.cjs +138 -2
  22. package/.claude/helpers/toggle-statusline.cjs +73 -0
  23. package/.claude/helpers/token-tracker.cjs +934 -0
  24. package/.claude/helpers/utils/monograph.cjs +39 -4
  25. package/.claude/helpers/utils/telemetry.cjs +3 -3
  26. package/.claude/settings.json +10 -0
  27. package/.claude/skills/mastermind/createorg.md +227 -16
  28. package/.claude/skills/mastermind/idea.md +15 -3
  29. package/.claude/skills/mastermind/runorg.md +2 -1
  30. package/package.json +1 -1
  31. package/packages/@monomind/cli/dist/src/commands/doctor.js +96 -4
  32. package/packages/@monomind/cli/dist/src/commands/index.js +2 -0
  33. package/packages/@monomind/cli/dist/src/commands/org.d.ts +4 -0
  34. package/packages/@monomind/cli/dist/src/commands/org.js +93 -0
  35. package/packages/@monomind/cli/dist/src/mcp-tools/memory-tools.js +6 -6
  36. package/packages/@monomind/cli/dist/src/mcp-tools/monograph-tools.js +329 -37
  37. package/packages/@monomind/cli/dist/src/mcp-tools/session-tools.js +9 -10
  38. package/packages/@monomind/cli/dist/src/mcp-tools/task-tools.js +7 -8
  39. package/packages/@monomind/cli/dist/src/mcp-tools/types.d.ts +1 -0
  40. package/packages/@monomind/cli/dist/src/mcp-tools/types.js +49 -0
  41. package/packages/@monomind/cli/dist/src/services/worker-daemon.js +295 -5
  42. package/packages/@monomind/cli/dist/src/transfer/serialization/cfp.js +1 -1
  43. package/packages/@monomind/cli/package.json +1 -1
@@ -6,13 +6,12 @@
6
6
  import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync, statSync } from 'node:fs';
7
7
  import { join } from 'node:path';
8
8
  import { randomBytes } from 'node:crypto';
9
- import { getProjectCwd } from './types.js';
10
- // Storage paths
11
- const STORAGE_DIR = '.monomind';
9
+ import { getMonomindDataRoot } from './types.js';
10
+ // Storage paths — relative to the git-safe data root
12
11
  const TASK_DIR = 'tasks';
13
12
  const TASK_FILE = 'store.json';
14
13
  function getTaskDir() {
15
- return join(getProjectCwd(), STORAGE_DIR, TASK_DIR);
14
+ return join(getMonomindDataRoot(), TASK_DIR);
16
15
  }
17
16
  function getTaskPath() {
18
17
  return join(getTaskDir(), TASK_FILE);
@@ -248,7 +247,7 @@ export const taskTools = [
248
247
  saveTaskStore(store);
249
248
  // Sync assigned agents back to idle and increment taskCount
250
249
  if (task.assignedTo.length > 0) {
251
- const agentStorePath = join(getProjectCwd(), STORAGE_DIR, 'agents', 'store.json');
250
+ const agentStorePath = join(getMonomindDataRoot(), 'agents', 'store.json');
252
251
  try {
253
252
  let agentStore = { agents: {} };
254
253
  if (existsSync(agentStorePath) && statSync(agentStorePath).size <= MAX_TASK_STORE_BYTES) {
@@ -267,7 +266,7 @@ export const taskTools = [
267
266
  (agentStore.agents[agentId].taskCount || 0) + 1;
268
267
  }
269
268
  }
270
- const agentDir = join(getProjectCwd(), STORAGE_DIR, 'agents');
269
+ const agentDir = join(getMonomindDataRoot(), 'agents');
271
270
  if (!existsSync(agentDir))
272
271
  mkdirSync(agentDir, { recursive: true });
273
272
  const tmpAgent1 = `${agentStorePath}.${process.pid}.${Date.now()}.tmp`;
@@ -374,7 +373,7 @@ export const taskTools = [
374
373
  }
375
374
  const previouslyAssigned = [...task.assignedTo];
376
375
  // Load agent store to sync worker state
377
- const agentStorePath = join(getProjectCwd(), STORAGE_DIR, 'agents', 'store.json');
376
+ const agentStorePath = join(getMonomindDataRoot(), 'agents', 'store.json');
378
377
  let agentStore = { agents: {} };
379
378
  try {
380
379
  if (existsSync(agentStorePath) && statSync(agentStorePath).size <= MAX_TASK_STORE_BYTES) {
@@ -424,7 +423,7 @@ export const taskTools = [
424
423
  }
425
424
  saveTaskStore(store);
426
425
  // Save agent store
427
- const agentDir = join(getProjectCwd(), STORAGE_DIR, 'agents');
426
+ const agentDir = join(getMonomindDataRoot(), 'agents');
428
427
  if (!existsSync(agentDir)) {
429
428
  mkdirSync(agentDir, { recursive: true });
430
429
  }
@@ -23,6 +23,7 @@ export interface MCPToolResult {
23
23
  * where process.cwd() may resolve to '/') over the real process.cwd().
24
24
  */
25
25
  export declare function getProjectCwd(): string;
26
+ export declare function getMonomindDataRoot(cwd?: string): string;
26
27
  export interface MCPTool {
27
28
  name: string;
28
29
  description: string;
@@ -3,6 +3,8 @@
3
3
  *
4
4
  * Local type definitions to avoid external imports outside package boundary.
5
5
  */
6
+ import { statSync, readFileSync } from 'node:fs';
7
+ import { join, dirname, resolve } from 'node:path';
6
8
  /**
7
9
  * Returns the effective project working directory.
8
10
  * Prefers MONOMIND_CWD (set by the install script for global/MCP installs
@@ -11,4 +13,51 @@
11
13
  export function getProjectCwd() {
12
14
  return process.env.MONOMIND_CWD || process.cwd();
13
15
  }
16
+ /**
17
+ * Returns the stable Monomind data root that survives branch switches and is
18
+ * shared across all git worktrees of the same repository.
19
+ *
20
+ * Resolution order:
21
+ * 1. MONOMIND_DATA_DIR env var — allows overriding to e.g. ~/.monomind/<project>
22
+ * 2. <repo>/.git/monomind/ — regular repo (branch-agnostic, shared by design)
23
+ * 3. <main-repo>/.git/monomind/— worktree: .git is a pointer file → resolve to
24
+ * the shared .git dir of the main worktree
25
+ * 4. <cwd>/.monomind/ — fallback when git is unavailable
26
+ *
27
+ * Mirrors the _getGitMonomindDir() function in server.mjs so session, task,
28
+ * memory, and org data all land in the same stable location.
29
+ */
30
+ const _dataRootCache = new Map();
31
+ export function getMonomindDataRoot(cwd) {
32
+ if (process.env.MONOMIND_DATA_DIR)
33
+ return process.env.MONOMIND_DATA_DIR;
34
+ const workDir = cwd || getProjectCwd();
35
+ if (_dataRootCache.has(workDir))
36
+ return _dataRootCache.get(workDir);
37
+ let result;
38
+ try {
39
+ const gitEntry = join(workDir, '.git');
40
+ const st = statSync(gitEntry);
41
+ if (st.isDirectory()) {
42
+ result = join(gitEntry, 'monomind');
43
+ }
44
+ else {
45
+ // Worktree: .git is a text file "gitdir: /main/.git/worktrees/name"
46
+ const m = readFileSync(gitEntry, 'utf8').match(/^gitdir:\s*(.+)/m);
47
+ if (m) {
48
+ const worktreeDir = resolve(workDir, m[1].trim());
49
+ const commonGitDir = dirname(dirname(worktreeDir));
50
+ result = join(commonGitDir, 'monomind');
51
+ }
52
+ else {
53
+ result = join(workDir, '.monomind');
54
+ }
55
+ }
56
+ }
57
+ catch {
58
+ result = join(workDir, '.monomind');
59
+ }
60
+ _dataRootCache.set(workDir, result);
61
+ return result;
62
+ }
14
63
  //# sourceMappingURL=types.js.map
@@ -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.14.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",