monomind 1.10.26 → 1.10.28
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 +126 -17
- package/.claude/settings.local.json +2 -1
- package/package.json +2 -2
- package/packages/@monomind/cli/dist/src/commands/init.js +48 -1
- package/packages/@monomind/cli/dist/src/init/executor.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/init/executor.js +61 -0
- package/packages/@monomind/cli/dist/src/init/index.d.ts +1 -1
- package/packages/@monomind/cli/dist/src/init/index.js +1 -1
- package/packages/@monomind/cli/package.json +2 -2
- package/packages/@monomind/cli/scripts/understand-analyze.mjs +80 -5
|
@@ -167,26 +167,60 @@ function _recordGraphTelemetry(event) {
|
|
|
167
167
|
} catch (e) { /* non-fatal */ }
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
// Re-inject graph
|
|
170
|
+
// Re-inject graph context after compaction so the LLM doesn't lose its spatial map.
|
|
171
|
+
// Prefers recently-edited files (session context) over pure degree centrality so
|
|
172
|
+
// the injected anchors match what the LLM was actually working on.
|
|
171
173
|
function _injectCompactGraphMap() {
|
|
172
174
|
try {
|
|
173
175
|
var db = _openMonographDb();
|
|
174
176
|
if (!db) return;
|
|
175
177
|
try {
|
|
176
178
|
var nodeC = db.prepare("SELECT COUNT(*) AS c FROM nodes").get().c;
|
|
177
|
-
var
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
179
|
+
var anchors = [];
|
|
180
|
+
var seenPaths = {};
|
|
181
|
+
|
|
182
|
+
// 1. Prefer recently-edited files (up to 5) — these are what matters NOW.
|
|
183
|
+
var recentEdits = _getRecentEdits();
|
|
184
|
+
for (var ri = 0; ri < Math.min(recentEdits.length, 5); ri++) {
|
|
185
|
+
var rfile = recentEdits[ri].file;
|
|
186
|
+
// Normalise to relative path for DB lookup
|
|
187
|
+
var rrel = (rfile.indexOf(CWD) === 0) ? rfile.slice(CWD.length + 1) : rfile;
|
|
188
|
+
try {
|
|
189
|
+
var rnode = db.prepare(
|
|
190
|
+
"SELECT n.name, n.label, n.file_path, " +
|
|
191
|
+
"(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg " +
|
|
192
|
+
"FROM nodes n WHERE n.label='File' AND (n.file_path=? OR n.file_path=?) LIMIT 1"
|
|
193
|
+
).get(rfile, rrel);
|
|
194
|
+
if (rnode && !seenPaths[rnode.file_path]) {
|
|
195
|
+
seenPaths[rnode.file_path] = 1;
|
|
196
|
+
anchors.push({ name: rnode.name, label: rnode.label, file_path: rnode.file_path, deg: rnode.deg, tag: '✎' });
|
|
197
|
+
}
|
|
198
|
+
} catch (e) { /* ignore — file may not be in graph yet */ }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 2. Fill remaining slots (up to 8 total) with god nodes (high centrality).
|
|
202
|
+
if (anchors.length < 8) {
|
|
203
|
+
var gods = db.prepare(
|
|
204
|
+
"SELECT n.name, n.label, n.file_path, " +
|
|
205
|
+
"(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg " +
|
|
206
|
+
"FROM nodes n " +
|
|
207
|
+
"WHERE n.label NOT IN ('Concept') AND n.file_path IS NOT NULL AND n.file_path != '' " +
|
|
208
|
+
"AND n.name NOT LIKE '(%' AND n.name NOT LIKE '%=>%' AND length(n.name) >= 3 " +
|
|
209
|
+
"ORDER BY deg DESC LIMIT 15"
|
|
210
|
+
).all();
|
|
211
|
+
for (var gi = 0; gi < gods.length && anchors.length < 8; gi++) {
|
|
212
|
+
if (!seenPaths[gods[gi].file_path]) {
|
|
213
|
+
seenPaths[gods[gi].file_path] = 1;
|
|
214
|
+
anchors.push({ name: gods[gi].name, label: gods[gi].label, file_path: gods[gi].file_path, deg: gods[gi].deg, tag: '' });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (anchors.length > 0) {
|
|
220
|
+
console.log('[COMPACT_GRAPH] ' + nodeC + ' nodes. Session context (✎ = recently edited):');
|
|
221
|
+
for (var ci = 0; ci < anchors.length; ci++) {
|
|
222
|
+
var g = anchors[ci];
|
|
223
|
+
console.log(' ' + (g.tag || ' ') + ' ' + g.name + ' [' + g.label + '] — ' + g.file_path + ' (deg ' + g.deg + ')');
|
|
190
224
|
}
|
|
191
225
|
console.log(' Use mcp__monomind__monograph_suggest first when navigating.');
|
|
192
226
|
}
|
|
@@ -194,6 +228,37 @@ function _injectCompactGraphMap() {
|
|
|
194
228
|
} catch (e) {}
|
|
195
229
|
}
|
|
196
230
|
|
|
231
|
+
// ── Recent edit history ────────────────────────────────────────────────────────
|
|
232
|
+
// Track last N edited file paths so compact injection and pre-resolve can surface
|
|
233
|
+
// the files the LLM was actively working on instead of pure centrality anchors.
|
|
234
|
+
function _recordRecentEdit(filePath) {
|
|
235
|
+
if (!filePath) return;
|
|
236
|
+
try {
|
|
237
|
+
var f = path.join(CWD, '.monomind', 'metrics', 'recent-edits.json');
|
|
238
|
+
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
239
|
+
var d = { edits: [] };
|
|
240
|
+
try { d = JSON.parse(fs.readFileSync(f, 'utf-8')); } catch (_) {}
|
|
241
|
+
if (!Array.isArray(d.edits)) d.edits = [];
|
|
242
|
+
// Remove stale entry for same file, then prepend fresh one
|
|
243
|
+
d.edits = d.edits.filter(function(e) { return e.file !== filePath; });
|
|
244
|
+
d.edits.unshift({ file: filePath, editedAt: Date.now() });
|
|
245
|
+
if (d.edits.length > 10) d.edits = d.edits.slice(0, 10);
|
|
246
|
+
fs.writeFileSync(f, JSON.stringify(d));
|
|
247
|
+
} catch (e) { /* non-fatal */ }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function _getRecentEdits() {
|
|
251
|
+
try {
|
|
252
|
+
var f = path.join(CWD, '.monomind', 'metrics', 'recent-edits.json');
|
|
253
|
+
if (!fs.existsSync(f)) return [];
|
|
254
|
+
var d = JSON.parse(fs.readFileSync(f, 'utf-8'));
|
|
255
|
+
if (!Array.isArray(d.edits)) return [];
|
|
256
|
+
// Only return edits from the last 2 hours (session-scoped)
|
|
257
|
+
var cutoff = Date.now() - 2 * 60 * 60 * 1000;
|
|
258
|
+
return d.edits.filter(function(e) { return e.editedAt > cutoff; });
|
|
259
|
+
} catch (e) { return []; }
|
|
260
|
+
}
|
|
261
|
+
|
|
197
262
|
// ── Loop drift detection ───────────────────────────────────────────────────────
|
|
198
263
|
// Record tool call signatures per session, warn when the same call recurs ≥3×.
|
|
199
264
|
function _recordToolCall(signature) {
|
|
@@ -910,11 +975,14 @@ const handlers = {
|
|
|
910
975
|
else if (topGraph.label === 'Class' || topGraph.label === 'Interface') agent = 'system-architect';
|
|
911
976
|
// Functions, files, methods → coder
|
|
912
977
|
else agent = 'coder';
|
|
978
|
+
// Scale confidence by graph degree: well-connected nodes are stronger anchors.
|
|
979
|
+
var topDeg = topGraph.deg || 0;
|
|
980
|
+
var graphConf = topDeg > 30 ? 0.80 : (topDeg > 10 ? 0.75 : 0.70);
|
|
913
981
|
result = Object.assign({}, result, {
|
|
914
982
|
agent: agent,
|
|
915
983
|
agentSlug: agent,
|
|
916
|
-
confidence:
|
|
917
|
-
reason: 'Graph fallback: top file ' + (topGraph.name || '').substring(0, 30) + ' [' + topGraph.label + ']',
|
|
984
|
+
confidence: graphConf,
|
|
985
|
+
reason: 'Graph fallback: top file ' + (topGraph.name || '').substring(0, 30) + ' [' + topGraph.label + '] deg=' + topDeg,
|
|
918
986
|
specificAgents: [],
|
|
919
987
|
extrasMatches: [],
|
|
920
988
|
});
|
|
@@ -1133,11 +1201,46 @@ const handlers = {
|
|
|
1133
1201
|
// Pre-resolve top-5 relevant files for the user's prompt — the LLM
|
|
1134
1202
|
// sees the answer inline instead of being told to call a tool.
|
|
1135
1203
|
var suggestions = getMonographSuggestions(prompt, 5);
|
|
1204
|
+
|
|
1205
|
+
// Boost recently-edited files to the top of pre-resolve suggestions.
|
|
1206
|
+
// Even when the FTS index hasn't caught up to the latest edits, the
|
|
1207
|
+
// LLM should see the files it just modified as the primary context.
|
|
1208
|
+
try {
|
|
1209
|
+
var recentEditsForRoute = _getRecentEdits();
|
|
1210
|
+
if (recentEditsForRoute.length > 0) {
|
|
1211
|
+
// Extract prompt keywords for relevance gating
|
|
1212
|
+
var promptWords = (prompt || '').toLowerCase().match(/[a-z][a-z0-9_-]{2,}/g) || [];
|
|
1213
|
+
var promptWordSet = {};
|
|
1214
|
+
for (var pw = 0; pw < promptWords.length; pw++) promptWordSet[promptWords[pw]] = 1;
|
|
1215
|
+
|
|
1216
|
+
var existingFiles = {};
|
|
1217
|
+
for (var se = 0; se < suggestions.length; se++) existingFiles[suggestions[se].file || ''] = 1;
|
|
1218
|
+
|
|
1219
|
+
var editBoosts = [];
|
|
1220
|
+
for (var re = 0; re < recentEditsForRoute.length && editBoosts.length < 2; re++) {
|
|
1221
|
+
var reFile = recentEditsForRoute[re].file;
|
|
1222
|
+
// Skip if already in suggestions
|
|
1223
|
+
if (existingFiles[reFile]) continue;
|
|
1224
|
+
var reName = path.basename(reFile, path.extname(reFile)).toLowerCase();
|
|
1225
|
+
// Only boost if filename shares a keyword with the prompt OR the edit is very recent (<3 min)
|
|
1226
|
+
var veryRecent = (Date.now() - recentEditsForRoute[re].editedAt) < 3 * 60 * 1000;
|
|
1227
|
+
var matches = promptWordSet[reName] || veryRecent;
|
|
1228
|
+
if (matches) {
|
|
1229
|
+
editBoosts.push({ name: path.basename(reFile), label: 'File', file: reFile, deg: 0, _editBoost: true });
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
if (editBoosts.length > 0) {
|
|
1233
|
+
suggestions = editBoosts.concat(suggestions).slice(0, 5);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
} catch (e) { /* non-fatal */ }
|
|
1237
|
+
|
|
1136
1238
|
if (suggestions.length > 0) {
|
|
1137
1239
|
console.log('\n[MONOGRAPH] ' + nodeCount + ' nodes indexed. Top files for this task (pre-resolved from graph):');
|
|
1138
1240
|
for (var si = 0; si < suggestions.length; si++) {
|
|
1139
1241
|
var s = suggestions[si];
|
|
1140
|
-
|
|
1242
|
+
var editTag = s._editBoost ? ' ✎' : '';
|
|
1243
|
+
console.log(' · ' + s.name + ' [' + s.label + '] — ' + (s.file || '') + (s.deg ? ' (deg ' + s.deg + ')' : '') + editTag);
|
|
1141
1244
|
}
|
|
1142
1245
|
console.log(' Use mcp__monomind__monograph_query / monograph_impact for deeper drill-down.');
|
|
1143
1246
|
_recordGraphTelemetry('preresolve_hit');
|
|
@@ -1347,6 +1450,12 @@ const handlers = {
|
|
|
1347
1450
|
intelligence.recordEdit(file);
|
|
1348
1451
|
} catch (e) { /* non-fatal */ }
|
|
1349
1452
|
}
|
|
1453
|
+
// Track recently-edited files for compact injection and pre-resolve boosting.
|
|
1454
|
+
try {
|
|
1455
|
+
var editedForRecent = hookInput.file_path || toolInput.file_path
|
|
1456
|
+
|| process.env.TOOL_INPUT_file_path || args[0] || '';
|
|
1457
|
+
if (editedForRecent) _recordRecentEdit(editedForRecent);
|
|
1458
|
+
} catch (e) { /* non-fatal */ }
|
|
1350
1459
|
// Increment write counter and rebuild monograph when threshold hit.
|
|
1351
1460
|
_maybeRebuildMonograph();
|
|
1352
1461
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monomind",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.28",
|
|
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",
|
|
@@ -127,4 +127,4 @@
|
|
|
127
127
|
"access": "public",
|
|
128
128
|
"tag": "latest"
|
|
129
129
|
}
|
|
130
|
-
}
|
|
130
|
+
}
|
|
@@ -6,7 +6,7 @@ import { output } from '../output.js';
|
|
|
6
6
|
import { confirm, select, multiSelect, input } from '../prompt.js';
|
|
7
7
|
import * as fs from 'fs';
|
|
8
8
|
import * as path from 'path';
|
|
9
|
-
import { executeInit, executeUpgrade, executeUpgradeWithMissing, DEFAULT_INIT_OPTIONS, MINIMAL_INIT_OPTIONS, FULL_INIT_OPTIONS, } from '../init/index.js';
|
|
9
|
+
import { executeInit, executeUpgrade, executeUpgradeWithMissing, findMonomindProjects, DEFAULT_INIT_OPTIONS, MINIMAL_INIT_OPTIONS, FULL_INIT_OPTIONS, } from '../init/index.js';
|
|
10
10
|
// Check if project is already initialized
|
|
11
11
|
function isInitialized(cwd) {
|
|
12
12
|
const claudePath = path.join(cwd, '.claude', 'settings.json');
|
|
@@ -663,10 +663,57 @@ const upgradeCommand = {
|
|
|
663
663
|
type: 'boolean',
|
|
664
664
|
default: false,
|
|
665
665
|
},
|
|
666
|
+
{
|
|
667
|
+
name: 'all',
|
|
668
|
+
description: 'Upgrade all known monomind projects on this machine (scans ~/Desktop, ~/projects, etc.)',
|
|
669
|
+
type: 'boolean',
|
|
670
|
+
default: false,
|
|
671
|
+
},
|
|
666
672
|
],
|
|
667
673
|
action: async (ctx) => {
|
|
668
674
|
const addMissing = (ctx.flags['add-missing'] || ctx.flags.addMissing);
|
|
669
675
|
const upgradeSettings = (ctx.flags.settings);
|
|
676
|
+
const upgradeAll = (ctx.flags.all);
|
|
677
|
+
// ── --all: scan for every monomind project and upgrade each ──────────────
|
|
678
|
+
if (upgradeAll) {
|
|
679
|
+
output.writeln();
|
|
680
|
+
output.writeln(output.bold('Upgrading all monomind projects'));
|
|
681
|
+
output.writeln(output.dim('Scanning ~/Desktop, ~/projects, ~/code… (this may take a moment)'));
|
|
682
|
+
output.writeln();
|
|
683
|
+
const projects = findMonomindProjects();
|
|
684
|
+
if (projects.length === 0) {
|
|
685
|
+
output.printInfo('No monomind projects found. Install monomind in a project first: npx monomind init');
|
|
686
|
+
return { success: true, exitCode: 0 };
|
|
687
|
+
}
|
|
688
|
+
output.printInfo(`Found ${projects.length} project(s). Upgrading…`);
|
|
689
|
+
output.writeln();
|
|
690
|
+
let succeeded = 0;
|
|
691
|
+
let failed = 0;
|
|
692
|
+
for (const projDir of projects) {
|
|
693
|
+
const spinner = output.createSpinner({ text: projDir });
|
|
694
|
+
spinner.start();
|
|
695
|
+
try {
|
|
696
|
+
const res = addMissing
|
|
697
|
+
? await executeUpgradeWithMissing(projDir, upgradeSettings)
|
|
698
|
+
: await executeUpgrade(projDir, upgradeSettings);
|
|
699
|
+
if (res.success) {
|
|
700
|
+
spinner.succeed(projDir + ' (' + res.updated.length + ' updated)');
|
|
701
|
+
succeeded++;
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
spinner.fail(projDir + ' — ' + (res.errors[0] || 'unknown error'));
|
|
705
|
+
failed++;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
catch (e) {
|
|
709
|
+
spinner.fail(projDir + ' — ' + (e instanceof Error ? e.message : String(e)));
|
|
710
|
+
failed++;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
output.writeln();
|
|
714
|
+
output.printInfo(`Done: ${succeeded} upgraded, ${failed} failed out of ${projects.length} projects.`);
|
|
715
|
+
return { success: failed === 0, exitCode: failed > 0 ? 1 : 0 };
|
|
716
|
+
}
|
|
670
717
|
output.writeln();
|
|
671
718
|
output.writeln(output.bold('Upgrading MonoMind'));
|
|
672
719
|
if (addMissing && upgradeSettings) {
|
|
@@ -34,5 +34,11 @@ export declare function executeUpgrade(targetDir: string, upgradeSettings?: bool
|
|
|
34
34
|
* @param upgradeSettings - If true, merge new settings into existing settings.json
|
|
35
35
|
*/
|
|
36
36
|
export declare function executeUpgradeWithMissing(targetDir: string, upgradeSettings?: boolean): Promise<UpgradeResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Scan common locations for directories that have monomind installed
|
|
39
|
+
* (presence of .claude/helpers/hook-handler.cjs is the definitive signal).
|
|
40
|
+
* Searches up to maxDepth directory levels below each search root.
|
|
41
|
+
*/
|
|
42
|
+
export declare function findMonomindProjects(maxDepth?: number): string[];
|
|
37
43
|
export default executeInit;
|
|
38
44
|
//# sourceMappingURL=executor.d.ts.map
|
|
@@ -2168,5 +2168,66 @@ function countEnabledHooks(options) {
|
|
|
2168
2168
|
count++;
|
|
2169
2169
|
return count;
|
|
2170
2170
|
}
|
|
2171
|
+
/**
|
|
2172
|
+
* Scan common locations for directories that have monomind installed
|
|
2173
|
+
* (presence of .claude/helpers/hook-handler.cjs is the definitive signal).
|
|
2174
|
+
* Searches up to maxDepth directory levels below each search root.
|
|
2175
|
+
*/
|
|
2176
|
+
export function findMonomindProjects(maxDepth = 3) {
|
|
2177
|
+
const esmReq = createRequire(import.meta.url);
|
|
2178
|
+
const os = esmReq('os');
|
|
2179
|
+
const home = os.homedir();
|
|
2180
|
+
const searchRoots = [
|
|
2181
|
+
path.join(home, 'Desktop'),
|
|
2182
|
+
path.join(home, 'projects'),
|
|
2183
|
+
path.join(home, 'code'),
|
|
2184
|
+
path.join(home, 'work'),
|
|
2185
|
+
path.join(home, 'dev'),
|
|
2186
|
+
path.join(home, 'repos'),
|
|
2187
|
+
path.join(home, 'src'),
|
|
2188
|
+
].filter(r => fs.existsSync(r));
|
|
2189
|
+
// Also check known-projects registry if it exists
|
|
2190
|
+
const registryPath = path.join(home, '.monomind-projects.json');
|
|
2191
|
+
if (fs.existsSync(registryPath)) {
|
|
2192
|
+
try {
|
|
2193
|
+
const reg = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
|
|
2194
|
+
if (Array.isArray(reg.projects)) {
|
|
2195
|
+
for (const p of reg.projects) {
|
|
2196
|
+
if (!searchRoots.includes(p) && fs.existsSync(p))
|
|
2197
|
+
searchRoots.push(p);
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
catch { }
|
|
2202
|
+
}
|
|
2203
|
+
const found = new Set();
|
|
2204
|
+
function walk(dir, depth) {
|
|
2205
|
+
if (depth > maxDepth)
|
|
2206
|
+
return;
|
|
2207
|
+
const marker = path.join(dir, '.claude', 'helpers', 'hook-handler.cjs');
|
|
2208
|
+
if (fs.existsSync(marker)) {
|
|
2209
|
+
found.add(dir);
|
|
2210
|
+
return;
|
|
2211
|
+
} // don't recurse into a monomind project
|
|
2212
|
+
let entries;
|
|
2213
|
+
try {
|
|
2214
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
2215
|
+
}
|
|
2216
|
+
catch {
|
|
2217
|
+
return;
|
|
2218
|
+
}
|
|
2219
|
+
for (const e of entries) {
|
|
2220
|
+
if (!e.isDirectory())
|
|
2221
|
+
continue;
|
|
2222
|
+
if (e.name.startsWith('.') || e.name === 'node_modules')
|
|
2223
|
+
continue;
|
|
2224
|
+
walk(path.join(dir, e.name), depth + 1);
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
2227
|
+
for (const root of searchRoots) {
|
|
2228
|
+
walk(root, 0);
|
|
2229
|
+
}
|
|
2230
|
+
return [...found];
|
|
2231
|
+
}
|
|
2171
2232
|
export default executeInit;
|
|
2172
2233
|
//# sourceMappingURL=executor.js.map
|
|
@@ -8,6 +8,6 @@ export { generateMCPConfig, generateMCPJson, } from './mcp-generator.js';
|
|
|
8
8
|
export { generateStatuslineScript, generateStatuslineHook, } from './statusline-generator.js';
|
|
9
9
|
export { generatePreCommitHook, generatePostCommitHook, generateSessionManager, generateAgentRouter, generateMemoryHelper, generateHookHandler, generateIntelligenceStub, generateAutoMemoryHook, } from './helpers-generator.js';
|
|
10
10
|
export { generateClaudeMd, generateMinimalClaudeMd, CLAUDE_MD_TEMPLATES, } from './claudemd-generator.js';
|
|
11
|
-
export { executeInit, executeUpgrade, executeUpgradeWithMissing, default } from './executor.js';
|
|
11
|
+
export { executeInit, executeUpgrade, executeUpgradeWithMissing, findMonomindProjects, default } from './executor.js';
|
|
12
12
|
export type { UpgradeResult } from './executor.js';
|
|
13
13
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -11,5 +11,5 @@ export { generateStatuslineScript, generateStatuslineHook, } from './statusline-
|
|
|
11
11
|
export { generatePreCommitHook, generatePostCommitHook, generateSessionManager, generateAgentRouter, generateMemoryHelper, generateHookHandler, generateIntelligenceStub, generateAutoMemoryHook, } from './helpers-generator.js';
|
|
12
12
|
export { generateClaudeMd, generateMinimalClaudeMd, CLAUDE_MD_TEMPLATES, } from './claudemd-generator.js';
|
|
13
13
|
// Main executor
|
|
14
|
-
export { executeInit, executeUpgrade, executeUpgradeWithMissing, default } from './executor.js';
|
|
14
|
+
export { executeInit, executeUpgrade, executeUpgradeWithMissing, findMonomindProjects, default } from './executor.js';
|
|
15
15
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monoes/monomindcli",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.28",
|
|
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",
|
|
@@ -112,4 +112,4 @@
|
|
|
112
112
|
"access": "public",
|
|
113
113
|
"tag": "latest"
|
|
114
114
|
}
|
|
115
|
-
}
|
|
115
|
+
}
|
|
@@ -48,11 +48,12 @@ const dbPathArg = argVal('db') ? resolve(argVal('db')) : join(projectDir,
|
|
|
48
48
|
const outputArg = argVal('output')? resolve(argVal('output')) : join(projectDir, '.understand', 'knowledge-graph.json');
|
|
49
49
|
const batchSize = parseInt(argVal('batch-size') || '5', 10);
|
|
50
50
|
const maxFiles = parseInt(argVal('max-files') || '0', 10);
|
|
51
|
-
const dryRun
|
|
52
|
-
const noLlm
|
|
53
|
-
const layersOnly
|
|
54
|
-
const incremental
|
|
55
|
-
const
|
|
51
|
+
const dryRun = hasFlag('dry-run');
|
|
52
|
+
const noLlm = hasFlag('no-llm');
|
|
53
|
+
const layersOnly = hasFlag('layers-only');
|
|
54
|
+
const incremental = hasFlag('incremental');
|
|
55
|
+
const importAnalysesStdin = hasFlag('import-analyses-stdin');
|
|
56
|
+
const onboard = hasFlag('onboard');
|
|
56
57
|
const onboardOut = argVal('onboard-out') ? resolve(argVal('onboard-out')) : join(projectDir, 'ONBOARDING.md');
|
|
57
58
|
|
|
58
59
|
// ── Resolve @monoes/monograph for DB access ──────────────────────────────────
|
|
@@ -629,6 +630,79 @@ async function main() {
|
|
|
629
630
|
try { db.prepare(`ALTER TABLE nodes ADD COLUMN properties TEXT`).run(); } catch {}
|
|
630
631
|
try { db.prepare(`CREATE TABLE IF NOT EXISTS communities (id INTEGER PRIMARY KEY, label TEXT, size INTEGER NOT NULL DEFAULT 0, cohesion_score REAL NOT NULL DEFAULT 0.0)`).run(); } catch {}
|
|
631
632
|
|
|
633
|
+
// ── Import analyses from stdin (slash-command write-back path) ───────────
|
|
634
|
+
// When Claude Code's /monomind:understand has collected per-file summaries
|
|
635
|
+
// through the active session, it pipes them back via:
|
|
636
|
+
// echo "$ANALYSES_JSON" | node understand-analyze.mjs --import-analyses-stdin
|
|
637
|
+
// The JSON shape: { "analyses": [{ "id": <nodeId>, "fileSummary": "...", "tags": [...], ... }] }
|
|
638
|
+
if (importAnalysesStdin) {
|
|
639
|
+
const stdinText = await new Promise((resolve) => {
|
|
640
|
+
let buf = '';
|
|
641
|
+
process.stdin.setEncoding('utf-8');
|
|
642
|
+
process.stdin.on('data', c => { buf += c; });
|
|
643
|
+
process.stdin.on('end', () => resolve(buf));
|
|
644
|
+
process.stdin.on('error', () => resolve(buf));
|
|
645
|
+
// Bail out after 30 s so the slash command never hangs
|
|
646
|
+
setTimeout(() => resolve(buf), 30000);
|
|
647
|
+
process.stdin.resume();
|
|
648
|
+
});
|
|
649
|
+
let parsed = null;
|
|
650
|
+
try { parsed = JSON.parse(stdinText); } catch {}
|
|
651
|
+
const analyses = (parsed && Array.isArray(parsed.analyses)) ? parsed.analyses : [];
|
|
652
|
+
if (analyses.length === 0) {
|
|
653
|
+
console.error('[understand] --import-analyses-stdin: no analyses found in stdin JSON');
|
|
654
|
+
mg.closeDb(db);
|
|
655
|
+
process.exit(1);
|
|
656
|
+
}
|
|
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 = ?`);
|
|
663
|
+
let written = 0;
|
|
664
|
+
const tx = db.transaction(() => {
|
|
665
|
+
for (const analysis of analyses) {
|
|
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 {}; } })();
|
|
685
|
+
const merged = {
|
|
686
|
+
...existing,
|
|
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 } : {}),
|
|
692
|
+
ua_analyzed_at: new Date().toISOString(),
|
|
693
|
+
};
|
|
694
|
+
updateNode.run(JSON.stringify(merged), nodeRow.id);
|
|
695
|
+
written++;
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
tx();
|
|
699
|
+
// Rebuild FTS so summaries are immediately searchable
|
|
700
|
+
try { db.prepare(`INSERT INTO nodes_fts(nodes_fts) VALUES('rebuild')`).run(); } catch {}
|
|
701
|
+
mg.closeDb(db);
|
|
702
|
+
console.log(`[understand] Imported ${written} analyses from stdin. FTS rebuilt.`);
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
632
706
|
// ── Load all file nodes ──────────────────────────────────────────────────
|
|
633
707
|
let fileNodes = db.prepare(`SELECT id, name, file_path, properties FROM nodes WHERE label = 'File' AND file_path IS NOT NULL`).all();
|
|
634
708
|
console.log(`[understand] Found ${fileNodes.length} file nodes in DB`);
|
|
@@ -947,6 +1021,7 @@ function buildGraphJson(dir, fileNodes, analysisMap, layers) {
|
|
|
947
1021
|
const a = analysisMap[n.file_path] || {};
|
|
948
1022
|
return {
|
|
949
1023
|
id: 'file:' + (n.file_path || n.name),
|
|
1024
|
+
dbId: n.id, // integer PK — use this in --import-analyses-stdin payloads
|
|
950
1025
|
type: 'file',
|
|
951
1026
|
name: n.name,
|
|
952
1027
|
filePath: n.file_path,
|