@monoes/monomindcli 1.10.11 → 1.10.12

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.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Draft an Architecture Decision Record from accumulated decision markers in this session's prompts
3
+ ---
4
+
5
+ Run the hook handler's adr-draft action to scan `.monomind/decisions.jsonl` for the last 7 days of decision markers (e.g. "let's go with X", "we chose Y", "decision: Z") and produce an ADR template in `docs/adrs/`.
6
+
7
+ ```bash
8
+ node "$CLAUDE_PROJECT_DIR/.claude/helpers/hook-handler.cjs" adr-draft
9
+ ```
10
+
11
+ After running, open the generated `docs/adrs/ADR-NNNN-YYYY-MM-DD-session-decisions.md`, fill in the Context and Consequences sections, and change Status to Accepted/Rejected.
@@ -62,6 +62,8 @@ function getMonographSuggestions(taskText, limit) {
62
62
  try {
63
63
  // BM25 ranks better than degree for keyword relevance; tie-break by deg.
64
64
  // File/Function/Class outrank Section so navigable nodes win.
65
+ // Filter out anonymous lambdas, arrow expressions, and other unnamed
66
+ // garbage that the AST extraction picks up but isn't navigable.
65
67
  rows = db.prepare(
66
68
  "SELECT n.id, n.name, n.label, n.file_path AS file, " +
67
69
  "bm25(nodes_fts) AS bm25_score, " +
@@ -71,6 +73,8 @@ function getMonographSuggestions(taskText, limit) {
71
73
  "FROM nodes_fts f JOIN nodes n ON f.rowid = n.rowid " +
72
74
  "WHERE nodes_fts MATCH ? AND n.file_path IS NOT NULL AND n.file_path != '' " +
73
75
  "AND n.label NOT IN ('Concept') " +
76
+ "AND n.name NOT LIKE '(%' AND n.name NOT LIKE '%=>%' AND n.name != 'function' " +
77
+ "AND length(n.name) >= 3 " +
74
78
  "ORDER BY label_rank DESC, bm25_score ASC, deg DESC LIMIT ?"
75
79
  ).all(ftsQuery, lim);
76
80
  } catch (e) {
@@ -157,6 +161,33 @@ function _recordGraphTelemetry(event) {
157
161
  } catch (e) { /* non-fatal */ }
158
162
  }
159
163
 
164
+ // Re-inject graph god nodes after compaction so the LLM doesn't lose its spatial map.
165
+ function _injectCompactGraphMap() {
166
+ try {
167
+ var db = _openMonographDb();
168
+ if (!db) return;
169
+ try {
170
+ var nodeC = db.prepare("SELECT COUNT(*) AS c FROM nodes").get().c;
171
+ var gods = db.prepare(
172
+ "SELECT n.name, n.label, n.file_path, " +
173
+ "(SELECT COUNT(*) FROM edges WHERE source_id=n.id OR target_id=n.id) AS deg " +
174
+ "FROM nodes n " +
175
+ "WHERE n.label NOT IN ('Concept') AND n.file_path IS NOT NULL AND n.file_path != '' " +
176
+ "AND n.name NOT LIKE '(%' AND n.name NOT LIKE '%=>%' AND length(n.name) >= 3 " +
177
+ "ORDER BY deg DESC LIMIT 8"
178
+ ).all();
179
+ if (gods.length > 0) {
180
+ console.log('[COMPACT_GRAPH] ' + nodeC + ' nodes available. Top spatial anchors:');
181
+ for (var ci = 0; ci < gods.length; ci++) {
182
+ var g = gods[ci];
183
+ console.log(' · ' + g.name + ' [' + g.label + '] — ' + g.file_path + ' (deg ' + g.deg + ')');
184
+ }
185
+ console.log(' Use mcp__monomind__monograph_suggest first when navigating.');
186
+ }
187
+ } finally { try { db.close(); } catch (_) {} }
188
+ } catch (e) {}
189
+ }
190
+
160
191
  // ── Loop drift detection ───────────────────────────────────────────────────────
161
192
  // Record tool call signatures per session, warn when the same call recurs ≥3×.
162
193
  function _recordToolCall(signature) {
@@ -2080,11 +2111,9 @@ const handlers = {
2080
2111
  },
2081
2112
 
2082
2113
  'compact-manual': async () => {
2083
- // Consolidate intelligence before compaction so patterns survive
2084
2114
  if (intelligence && intelligence.consolidate) {
2085
2115
  try { await runWithTimeout(function() { return intelligence.consolidate(); }, 'intelligence.consolidate()'); } catch (e) { /* non-fatal */ }
2086
2116
  }
2087
- // Save current routing context for post-compact restore
2088
2117
  try {
2089
2118
  var lastRoute = path.join(CWD, '.monomind', 'last-route.json');
2090
2119
  if (fs.existsSync(lastRoute)) {
@@ -2092,11 +2121,11 @@ const handlers = {
2092
2121
  console.log('[COMPACT_CONTEXT] Last route: ' + route.agent + ' (' + (route.confidence != null ? (route.confidence * 100).toFixed(0) : '?') + '%)');
2093
2122
  }
2094
2123
  } catch (e) { /* non-fatal */ }
2124
+ _injectCompactGraphMap();
2095
2125
  console.log('[COMPACT] Manual compaction — intelligence consolidated, context preserved');
2096
2126
  },
2097
2127
 
2098
2128
  'compact-auto': async () => {
2099
- // Same consolidation for auto-compact
2100
2129
  if (intelligence && intelligence.consolidate) {
2101
2130
  try { await runWithTimeout(function() { return intelligence.consolidate(); }, 'intelligence.consolidate()'); } catch (e) { /* non-fatal */ }
2102
2131
  }
@@ -2107,6 +2136,7 @@ const handlers = {
2107
2136
  console.log('[COMPACT_CONTEXT] Last route: ' + route.agent + ' (' + (route.confidence != null ? (route.confidence * 100).toFixed(0) : '?') + '%)');
2108
2137
  }
2109
2138
  } catch (e) { /* non-fatal */ }
2139
+ _injectCompactGraphMap();
2110
2140
  console.log('[COMPACT] Auto compaction — intelligence consolidated, context preserved');
2111
2141
  console.log('GOLDEN RULE: 1 message = all parallel operations');
2112
2142
  },
@@ -2199,6 +2229,62 @@ const handlers = {
2199
2229
  console.log('[OK] Agent registered');
2200
2230
  },
2201
2231
 
2232
+ // Draft an ADR from accumulated decision markers in .monomind/decisions.jsonl.
2233
+ // Usage: node hook-handler.cjs adr-draft (or via /adr slash command)
2234
+ 'adr-draft': () => {
2235
+ var jsonl = path.join(CWD, '.monomind', 'decisions.jsonl');
2236
+ if (!fs.existsSync(jsonl)) {
2237
+ console.log('[ADR] No decisions recorded yet. Type prompts containing markers like "let\'s go with X", "we chose Y", "decision: Z" to populate the log.');
2238
+ return;
2239
+ }
2240
+ var lines = fs.readFileSync(jsonl, 'utf-8').trim().split('\n').filter(Boolean);
2241
+ if (lines.length === 0) {
2242
+ console.log('[ADR] decisions.jsonl is empty.');
2243
+ return;
2244
+ }
2245
+ // Group decisions captured in the last 7 days
2246
+ var cutoff = Date.now() - 7 * 24 * 60 * 60 * 1000;
2247
+ var recent = lines.map(function(l) { try { return JSON.parse(l); } catch (_) { return null; } })
2248
+ .filter(function(d) { return d && d.ts >= cutoff; });
2249
+ if (recent.length === 0) {
2250
+ console.log('[ADR] No decisions in the last 7 days. Older entries: ' + lines.length + '.');
2251
+ return;
2252
+ }
2253
+
2254
+ var adrsDir = path.join(CWD, 'docs', 'adrs');
2255
+ try { fs.mkdirSync(adrsDir, { recursive: true }); } catch (_) {}
2256
+ // Pick next ADR number
2257
+ var existing = [];
2258
+ try { existing = fs.readdirSync(adrsDir).filter(function(f) { return /^ADR-\d{4}/.test(f); }); } catch (_) {}
2259
+ var nextNum = existing.length + 1;
2260
+ var num = String(nextNum).padStart(4, '0');
2261
+ var stamp = new Date().toISOString().slice(0,10);
2262
+ var slug = 'session-decisions';
2263
+ var fname = 'ADR-' + num + '-' + stamp + '-' + slug + '.md';
2264
+ var outPath = path.join(adrsDir, fname);
2265
+
2266
+ var body = '# ADR-' + num + ': Session decisions (' + stamp + ')\n\n' +
2267
+ '**Status:** Proposed\n**Date:** ' + stamp + '\n\n' +
2268
+ '## Context\n\n' +
2269
+ 'During recent sessions, the following decision markers were captured ' +
2270
+ 'from user prompts. Each excerpt is the surrounding sentence at the time.\n\n' +
2271
+ '## Decisions\n\n';
2272
+ for (var i = 0; i < recent.length; i++) {
2273
+ var d = recent[i];
2274
+ var date = new Date(d.ts).toISOString().slice(0,16).replace('T',' ');
2275
+ body += '### ' + (i + 1) + '. ' + date + '\n\n';
2276
+ for (var j = 0; j < d.excerpts.length; j++) {
2277
+ body += '> ' + d.excerpts[j].trim() + '\n\n';
2278
+ }
2279
+ if (d.prompt) body += '_Prompt:_ ' + d.prompt.slice(0, 200) + (d.prompt.length > 200 ? '…' : '') + '\n\n';
2280
+ }
2281
+ body += '## Consequences\n\n_(fill in after review)_\n\n' +
2282
+ '## Status\n\nProposed — awaiting human review and refinement.\n';
2283
+ fs.writeFileSync(outPath, body);
2284
+ console.log('[ADR_DRAFT] Wrote ' + recent.length + ' decision(s) to ' + outPath);
2285
+ console.log(' Edit the file to fill in Context and Consequences, then change Status to Accepted/Rejected.');
2286
+ },
2287
+
2202
2288
  'status': () => {
2203
2289
  console.log('[OK] Status check');
2204
2290
  },
@@ -2128,6 +2128,70 @@ export async function startServer({ port = 4242, projectDir, openBrowser = true
2128
2128
  return;
2129
2129
  }
2130
2130
 
2131
+ // ------------------------------------------------- Monograph
2132
+ // GET /api/monograph — node/edge counts, top god nodes, type distribution.
2133
+ // (Distinct from /api/graph which serves session/journal graph data.)
2134
+ // Reads .monomind/monograph.db via sqlite3 CLI to avoid bundling better-sqlite3.
2135
+ if (req.method === 'GET' && url === '/api/monograph') {
2136
+ try {
2137
+ const dbPath = path.join(projectDir || process.cwd(), '.monomind', 'monograph.db');
2138
+ if (!fs.existsSync(dbPath)) {
2139
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2140
+ res.end(JSON.stringify({ exists: false }));
2141
+ return;
2142
+ }
2143
+ const { execSync } = await import('child_process');
2144
+ // Pipe SQL via stdin to avoid shell quoting issues with single-quoted SQL strings.
2145
+ const runSql = (sql, timeout = 5000) => {
2146
+ try {
2147
+ return execSync(`sqlite3 -json "${dbPath}"`,
2148
+ { encoding: 'utf-8', timeout: timeout, input: sql + ';' });
2149
+ } catch (e) { return '[]'; }
2150
+ };
2151
+ const counts = JSON.parse(runSql(
2152
+ "SELECT (SELECT COUNT(*) FROM nodes) AS nodes, (SELECT COUNT(*) FROM edges) AS edges;"
2153
+ ) || '[{}]')[0] || { nodes: 0, edges: 0 };
2154
+ // Compute degree in one pass via GROUP BY (much faster than per-row subquery).
2155
+ const gods = JSON.parse(runSql(
2156
+ "WITH deg(node_id, d) AS (" +
2157
+ " SELECT source_id, COUNT(*) FROM edges GROUP BY source_id " +
2158
+ " UNION ALL " +
2159
+ " SELECT target_id, COUNT(*) FROM edges GROUP BY target_id" +
2160
+ "), totals AS (" +
2161
+ " SELECT node_id, SUM(d) AS deg FROM deg GROUP BY node_id" +
2162
+ ") " +
2163
+ "SELECT n.name, n.label, n.file_path, t.deg " +
2164
+ "FROM nodes n JOIN totals t ON t.node_id = n.id " +
2165
+ "WHERE n.label NOT IN ('Concept') " +
2166
+ "AND n.file_path IS NOT NULL AND n.file_path != '' " +
2167
+ "AND n.name NOT LIKE '(%' AND length(n.name) >= 3 " +
2168
+ "ORDER BY t.deg DESC LIMIT 20",
2169
+ 10000
2170
+ ) || '[]');
2171
+ const types = JSON.parse(runSql(
2172
+ "SELECT label, COUNT(*) AS count FROM nodes GROUP BY label ORDER BY count DESC LIMIT 12"
2173
+ ) || '[]');
2174
+ const relations = JSON.parse(runSql(
2175
+ "SELECT relation, COUNT(*) AS count FROM edges GROUP BY relation ORDER BY count DESC"
2176
+ ) || '[]');
2177
+
2178
+ res.writeHead(200, { 'Content-Type': 'application/json' });
2179
+ res.end(JSON.stringify({
2180
+ exists: true,
2181
+ nodes: counts.nodes,
2182
+ edges: counts.edges,
2183
+ godNodes: gods,
2184
+ typeDistribution: types,
2185
+ relationDistribution: relations,
2186
+ updatedAt: fs.statSync(dbPath).mtime,
2187
+ }));
2188
+ } catch (err) {
2189
+ res.writeHead(500, { 'Content-Type': 'application/json' });
2190
+ res.end(JSON.stringify({ error: String(err) }));
2191
+ }
2192
+ return;
2193
+ }
2194
+
2131
2195
  // ------------------------------------------------- Org management
2132
2196
  // GET /api/orgs — list all saved org configs
2133
2197
  if (req.method === 'GET' && url === '/api/orgs') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monomindcli",
3
- "version": "1.10.11",
3
+ "version": "1.10.12",
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",