monomind 1.10.27 → 1.10.29

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.
@@ -199,13 +199,14 @@ function _injectCompactGraphMap() {
199
199
  }
200
200
 
201
201
  // 2. Fill remaining slots (up to 8 total) with god nodes (high centrality).
202
- var slotsLeft = 8 - anchors.length;
203
- if (slotsLeft > 0) {
202
+ // Exclude node_modules paths (external typings like `Path [Interface]` skew rankings).
203
+ if (anchors.length < 8) {
204
204
  var gods = db.prepare(
205
205
  "SELECT n.name, n.label, n.file_path, " +
206
206
  "(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg " +
207
207
  "FROM nodes n " +
208
208
  "WHERE n.label NOT IN ('Concept') AND n.file_path IS NOT NULL AND n.file_path != '' " +
209
+ "AND n.file_path NOT LIKE '%/node_modules/%' AND n.file_path NOT LIKE '%node_modules%' " +
209
210
  "AND n.name NOT LIKE '(%' AND n.name NOT LIKE '%=>%' AND length(n.name) >= 3 " +
210
211
  "ORDER BY deg DESC LIMIT 15"
211
212
  ).all();
@@ -1731,13 +1732,37 @@ const handlers = {
1731
1732
  "FROM nodes n " +
1732
1733
  "WHERE n.label NOT IN ('Concept') " +
1733
1734
  "AND n.file_path IS NOT NULL AND n.file_path != '' " +
1735
+ "AND n.file_path NOT LIKE '%/node_modules/%' AND n.file_path NOT LIKE '%node_modules%' " +
1734
1736
  "ORDER BY deg DESC LIMIT 12"
1735
1737
  ).all();
1738
+
1739
+ // Check graph staleness: compare stored commit hash with current HEAD.
1740
+ var mgStaleIndicator = '';
1741
+ try {
1742
+ var mgLastCommitRow = null;
1743
+ try { mgLastCommitRow = mgDb.prepare("SELECT value FROM index_meta WHERE key='ua_last_commit'").get(); } catch (_) {}
1744
+ if (mgLastCommitRow && mgLastCommitRow.value) {
1745
+ var { execFileSync: mgExec } = require('child_process');
1746
+ var currentHead = '';
1747
+ try { currentHead = mgExec('git', ['rev-parse', 'HEAD'], { cwd: CWD, encoding: 'utf-8' }).trim(); } catch (_) {}
1748
+ if (currentHead && currentHead !== mgLastCommitRow.value) {
1749
+ var commitsBehind = 0;
1750
+ try {
1751
+ var revList = mgExec('git', ['rev-list', '--count', mgLastCommitRow.value + '..' + currentHead], { cwd: CWD, encoding: 'utf-8' }).trim();
1752
+ commitsBehind = parseInt(revList, 10) || 0;
1753
+ } catch (_) {}
1754
+ if (commitsBehind > 0) {
1755
+ mgStaleIndicator = ' [⚡ graph ' + commitsBehind + ' commit' + (commitsBehind === 1 ? '' : 's') + ' behind — run: npx monomind monograph build]';
1756
+ }
1757
+ }
1758
+ }
1759
+ } catch (_) {}
1760
+
1736
1761
  if (mgGodNodes.length > 0) {
1737
1762
  var mgGodStr = mgGodNodes.slice(0, 8).map(function(n) {
1738
1763
  return n.name + ' (' + n.label + ', ' + n.deg + ' links)';
1739
1764
  }).join(', ');
1740
- console.log('[MONOGRAPH_CONTEXT] ' + mgNodeCount + ' nodes · ' + mgEdgeCount + ' edges. Key nodes: ' + mgGodStr);
1765
+ console.log('[MONOGRAPH_CONTEXT] ' + mgNodeCount + ' nodes · ' + mgEdgeCount + ' edges. Key nodes: ' + mgGodStr + mgStaleIndicator);
1741
1766
  // Write god nodes into knowledge chunks so semantic search finds them
1742
1767
  var mgKnowledgeDir = path.join(CWD, '.monomind', 'knowledge');
1743
1768
  var mgChunksFile = path.join(mgKnowledgeDir, 'chunks.jsonl');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monomind",
3
- "version": "1.10.27",
3
+ "version": "1.10.29",
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",
@@ -689,8 +689,29 @@ const upgradeCommand = {
689
689
  output.writeln();
690
690
  let succeeded = 0;
691
691
  let failed = 0;
692
+ // Try to read control URL for dashboard progress events (best-effort)
693
+ let controlUrl = 'http://localhost:4242';
694
+ try {
695
+ const ctrlCfg = JSON.parse(fs.readFileSync(path.join(process.cwd(), '.monomind', 'control.json'), 'utf-8'));
696
+ if (ctrlCfg.url)
697
+ controlUrl = ctrlCfg.url;
698
+ }
699
+ catch { }
700
+ const emitUpgradeProgress = async (projDir, status, current, total) => {
701
+ try {
702
+ const { default: http } = await import('http');
703
+ const payload = JSON.stringify({ type: 'upgrade:progress', project: projDir, status, current, total, ts: Date.now() });
704
+ const url = new URL(controlUrl + '/api/mastermind/event');
705
+ const req = http.request({ hostname: url.hostname, port: parseInt(url.port || '4242'), path: url.pathname, method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) } });
706
+ req.write(payload);
707
+ req.end();
708
+ req.on('error', () => { });
709
+ }
710
+ catch { }
711
+ };
692
712
  for (const projDir of projects) {
693
- const spinner = output.createSpinner({ text: projDir });
713
+ const projIdx = projects.indexOf(projDir) + 1;
714
+ const spinner = output.createSpinner({ text: `[${projIdx}/${projects.length}] ${projDir}` });
694
715
  spinner.start();
695
716
  try {
696
717
  const res = addMissing
@@ -699,15 +720,18 @@ const upgradeCommand = {
699
720
  if (res.success) {
700
721
  spinner.succeed(projDir + ' (' + res.updated.length + ' updated)');
701
722
  succeeded++;
723
+ emitUpgradeProgress(projDir, 'success', projIdx, projects.length);
702
724
  }
703
725
  else {
704
726
  spinner.fail(projDir + ' — ' + (res.errors[0] || 'unknown error'));
705
727
  failed++;
728
+ emitUpgradeProgress(projDir, 'failed', projIdx, projects.length);
706
729
  }
707
730
  }
708
731
  catch (e) {
709
732
  spinner.fail(projDir + ' — ' + (e instanceof Error ? e.message : String(e)));
710
733
  failed++;
734
+ emitUpgradeProgress(projDir, 'failed', projIdx, projects.length);
711
735
  }
712
736
  }
713
737
  output.writeln();
@@ -342,6 +342,8 @@ export async function executeInit(options) {
342
342
  await startDaemonBackground(targetDir, result);
343
343
  // Run doctor auto-fix (non-blocking, best-effort)
344
344
  await runDoctorFix(targetDir, result);
345
+ // Register this project in ~/.monomind-projects.json so upgrade --all finds it
346
+ _registerMonomindProject(targetDir);
345
347
  }
346
348
  catch (error) {
347
349
  result.success = false;
@@ -2168,13 +2170,39 @@ function countEnabledHooks(options) {
2168
2170
  count++;
2169
2171
  return count;
2170
2172
  }
2173
+ /**
2174
+ * Register a project directory in ~/.monomind-projects.json so that
2175
+ * `monomind init upgrade --all` can find it without doing a directory scan.
2176
+ * Best-effort: failures are silently swallowed.
2177
+ */
2178
+ function _registerMonomindProject(dir) {
2179
+ try {
2180
+ const esmReq = createRequire(import.meta.url);
2181
+ const os = esmReq('os');
2182
+ const registryPath = path.join(os.homedir(), '.monomind-projects.json');
2183
+ let reg = { projects: [] };
2184
+ try {
2185
+ reg = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
2186
+ }
2187
+ catch { }
2188
+ if (!Array.isArray(reg.projects))
2189
+ reg.projects = [];
2190
+ const abs = path.resolve(dir);
2191
+ if (!reg.projects.includes(abs)) {
2192
+ reg.projects.push(abs);
2193
+ fs.writeFileSync(registryPath, JSON.stringify(reg, null, 2), 'utf-8');
2194
+ }
2195
+ }
2196
+ catch { /* non-fatal */ }
2197
+ }
2171
2198
  /**
2172
2199
  * Scan common locations for directories that have monomind installed
2173
2200
  * (presence of .claude/helpers/hook-handler.cjs is the definitive signal).
2174
2201
  * Searches up to maxDepth directory levels below each search root.
2175
2202
  */
2176
2203
  export function findMonomindProjects(maxDepth = 3) {
2177
- const os = require('os');
2204
+ const esmReq = createRequire(import.meta.url);
2205
+ const os = esmReq('os');
2178
2206
  const home = os.homedir();
2179
2207
  const searchRoots = [
2180
2208
  path.join(home, 'Desktop'),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monomindcli",
3
- "version": "1.10.27",
3
+ "version": "1.10.29",
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",
@@ -654,32 +654,65 @@ async function main() {
654
654
  mg.closeDb(db);
655
655
  process.exit(1);
656
656
  }
657
- const updateNode = db.prepare(`UPDATE nodes SET properties = ? WHERE id = ?`);
657
+ // Resolve each analysis item to a DB integer node id.
658
+ // graph.json nodes have id='file:<path>' (synthetic); the real PK is an integer.
659
+ // Accept: analysis.dbId (integer), analysis.filePath, or derive path from analysis.id.
660
+ const lookupByPath = db.prepare(`SELECT id, properties FROM nodes WHERE file_path=? OR file_path=? LIMIT 1`);
661
+ const lookupById = db.prepare(`SELECT id, properties FROM nodes WHERE id=? LIMIT 1`);
662
+ const updateNode = db.prepare(`UPDATE nodes SET properties = ? WHERE id = ?`);
658
663
  let written = 0;
659
664
  const tx = db.transaction(() => {
660
665
  for (const analysis of analyses) {
661
- if (!analysis || !analysis.id) continue;
662
- const existing = (() => {
663
- try { const row = db.prepare('SELECT properties FROM nodes WHERE id=?').get(analysis.id); return row?.properties ? JSON.parse(row.properties) : {}; } catch { return {}; }
664
- })();
666
+ if (!analysis) continue;
667
+ let nodeRow = null;
668
+ // 1. Prefer explicit integer dbId (added by newer graph.json emitter)
669
+ if (analysis.dbId != null) {
670
+ nodeRow = lookupById.get(analysis.dbId);
671
+ }
672
+ // 2. Fall back to filePath from graph.json node
673
+ if (!nodeRow && analysis.filePath) {
674
+ const rel = analysis.filePath.startsWith('/') ? relative(projectDir, analysis.filePath) : analysis.filePath;
675
+ nodeRow = lookupByPath.get(analysis.filePath, rel);
676
+ }
677
+ // 3. Derive path from synthetic id ('file:<path>')
678
+ if (!nodeRow && analysis.id && String(analysis.id).startsWith('file:')) {
679
+ const derivedPath = String(analysis.id).slice(5);
680
+ const rel = derivedPath.startsWith('/') ? relative(projectDir, derivedPath) : derivedPath;
681
+ nodeRow = lookupByPath.get(derivedPath, rel);
682
+ }
683
+ if (!nodeRow) continue;
684
+ const existing = (() => { try { return nodeRow.properties ? JSON.parse(nodeRow.properties) : {}; } catch { return {}; } })();
665
685
  const merged = {
666
686
  ...existing,
667
- ...(analysis.fileSummary ? { summary: analysis.fileSummary } : {}),
668
- ...(analysis.tags ? { tags: analysis.tags } : {}),
669
- ...(analysis.complexity ? { complexity: analysis.complexity } : {}),
670
- ...(analysis.functionSummaries ? { functionSummaries: analysis.functionSummaries } : {}),
671
- ...(analysis.classSummaries ? { classSummaries: analysis.classSummaries } : {}),
687
+ ...(analysis.fileSummary ? { summary: analysis.fileSummary } : {}),
688
+ ...(analysis.tags ? { tags: analysis.tags } : {}),
689
+ ...(analysis.complexity ? { complexity: analysis.complexity } : {}),
690
+ ...(analysis.functionSummaries ? { functionSummaries: analysis.functionSummaries } : {}),
691
+ ...(analysis.classSummaries ? { classSummaries: analysis.classSummaries } : {}),
672
692
  ua_analyzed_at: new Date().toISOString(),
673
693
  };
674
- updateNode.run(JSON.stringify(merged), analysis.id);
694
+ updateNode.run(JSON.stringify(merged), nodeRow.id);
675
695
  written++;
676
696
  }
677
697
  });
678
698
  tx();
679
699
  // Rebuild FTS so summaries are immediately searchable
680
700
  try { db.prepare(`INSERT INTO nodes_fts(nodes_fts) VALUES('rebuild')`).run(); } catch {}
681
- mg.closeDb(db);
701
+
702
+ // Validate write-back: count nodes that now have a readable summary in properties
703
+ let enrichedCount = 0;
704
+ try {
705
+ const row = db.prepare(
706
+ `SELECT COUNT(*) AS c FROM nodes WHERE json_extract(properties, '$.summary') IS NOT NULL AND length(json_extract(properties, '$.summary')) > 5`
707
+ ).get();
708
+ enrichedCount = row ? row.c : 0;
709
+ } catch (_) {}
682
710
  console.log(`[understand] Imported ${written} analyses from stdin. FTS rebuilt.`);
711
+ console.log(`[understand] Validation: ${enrichedCount} node(s) now have LLM summaries readable via json_extract.`);
712
+ if (written > 0 && enrichedCount < written) {
713
+ console.warn(`[understand] WARNING: only ${enrichedCount} of ${written} imported analyses are queryable. Some properties may not have committed — check if properties column is TEXT.`);
714
+ }
715
+ mg.closeDb(db);
683
716
  return;
684
717
  }
685
718
 
@@ -1001,6 +1034,7 @@ function buildGraphJson(dir, fileNodes, analysisMap, layers) {
1001
1034
  const a = analysisMap[n.file_path] || {};
1002
1035
  return {
1003
1036
  id: 'file:' + (n.file_path || n.name),
1037
+ dbId: n.id, // integer PK — use this in --import-analyses-stdin payloads
1004
1038
  type: 'file',
1005
1039
  name: n.name,
1006
1040
  filePath: n.file_path,