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.
- package/.claude/helpers/hook-handler.cjs +28 -3
- package/package.json +1 -1
- package/packages/@monomind/cli/dist/src/commands/init.js +25 -1
- package/packages/@monomind/cli/dist/src/init/executor.js +29 -1
- package/packages/@monomind/cli/package.json +1 -1
- package/packages/@monomind/cli/scripts/understand-analyze.mjs +46 -12
|
@@ -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
|
-
|
|
203
|
-
if (
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
|
662
|
-
|
|
663
|
-
|
|
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
|
|
668
|
-
...(analysis.tags
|
|
669
|
-
...(analysis.complexity
|
|
670
|
-
...(analysis.functionSummaries ? { functionSummaries:
|
|
671
|
-
...(analysis.classSummaries ? { 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),
|
|
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
|
-
|
|
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,
|