monomind 1.10.25 → 1.10.27
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 +127 -17
- package/.claude/helpers/router.cjs +35 -0
- 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 +60 -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 +59 -5
|
@@ -167,26 +167,61 @@ 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
|
+
var slotsLeft = 8 - anchors.length;
|
|
203
|
+
if (slotsLeft > 0) {
|
|
204
|
+
var gods = db.prepare(
|
|
205
|
+
"SELECT n.name, n.label, n.file_path, " +
|
|
206
|
+
"(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg " +
|
|
207
|
+
"FROM nodes n " +
|
|
208
|
+
"WHERE n.label NOT IN ('Concept') AND n.file_path IS NOT NULL AND n.file_path != '' " +
|
|
209
|
+
"AND n.name NOT LIKE '(%' AND n.name NOT LIKE '%=>%' AND length(n.name) >= 3 " +
|
|
210
|
+
"ORDER BY deg DESC LIMIT 15"
|
|
211
|
+
).all();
|
|
212
|
+
for (var gi = 0; gi < gods.length && anchors.length < 8; gi++) {
|
|
213
|
+
if (!seenPaths[gods[gi].file_path]) {
|
|
214
|
+
seenPaths[gods[gi].file_path] = 1;
|
|
215
|
+
anchors.push({ name: gods[gi].name, label: gods[gi].label, file_path: gods[gi].file_path, deg: gods[gi].deg, tag: '' });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (anchors.length > 0) {
|
|
221
|
+
console.log('[COMPACT_GRAPH] ' + nodeC + ' nodes. Session context (✎ = recently edited):');
|
|
222
|
+
for (var ci = 0; ci < anchors.length; ci++) {
|
|
223
|
+
var g = anchors[ci];
|
|
224
|
+
console.log(' ' + (g.tag || ' ') + ' ' + g.name + ' [' + g.label + '] — ' + g.file_path + ' (deg ' + g.deg + ')');
|
|
190
225
|
}
|
|
191
226
|
console.log(' Use mcp__monomind__monograph_suggest first when navigating.');
|
|
192
227
|
}
|
|
@@ -194,6 +229,37 @@ function _injectCompactGraphMap() {
|
|
|
194
229
|
} catch (e) {}
|
|
195
230
|
}
|
|
196
231
|
|
|
232
|
+
// ── Recent edit history ────────────────────────────────────────────────────────
|
|
233
|
+
// Track last N edited file paths so compact injection and pre-resolve can surface
|
|
234
|
+
// the files the LLM was actively working on instead of pure centrality anchors.
|
|
235
|
+
function _recordRecentEdit(filePath) {
|
|
236
|
+
if (!filePath) return;
|
|
237
|
+
try {
|
|
238
|
+
var f = path.join(CWD, '.monomind', 'metrics', 'recent-edits.json');
|
|
239
|
+
fs.mkdirSync(path.dirname(f), { recursive: true });
|
|
240
|
+
var d = { edits: [] };
|
|
241
|
+
try { d = JSON.parse(fs.readFileSync(f, 'utf-8')); } catch (_) {}
|
|
242
|
+
if (!Array.isArray(d.edits)) d.edits = [];
|
|
243
|
+
// Remove stale entry for same file, then prepend fresh one
|
|
244
|
+
d.edits = d.edits.filter(function(e) { return e.file !== filePath; });
|
|
245
|
+
d.edits.unshift({ file: filePath, editedAt: Date.now() });
|
|
246
|
+
if (d.edits.length > 10) d.edits = d.edits.slice(0, 10);
|
|
247
|
+
fs.writeFileSync(f, JSON.stringify(d));
|
|
248
|
+
} catch (e) { /* non-fatal */ }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function _getRecentEdits() {
|
|
252
|
+
try {
|
|
253
|
+
var f = path.join(CWD, '.monomind', 'metrics', 'recent-edits.json');
|
|
254
|
+
if (!fs.existsSync(f)) return [];
|
|
255
|
+
var d = JSON.parse(fs.readFileSync(f, 'utf-8'));
|
|
256
|
+
if (!Array.isArray(d.edits)) return [];
|
|
257
|
+
// Only return edits from the last 2 hours (session-scoped)
|
|
258
|
+
var cutoff = Date.now() - 2 * 60 * 60 * 1000;
|
|
259
|
+
return d.edits.filter(function(e) { return e.editedAt > cutoff; });
|
|
260
|
+
} catch (e) { return []; }
|
|
261
|
+
}
|
|
262
|
+
|
|
197
263
|
// ── Loop drift detection ───────────────────────────────────────────────────────
|
|
198
264
|
// Record tool call signatures per session, warn when the same call recurs ≥3×.
|
|
199
265
|
function _recordToolCall(signature) {
|
|
@@ -910,11 +976,14 @@ const handlers = {
|
|
|
910
976
|
else if (topGraph.label === 'Class' || topGraph.label === 'Interface') agent = 'system-architect';
|
|
911
977
|
// Functions, files, methods → coder
|
|
912
978
|
else agent = 'coder';
|
|
979
|
+
// Scale confidence by graph degree: well-connected nodes are stronger anchors.
|
|
980
|
+
var topDeg = topGraph.deg || 0;
|
|
981
|
+
var graphConf = topDeg > 30 ? 0.80 : (topDeg > 10 ? 0.75 : 0.70);
|
|
913
982
|
result = Object.assign({}, result, {
|
|
914
983
|
agent: agent,
|
|
915
984
|
agentSlug: agent,
|
|
916
|
-
confidence:
|
|
917
|
-
reason: 'Graph fallback: top file ' + (topGraph.name || '').substring(0, 30) + ' [' + topGraph.label + ']',
|
|
985
|
+
confidence: graphConf,
|
|
986
|
+
reason: 'Graph fallback: top file ' + (topGraph.name || '').substring(0, 30) + ' [' + topGraph.label + '] deg=' + topDeg,
|
|
918
987
|
specificAgents: [],
|
|
919
988
|
extrasMatches: [],
|
|
920
989
|
});
|
|
@@ -1133,11 +1202,46 @@ const handlers = {
|
|
|
1133
1202
|
// Pre-resolve top-5 relevant files for the user's prompt — the LLM
|
|
1134
1203
|
// sees the answer inline instead of being told to call a tool.
|
|
1135
1204
|
var suggestions = getMonographSuggestions(prompt, 5);
|
|
1205
|
+
|
|
1206
|
+
// Boost recently-edited files to the top of pre-resolve suggestions.
|
|
1207
|
+
// Even when the FTS index hasn't caught up to the latest edits, the
|
|
1208
|
+
// LLM should see the files it just modified as the primary context.
|
|
1209
|
+
try {
|
|
1210
|
+
var recentEditsForRoute = _getRecentEdits();
|
|
1211
|
+
if (recentEditsForRoute.length > 0) {
|
|
1212
|
+
// Extract prompt keywords for relevance gating
|
|
1213
|
+
var promptWords = (prompt || '').toLowerCase().match(/[a-z][a-z0-9_-]{2,}/g) || [];
|
|
1214
|
+
var promptWordSet = {};
|
|
1215
|
+
for (var pw = 0; pw < promptWords.length; pw++) promptWordSet[promptWords[pw]] = 1;
|
|
1216
|
+
|
|
1217
|
+
var existingFiles = {};
|
|
1218
|
+
for (var se = 0; se < suggestions.length; se++) existingFiles[suggestions[se].file || ''] = 1;
|
|
1219
|
+
|
|
1220
|
+
var editBoosts = [];
|
|
1221
|
+
for (var re = 0; re < recentEditsForRoute.length && editBoosts.length < 2; re++) {
|
|
1222
|
+
var reFile = recentEditsForRoute[re].file;
|
|
1223
|
+
// Skip if already in suggestions
|
|
1224
|
+
if (existingFiles[reFile]) continue;
|
|
1225
|
+
var reName = path.basename(reFile, path.extname(reFile)).toLowerCase();
|
|
1226
|
+
// Only boost if filename shares a keyword with the prompt OR the edit is very recent (<3 min)
|
|
1227
|
+
var veryRecent = (Date.now() - recentEditsForRoute[re].editedAt) < 3 * 60 * 1000;
|
|
1228
|
+
var matches = promptWordSet[reName] || veryRecent;
|
|
1229
|
+
if (matches) {
|
|
1230
|
+
editBoosts.push({ name: path.basename(reFile), label: 'File', file: reFile, deg: 0, _editBoost: true });
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
if (editBoosts.length > 0) {
|
|
1234
|
+
suggestions = editBoosts.concat(suggestions).slice(0, 5);
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
} catch (e) { /* non-fatal */ }
|
|
1238
|
+
|
|
1136
1239
|
if (suggestions.length > 0) {
|
|
1137
1240
|
console.log('\n[MONOGRAPH] ' + nodeCount + ' nodes indexed. Top files for this task (pre-resolved from graph):');
|
|
1138
1241
|
for (var si = 0; si < suggestions.length; si++) {
|
|
1139
1242
|
var s = suggestions[si];
|
|
1140
|
-
|
|
1243
|
+
var editTag = s._editBoost ? ' ✎' : '';
|
|
1244
|
+
console.log(' · ' + s.name + ' [' + s.label + '] — ' + (s.file || '') + (s.deg ? ' (deg ' + s.deg + ')' : '') + editTag);
|
|
1141
1245
|
}
|
|
1142
1246
|
console.log(' Use mcp__monomind__monograph_query / monograph_impact for deeper drill-down.');
|
|
1143
1247
|
_recordGraphTelemetry('preresolve_hit');
|
|
@@ -1347,6 +1451,12 @@ const handlers = {
|
|
|
1347
1451
|
intelligence.recordEdit(file);
|
|
1348
1452
|
} catch (e) { /* non-fatal */ }
|
|
1349
1453
|
}
|
|
1454
|
+
// Track recently-edited files for compact injection and pre-resolve boosting.
|
|
1455
|
+
try {
|
|
1456
|
+
var editedForRecent = hookInput.file_path || toolInput.file_path
|
|
1457
|
+
|| process.env.TOOL_INPUT_file_path || args[0] || '';
|
|
1458
|
+
if (editedForRecent) _recordRecentEdit(editedForRecent);
|
|
1459
|
+
} catch (e) { /* non-fatal */ }
|
|
1350
1460
|
// Increment write counter and rebuild monograph when threshold hit.
|
|
1351
1461
|
_maybeRebuildMonograph();
|
|
1352
1462
|
|
|
@@ -143,11 +143,46 @@ function matchSkills(task, topN = 5) {
|
|
|
143
143
|
.slice(0, topN);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
// Categories that are almost never relevant in a software project context.
|
|
147
|
+
// We exclude these from extras suggestions UNLESS the prompt has explicit
|
|
148
|
+
// keywords that opt them back in. Default-conservative: in a code repo,
|
|
149
|
+
// an "Anthropologist" for "community docs" or "Unity Architect" for a
|
|
150
|
+
// "community module" is noise, not signal.
|
|
151
|
+
const _NON_DEV_EXTRA_CATEGORIES = new Set([
|
|
152
|
+
'academic', // Anthropologist, Geographer, Historian, Narratologist, Psychologist
|
|
153
|
+
'game-development', // Unity/Unreal/Godot/Blender — irrelevant for non-game codebases
|
|
154
|
+
'marketing',
|
|
155
|
+
'sales',
|
|
156
|
+
'paid-media',
|
|
157
|
+
'design', // brand/UI/UX specialists — irrelevant for code reviews
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
// Per-category opt-in keywords. If the prompt mentions one of these, that
|
|
161
|
+
// category is allowed back into extras.
|
|
162
|
+
const _CATEGORY_OPTIN_KEYWORDS = {
|
|
163
|
+
'academic': /\b(anthropolog|sociolog|cultural|ritual|kinship|ethnograph|histor(?:y|ical|ian)|geograph|narratolog|psycholog|belief\s*system)\b/i,
|
|
164
|
+
'game-development': /\b(unity|unreal|godot|blender|game\s*(?:design|dev|engine)|gameplay|shader\s*graph|level\s*design|ugc|roblox)\b/i,
|
|
165
|
+
'marketing': /\b(marketing|advertis|seo|tiktok|instagram|linkedin\s*content|brand|campaign|growth\s*hack|copywrit|blog\s*post|press\s*release|launch\s*strategy|pitch|investor)\b/i,
|
|
166
|
+
'sales': /\b(sales|prospect|outbound|pipeline|crm|cold\s*email|deal|account\s*strategy|quota|forecast|qbr)\b/i,
|
|
167
|
+
'paid-media': /\b(paid\s*media|ads?|google\s*ads|meta\s*ads|programmatic|ppc|cpa|conversion\s*tracking)\b/i,
|
|
168
|
+
'design': /\b(figma|brand\s*identity|design\s*system|ux\s*research|persona|usability|wireframe|prototype|color\s*palette|typography|moodboard)\b/i,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
function _allowedExtraCategories(taskLower) {
|
|
172
|
+
const allowed = new Set();
|
|
173
|
+
for (const [cat, re] of Object.entries(_CATEGORY_OPTIN_KEYWORDS)) {
|
|
174
|
+
if (re.test(taskLower)) allowed.add(cat);
|
|
175
|
+
}
|
|
176
|
+
return allowed;
|
|
177
|
+
}
|
|
178
|
+
|
|
146
179
|
function matchExtras(task, topN = 8) {
|
|
147
180
|
if (typeof task !== 'string') return [];
|
|
148
181
|
const registry = loadExtrasRegistry();
|
|
149
182
|
const taskLower = task.toLowerCase();
|
|
183
|
+
const optedIn = _allowedExtraCategories(taskLower);
|
|
150
184
|
return registry.extras
|
|
185
|
+
.filter(e => !_NON_DEV_EXTRA_CATEGORIES.has(e.category) || optedIn.has(e.category))
|
|
151
186
|
.map(e => ({ slug: e.slug, name: e.name, description: e.description, category: e.category, filePath: e.filePath, score: scoreEntry(e.keywords, taskLower) }))
|
|
152
187
|
.filter(e => e.score > 0)
|
|
153
188
|
.sort((a, b) => b.score - a.score)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monomind",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.27",
|
|
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,65 @@ 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 os = require('os');
|
|
2178
|
+
const home = os.homedir();
|
|
2179
|
+
const searchRoots = [
|
|
2180
|
+
path.join(home, 'Desktop'),
|
|
2181
|
+
path.join(home, 'projects'),
|
|
2182
|
+
path.join(home, 'code'),
|
|
2183
|
+
path.join(home, 'work'),
|
|
2184
|
+
path.join(home, 'dev'),
|
|
2185
|
+
path.join(home, 'repos'),
|
|
2186
|
+
path.join(home, 'src'),
|
|
2187
|
+
].filter(r => fs.existsSync(r));
|
|
2188
|
+
// Also check known-projects registry if it exists
|
|
2189
|
+
const registryPath = path.join(home, '.monomind-projects.json');
|
|
2190
|
+
if (fs.existsSync(registryPath)) {
|
|
2191
|
+
try {
|
|
2192
|
+
const reg = JSON.parse(fs.readFileSync(registryPath, 'utf-8'));
|
|
2193
|
+
if (Array.isArray(reg.projects)) {
|
|
2194
|
+
for (const p of reg.projects) {
|
|
2195
|
+
if (!searchRoots.includes(p) && fs.existsSync(p))
|
|
2196
|
+
searchRoots.push(p);
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
}
|
|
2200
|
+
catch { }
|
|
2201
|
+
}
|
|
2202
|
+
const found = new Set();
|
|
2203
|
+
function walk(dir, depth) {
|
|
2204
|
+
if (depth > maxDepth)
|
|
2205
|
+
return;
|
|
2206
|
+
const marker = path.join(dir, '.claude', 'helpers', 'hook-handler.cjs');
|
|
2207
|
+
if (fs.existsSync(marker)) {
|
|
2208
|
+
found.add(dir);
|
|
2209
|
+
return;
|
|
2210
|
+
} // don't recurse into a monomind project
|
|
2211
|
+
let entries;
|
|
2212
|
+
try {
|
|
2213
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
2214
|
+
}
|
|
2215
|
+
catch {
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
for (const e of entries) {
|
|
2219
|
+
if (!e.isDirectory())
|
|
2220
|
+
continue;
|
|
2221
|
+
if (e.name.startsWith('.') || e.name === 'node_modules')
|
|
2222
|
+
continue;
|
|
2223
|
+
walk(path.join(dir, e.name), depth + 1);
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
for (const root of searchRoots) {
|
|
2227
|
+
walk(root, 0);
|
|
2228
|
+
}
|
|
2229
|
+
return [...found];
|
|
2230
|
+
}
|
|
2171
2231
|
export default executeInit;
|
|
2172
2232
|
//# 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.27",
|
|
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,59 @@ 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
|
+
const updateNode = db.prepare(`UPDATE nodes SET properties = ? WHERE id = ?`);
|
|
658
|
+
let written = 0;
|
|
659
|
+
const tx = db.transaction(() => {
|
|
660
|
+
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
|
+
})();
|
|
665
|
+
const merged = {
|
|
666
|
+
...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 } : {}),
|
|
672
|
+
ua_analyzed_at: new Date().toISOString(),
|
|
673
|
+
};
|
|
674
|
+
updateNode.run(JSON.stringify(merged), analysis.id);
|
|
675
|
+
written++;
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
tx();
|
|
679
|
+
// Rebuild FTS so summaries are immediately searchable
|
|
680
|
+
try { db.prepare(`INSERT INTO nodes_fts(nodes_fts) VALUES('rebuild')`).run(); } catch {}
|
|
681
|
+
mg.closeDb(db);
|
|
682
|
+
console.log(`[understand] Imported ${written} analyses from stdin. FTS rebuilt.`);
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
|
|
632
686
|
// ── Load all file nodes ──────────────────────────────────────────────────
|
|
633
687
|
let fileNodes = db.prepare(`SELECT id, name, file_path, properties FROM nodes WHERE label = 'File' AND file_path IS NOT NULL`).all();
|
|
634
688
|
console.log(`[understand] Found ${fileNodes.length} file nodes in DB`);
|