monomind 1.10.17 → 1.10.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monomind",
3
- "version": "1.10.17",
3
+ "version": "1.10.19",
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",
@@ -27,6 +27,8 @@
27
27
  "packages/@monomind/cli/scripts/**/*.js",
28
28
  "packages/@monomind/cli/scripts/**/*.sh",
29
29
  "packages/@monomind/cli/package.json",
30
+ "scripts/*.mjs",
31
+ "scripts/*.sh",
30
32
  "packages/@monomind/shared/dist/**/*.js",
31
33
  "packages/@monomind/shared/dist/**/*.d.ts",
32
34
  "!packages/@monomind/shared/dist/**/*.map",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monoes/monomindcli",
3
- "version": "1.10.17",
3
+ "version": "1.10.19",
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",
@@ -0,0 +1,392 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Monomind Installer (formerly Monomind)
4
+ # https://github.com/nokhodian/monomind
5
+ #
6
+ # Usage:
7
+ # curl -fsSL https://cdn.jsdelivr.net/gh/nokhodian/monomind@main/scripts/install.sh | bash
8
+ # curl -fsSL https://cdn.jsdelivr.net/gh/nokhodian/monomind@main/scripts/install.sh | bash -s -- --full
9
+ # curl -fsSL https://cdn.jsdelivr.net/gh/nokhodian/monomind@main/scripts/install.sh | bash -s -- --global
10
+ # curl -fsSL https://cdn.jsdelivr.net/gh/nokhodian/monomind@main/scripts/install.sh | bash -s -- --minimal
11
+ #
12
+ # Options (via arguments):
13
+ # --global Global install (npm install -g)
14
+ # --minimal Minimal install (no optional deps)
15
+ # --full Full setup (global + MCP + doctor + init)
16
+ # --version=X.X.X Specific version
17
+ #
18
+ # Options (via environment - requires export):
19
+ # export MONOMIND_VERSION=alpha
20
+ # export MONOMIND_MINIMAL=1
21
+ # export MONOMIND_GLOBAL=1
22
+ #
23
+
24
+ set -euo pipefail
25
+
26
+ # Colors
27
+ RED='\033[0;31m'
28
+ GREEN='\033[0;32m'
29
+ YELLOW='\033[1;33m'
30
+ BLUE='\033[0;34m'
31
+ CYAN='\033[0;36m'
32
+ BOLD='\033[1m'
33
+ DIM='\033[2m'
34
+ NC='\033[0m' # No Color
35
+
36
+ # Default configuration (can be overridden by env vars)
37
+ VERSION="${MONOMIND_VERSION:-${MONOMIND_VERSION:-latest}}"
38
+ MINIMAL="${MONOMIND_MINIMAL:-0}"
39
+ GLOBAL="${MONOMIND_GLOBAL:-0}"
40
+ SETUP_MCP="${MONOMIND_SETUP_MCP:-0}"
41
+ RUN_DOCTOR="${MONOMIND_DOCTOR:-0}"
42
+ RUN_INIT="${MONOMIND_INIT:-1}"
43
+
44
+ # Parse command line arguments
45
+ while [[ $# -gt 0 ]]; do
46
+ case $1 in
47
+ --global|-g)
48
+ GLOBAL="1"
49
+ shift
50
+ ;;
51
+ --minimal|-m)
52
+ MINIMAL="1"
53
+ shift
54
+ ;;
55
+ --setup-mcp|--mcp)
56
+ SETUP_MCP="1"
57
+ shift
58
+ ;;
59
+ --doctor|-d)
60
+ RUN_DOCTOR="1"
61
+ shift
62
+ ;;
63
+ --init|-i)
64
+ RUN_INIT="1"
65
+ shift
66
+ ;;
67
+ --no-init)
68
+ RUN_INIT="0"
69
+ shift
70
+ ;;
71
+ --full|-f)
72
+ GLOBAL="1"
73
+ SETUP_MCP="1"
74
+ RUN_DOCTOR="1"
75
+ RUN_INIT="1"
76
+ shift
77
+ ;;
78
+ --version=*)
79
+ VERSION="${1#*=}"
80
+ shift
81
+ ;;
82
+ --help|-h)
83
+ echo "Monomind Installer"
84
+ echo ""
85
+ echo "Usage: curl -fsSL .../install.sh | bash -s -- [OPTIONS]"
86
+ echo ""
87
+ echo "Options:"
88
+ echo " --global, -g Install globally (npm install -g monomind)"
89
+ echo " --minimal, -m Minimal install (skip optional deps)"
90
+ echo " --setup-mcp Auto-configure MCP server for Claude Code"
91
+ echo " --doctor, -d Run diagnostics after install"
92
+ echo " --no-init Skip project initialization (enabled by default)"
93
+ echo " --full, -f Full setup (global + mcp + doctor + init)"
94
+ echo " --version=X.X.X Install specific version (default: alpha)"
95
+ echo " --help, -h Show this help"
96
+ exit 0
97
+ ;;
98
+ *)
99
+ shift
100
+ ;;
101
+ esac
102
+ done
103
+
104
+ PACKAGE="monomind@${VERSION}"
105
+
106
+ # Progress animation
107
+ SPINNER_CHARS="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
108
+ SPINNER_INDEX=0
109
+
110
+ spinner() {
111
+ printf "\r${CYAN}${SPINNER_CHARS:SPINNER_INDEX++:1}${NC} $1"
112
+ SPINNER_INDEX=$((SPINNER_INDEX % 10))
113
+ }
114
+
115
+ print_banner() {
116
+ echo ""
117
+ echo -e "${CYAN}╔═══════════════════════════════════════════════════════════╗${NC}"
118
+ echo -e "${CYAN}║${NC} ${BOLD}Monomind${NC} — AI Agent Orchestration for Claude Code ${CYAN}║${NC}"
119
+ echo -e "${CYAN}╚═══════════════════════════════════════════════════════════╝${NC}"
120
+ echo ""
121
+ }
122
+
123
+ print_step() {
124
+ echo -e "${GREEN}▸${NC} $1"
125
+ }
126
+
127
+ print_substep() {
128
+ echo -e " ${DIM}├─${NC} $1"
129
+ }
130
+
131
+ print_success() {
132
+ echo -e "${GREEN}✓${NC} $1"
133
+ }
134
+
135
+ print_warning() {
136
+ echo -e "${YELLOW}⚠${NC} $1"
137
+ }
138
+
139
+ print_error() {
140
+ echo -e "${RED}✗${NC} $1"
141
+ }
142
+
143
+ print_info() {
144
+ echo -e "${BLUE}ℹ${NC} $1"
145
+ }
146
+
147
+ check_requirements() {
148
+ print_step "Checking requirements..."
149
+
150
+ # Check Node.js
151
+ if command -v node &> /dev/null; then
152
+ NODE_VERSION=$(node -v | sed 's/v//')
153
+ NODE_MAJOR=$(echo "$NODE_VERSION" | cut -d. -f1)
154
+ if [ "$NODE_MAJOR" -ge 20 ]; then
155
+ print_substep "Node.js ${GREEN}v${NODE_VERSION}${NC} ✓"
156
+ else
157
+ print_error "Node.js 20+ required (found v${NODE_VERSION})"
158
+ echo ""
159
+ echo "Install Node.js 20+:"
160
+ echo " curl -fsSL https://fnm.vercel.app/install | bash"
161
+ echo " fnm install 20"
162
+ exit 1
163
+ fi
164
+ else
165
+ print_error "Node.js not found"
166
+ echo ""
167
+ echo "Install Node.js 20+:"
168
+ echo " curl -fsSL https://fnm.vercel.app/install | bash"
169
+ echo " fnm install 20"
170
+ exit 1
171
+ fi
172
+
173
+ # Check npm
174
+ if command -v npm &> /dev/null; then
175
+ NPM_VERSION=$(npm -v)
176
+ print_substep "npm ${GREEN}v${NPM_VERSION}${NC} ✓"
177
+ else
178
+ print_error "npm not found"
179
+ exit 1
180
+ fi
181
+
182
+ # Check Claude Code CLI
183
+ if command -v claude &> /dev/null; then
184
+ CLAUDE_VERSION=$(claude --version 2>/dev/null | head -1 || echo "installed")
185
+ print_substep "Claude Code ${GREEN}${CLAUDE_VERSION}${NC} ✓"
186
+ else
187
+ print_warning "Claude Code CLI not found"
188
+ print_substep "Installing Claude Code CLI via npm..."
189
+ if npm install -g @anthropic-ai/claude-code 2>/dev/null; then
190
+ if command -v claude &> /dev/null; then
191
+ CLAUDE_VERSION=$(claude --version 2>/dev/null | head -1 || echo "installed")
192
+ print_substep "Claude Code ${GREEN}${CLAUDE_VERSION}${NC} ✓"
193
+ else
194
+ print_substep "Installed. Restart terminal to use 'claude' command"
195
+ fi
196
+ else
197
+ print_warning "npm install failed. Try manually:"
198
+ print_substep "${BOLD}npm install -g @anthropic-ai/claude-code${NC}"
199
+ fi
200
+ fi
201
+
202
+ echo ""
203
+ }
204
+
205
+ show_install_options() {
206
+ print_step "Installation options:"
207
+ print_substep "Package: ${BOLD}${PACKAGE}${NC}"
208
+ if [ "$GLOBAL" = "1" ]; then
209
+ print_substep "Mode: ${BOLD}Global${NC} (npm install -g)"
210
+ else
211
+ print_substep "Mode: ${BOLD}npx${NC} (on-demand)"
212
+ fi
213
+ if [ "$MINIMAL" = "1" ]; then
214
+ print_substep "Profile: ${BOLD}Minimal${NC} (--omit=optional)"
215
+ else
216
+ print_substep "Profile: ${BOLD}Full${NC} (all features)"
217
+ fi
218
+ echo ""
219
+ }
220
+
221
+ install_package() {
222
+ local START_TIME=$(date +%s)
223
+
224
+ if [ "$GLOBAL" = "1" ]; then
225
+ print_step "Installing globally..."
226
+
227
+ if [ "$MINIMAL" = "1" ]; then
228
+ npm install -g "$PACKAGE" --omit=optional 2>&1 | while read -r line; do
229
+ if [[ "$line" == *"added"* ]]; then
230
+ print_substep "$line"
231
+ fi
232
+ done
233
+ else
234
+ npm install -g "$PACKAGE" 2>&1 | while read -r line; do
235
+ if [[ "$line" == *"added"* ]]; then
236
+ print_substep "$line"
237
+ fi
238
+ done
239
+ fi
240
+ else
241
+ print_step "Installing for npx usage..."
242
+ # Actually run npx to pre-install the package
243
+ npx -y "$PACKAGE" --version >/dev/null 2>&1 || true
244
+ print_substep "Package installed for npx"
245
+ fi
246
+
247
+ local END_TIME=$(date +%s)
248
+ local DURATION=$((END_TIME - START_TIME))
249
+
250
+ echo ""
251
+ print_success "Installed in ${BOLD}${DURATION}s${NC}"
252
+ }
253
+
254
+ verify_installation() {
255
+ print_step "Verifying installation..."
256
+
257
+ local VERSION_OUTPUT
258
+ if [ "$GLOBAL" = "1" ]; then
259
+ VERSION_OUTPUT=$(monomind --version 2>/dev/null || monomind --version 2>/dev/null || echo "")
260
+ if [ -z "$VERSION_OUTPUT" ]; then
261
+ print_warning "Global command not found in PATH"
262
+ print_substep "Try: ${BOLD}npm install -g monomind@${VERSION}${NC}"
263
+ return 0 # Don't fail - npm might need PATH refresh
264
+ fi
265
+ else
266
+ # For npx mode, package was already installed during install_package
267
+ VERSION_OUTPUT=$(npx "$PACKAGE" --version 2>/dev/null || echo "")
268
+ fi
269
+
270
+ if [ -n "$VERSION_OUTPUT" ]; then
271
+ print_substep "Version: ${GREEN}${VERSION_OUTPUT}${NC}"
272
+ echo ""
273
+ return 0
274
+ else
275
+ print_error "Installation verification failed"
276
+ return 1
277
+ fi
278
+ }
279
+
280
+ show_quickstart() {
281
+ echo -e "${CYAN}╔═══════════════════════════════════════════════════════════╗${NC}"
282
+ echo -e "${CYAN}║${NC} ${BOLD}Quick Start${NC} ${CYAN}║${NC}"
283
+ echo -e "${CYAN}╚═══════════════════════════════════════════════════════════╝${NC}"
284
+ echo ""
285
+
286
+ if [ "$GLOBAL" = "1" ]; then
287
+ echo -e " ${DIM}# Initialize project${NC}"
288
+ echo -e " ${BOLD}monomind init --wizard${NC}"
289
+ echo ""
290
+ echo -e " ${DIM}# Run system diagnostics${NC}"
291
+ echo -e " ${BOLD}monomind doctor${NC}"
292
+ echo ""
293
+ echo -e " ${DIM}# Add as MCP server to Claude Code${NC}"
294
+ echo -e " ${BOLD}claude mcp add monomind -- monomind mcp start${NC}"
295
+ else
296
+ echo -e " ${DIM}# Initialize project${NC}"
297
+ echo -e " ${BOLD}npx monomind@latest init --wizard${NC}"
298
+ echo ""
299
+ echo -e " ${DIM}# Run system diagnostics${NC}"
300
+ echo -e " ${BOLD}npx monomind@latest doctor${NC}"
301
+ echo ""
302
+ echo -e " ${DIM}# Add as MCP server to Claude Code${NC}"
303
+ echo -e " ${BOLD}claude mcp add monomind -- npx -y monomind@latest mcp start${NC}"
304
+ fi
305
+
306
+ echo ""
307
+ echo -e "${DIM}Documentation: https://github.com/nokhodian/monomind${NC}"
308
+ echo -e "${DIM}Issues: https://github.com/nokhodian/monomind/issues${NC}"
309
+ echo ""
310
+ }
311
+
312
+ setup_mcp_server() {
313
+ if [ "$SETUP_MCP" != "1" ]; then
314
+ return 0
315
+ fi
316
+
317
+ print_step "Setting up MCP server..."
318
+
319
+ if ! command -v claude &> /dev/null; then
320
+ print_warning "Claude CLI not found, skipping MCP setup"
321
+ return 0
322
+ fi
323
+
324
+ # Check if already configured
325
+ if claude mcp list 2>/dev/null | grep -q "monomind\|monomind"; then
326
+ print_substep "MCP server already configured ✓"
327
+ return 0
328
+ fi
329
+
330
+ # Add MCP server (pass MONOMIND_CWD so tools resolve paths correctly
331
+ # even when the MCP server is spawned with cwd='/')
332
+ if [ "$GLOBAL" = "1" ]; then
333
+ claude mcp add monomind -e MONOMIND_CWD="$HOME" -- monomind mcp start 2>/dev/null && \
334
+ print_substep "MCP server configured ✓" || \
335
+ print_warning "MCP setup failed - run manually: claude mcp add monomind -e MONOMIND_CWD=\"\$HOME\" -- monomind mcp start"
336
+ else
337
+ claude mcp add monomind -e MONOMIND_CWD="$HOME" -- npx -y monomind@${VERSION} mcp start 2>/dev/null && \
338
+ print_substep "MCP server configured ✓" || \
339
+ print_warning "MCP setup failed - run manually: claude mcp add monomind -e MONOMIND_CWD=\"\$HOME\" -- npx -y monomind@latest mcp start"
340
+ fi
341
+ echo ""
342
+ }
343
+
344
+ run_doctor() {
345
+ if [ "$RUN_DOCTOR" != "1" ]; then
346
+ return 0
347
+ fi
348
+
349
+ print_step "Running diagnostics..."
350
+ echo ""
351
+
352
+ if [ "$GLOBAL" = "1" ]; then
353
+ monomind doctor 2>&1 || true
354
+ else
355
+ npx monomind@${VERSION} doctor 2>&1 || true
356
+ fi
357
+ echo ""
358
+ }
359
+
360
+ run_init() {
361
+ if [ "$RUN_INIT" != "1" ]; then
362
+ return 0
363
+ fi
364
+
365
+ print_step "Initializing project..."
366
+ echo ""
367
+
368
+ if [ "$GLOBAL" = "1" ]; then
369
+ monomind init --yes 2>&1 || true
370
+ else
371
+ npx monomind@${VERSION} init --yes 2>&1 || true
372
+ fi
373
+ echo ""
374
+ }
375
+
376
+ # Main
377
+ main() {
378
+ print_banner
379
+ check_requirements
380
+ show_install_options
381
+ install_package
382
+ verify_installation
383
+ setup_mcp_server
384
+ run_doctor
385
+ run_init
386
+ show_quickstart
387
+
388
+ print_success "${BOLD}Monomind is ready!${NC}"
389
+ echo ""
390
+ }
391
+
392
+ main "$@"
@@ -0,0 +1,228 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ua-enrich.mjs — Option C
4
+ *
5
+ * Background enrichment runner that:
6
+ * 1. Runs the Understand-Anything extract-structure.mjs deterministic extraction
7
+ * on changed (or all) files in a project directory.
8
+ * 2. Merges the resulting structural + metadata into an existing monograph DB
9
+ * WITHOUT requiring the full LLM agent pipeline.
10
+ *
11
+ * For full LLM semantic enrichment (summaries, layers), run the /understand
12
+ * skill separately and then call ua-import.mjs on the resulting graph.json.
13
+ *
14
+ * This script is designed to be called from the post-edit or post-build hook
15
+ * as a lightweight, non-LLM enrichment step that runs in <2s.
16
+ *
17
+ * Usage:
18
+ * node scripts/ua-enrich.mjs [--dir <projectDir>] [--file <changedFile>] [--db <monograph.db>]
19
+ *
20
+ * Options:
21
+ * --dir Project root to scan (default: cwd)
22
+ * --file Single file to re-enrich (incremental mode)
23
+ * --db Path to monograph.db (default: <dir>/.monomind/monograph.db)
24
+ * --full Force full scan even if graph.json exists
25
+ *
26
+ * Env:
27
+ * UA_GRAPH_JSON Override path to UA graph.json
28
+ * UA_PLUGIN_DIR Override path to understand-anything-plugin directory
29
+ */
30
+
31
+ import { readFileSync, existsSync, statSync, writeFileSync, mkdirSync } from 'fs';
32
+ import { resolve, join, dirname, basename } from 'path';
33
+ import { createRequire } from 'module';
34
+ import { fileURLToPath } from 'url';
35
+ import { execSync, spawnSync } from 'child_process';
36
+
37
+ const __dir = dirname(fileURLToPath(import.meta.url));
38
+ const CWD = process.cwd();
39
+
40
+ // ── Parse CLI args ───────────────────────────────────────────────────────────
41
+ function arg(name) {
42
+ const i = process.argv.indexOf('--' + name);
43
+ return i !== -1 ? process.argv[i + 1] : null;
44
+ }
45
+ const hasFlag = (f) => process.argv.includes('--' + f);
46
+
47
+ const projectDir = resolve(arg('dir') || CWD);
48
+ const changedFile = arg('file') ? resolve(arg('file')) : null;
49
+ const dbPath = resolve(arg('db') || join(projectDir, '.monomind', 'monograph.db'));
50
+ const fullScan = hasFlag('full');
51
+
52
+ // ── Locate Understand-Anything plugin ───────────────────────────────────────
53
+ function findUAPlugin() {
54
+ const envPath = process.env.UA_PLUGIN_DIR;
55
+ if (envPath && existsSync(envPath)) return resolve(envPath);
56
+
57
+ // Common sibling locations relative to the monomind root
58
+ const candidates = [
59
+ join(__dir, '..', '..', 'knowledgegraph', 'Understand-Anything', 'understand-anything-plugin'),
60
+ join(dirname(__dir), '..', 'knowledgegraph', 'Understand-Anything', 'understand-anything-plugin'),
61
+ ];
62
+ for (const c of candidates) {
63
+ if (existsSync(c)) return c;
64
+ }
65
+ return null;
66
+ }
67
+
68
+ // ── Locate extract-structure.mjs ─────────────────────────────────────────────
69
+ function findExtractScript(pluginDir) {
70
+ const candidates = [
71
+ join(pluginDir, 'skills', 'understand', 'extract-structure.mjs'),
72
+ join(pluginDir, 'extract-structure.mjs'),
73
+ ];
74
+ for (const c of candidates) {
75
+ if (existsSync(c)) return c;
76
+ }
77
+ return null;
78
+ }
79
+
80
+ // ── Resolve monograph package ────────────────────────────────────────────────
81
+ function requireMonograph() {
82
+ const require = createRequire(import.meta.url);
83
+ const candidates = [
84
+ join(CWD, 'node_modules/.pnpm/node_modules/@monoes/monograph'),
85
+ join(CWD, 'packages/node_modules/.pnpm/node_modules/@monoes/monograph'),
86
+ join(CWD, 'node_modules/@monoes/monograph'),
87
+ ];
88
+ for (const c of candidates) {
89
+ try { if (existsSync(c)) return require(c); } catch {}
90
+ }
91
+ try { return require('@monoes/monograph'); } catch {}
92
+ throw new Error('Cannot find @monoes/monograph');
93
+ }
94
+
95
+ // ── Check for existing UA graph.json ────────────────────────────────────────
96
+ function findUAGraph(dir) {
97
+ if (process.env.UA_GRAPH_JSON) return process.env.UA_GRAPH_JSON;
98
+ const candidates = [
99
+ join(dir, '.understand', 'knowledge-graph.json'),
100
+ join(dir, '.understand', 'graph.json'),
101
+ join(dir, '.ua', 'knowledge-graph.json'),
102
+ join(dir, '.ua', 'graph.json'),
103
+ join(dir, 'graph.json'),
104
+ ];
105
+ for (const c of candidates) {
106
+ if (existsSync(c)) return c;
107
+ }
108
+ return null;
109
+ }
110
+
111
+ // ── Main ─────────────────────────────────────────────────────────────────────
112
+ async function main() {
113
+ console.log('[UA-ENRICH] Starting enrichment for', projectDir);
114
+
115
+ if (!existsSync(dbPath)) {
116
+ console.log('[UA-ENRICH] monograph.db not found — skipping (build monograph first)');
117
+ process.exit(0);
118
+ }
119
+
120
+ const mg = requireMonograph();
121
+ const db = mg.openDb(dbPath);
122
+
123
+ // ── Phase 1: Try importing existing UA graph.json ──────────────────────────
124
+ const existingGraph = findUAGraph(projectDir);
125
+ if (existingGraph && !fullScan) {
126
+ const stat = statSync(existingGraph);
127
+ const ageHours = (Date.now() - stat.mtimeMs) / 3_600_000;
128
+ if (ageHours < 24) {
129
+ console.log(`[UA-ENRICH] Found recent graph.json (${ageHours.toFixed(1)}h old) — importing`);
130
+ mg.closeDb(db);
131
+ // Delegate to ua-import.mjs
132
+ const importScript = join(__dir, 'ua-import.mjs');
133
+ if (existsSync(importScript)) {
134
+ const result = spawnSync(process.execPath, [importScript, existingGraph, dbPath], {
135
+ stdio: 'inherit', cwd: CWD,
136
+ });
137
+ process.exit(result.status ?? 0);
138
+ }
139
+ console.log('[UA-ENRICH] ua-import.mjs not found — continuing with direct enrichment');
140
+ } else {
141
+ console.log(`[UA-ENRICH] graph.json is ${ageHours.toFixed(0)}h old — will re-enrich from DB only`);
142
+ }
143
+ }
144
+
145
+ // ── Phase 2: Deterministic structural extraction (no LLM) ─────────────────
146
+ // Run UA's extract-structure.mjs on the target file/directory and capture output.
147
+ const pluginDir = findUAPlugin();
148
+ const extractScript = pluginDir ? findExtractScript(pluginDir) : null;
149
+
150
+ if (extractScript && changedFile) {
151
+ console.log('[UA-ENRICH] Running deterministic extraction on', basename(changedFile));
152
+ try {
153
+ const result = spawnSync(process.execPath, [extractScript, changedFile], {
154
+ cwd: projectDir,
155
+ timeout: 10_000,
156
+ encoding: 'utf-8',
157
+ });
158
+ if (result.stdout) {
159
+ let extracted;
160
+ try { extracted = JSON.parse(result.stdout); } catch { extracted = null; }
161
+ if (extracted && extracted.functions) {
162
+ // Write extracted structural data back into the node's properties
163
+ const normPath = changedFile.startsWith(projectDir)
164
+ ? changedFile.slice(projectDir.length + 1)
165
+ : changedFile;
166
+ const row = db.prepare("SELECT id, properties FROM nodes WHERE file_path LIKE ? LIMIT 1")
167
+ .get('%' + basename(changedFile));
168
+ if (row) {
169
+ const existing = row.properties ? JSON.parse(row.properties) : {};
170
+ const merged = {
171
+ ...existing,
172
+ ua_extracted: {
173
+ functions: extracted.functions?.length ?? 0,
174
+ classes: extracted.classes?.length ?? 0,
175
+ imports: extracted.imports?.length ?? 0,
176
+ exports: extracted.exports?.length ?? 0,
177
+ updatedAt: new Date().toISOString(),
178
+ },
179
+ };
180
+ db.prepare("UPDATE nodes SET properties = ? WHERE id = ?")
181
+ .run(JSON.stringify(merged), row.id);
182
+ console.log(`[UA-ENRICH] Updated structural data for ${row.id}`);
183
+ }
184
+ }
185
+ }
186
+ } catch (e) {
187
+ console.log('[UA-ENRICH] Extraction warning:', e.message);
188
+ }
189
+ } else if (!extractScript) {
190
+ console.log('[UA-ENRICH] UA extract script not found — skipping deterministic extraction');
191
+ console.log('[UA-ENRICH] Set UA_PLUGIN_DIR env var or place Understand-Anything beside monomind');
192
+ }
193
+
194
+ // ── Phase 3: Propagate existing UA summaries to FTS ───────────────────────
195
+ // If nodes have ua_type/summary in properties but FTS is stale, rebuild it.
196
+ try {
197
+ const enrichedCount = db.prepare(
198
+ `SELECT COUNT(*) AS c FROM nodes WHERE properties LIKE '%ua_type%'`
199
+ ).get().c;
200
+ if (enrichedCount > 0) {
201
+ console.log(`[UA-ENRICH] ${enrichedCount} UA-enriched nodes in DB`);
202
+ db.prepare(`INSERT INTO nodes_fts(nodes_fts) VALUES('rebuild')`).run();
203
+ console.log('[UA-ENRICH] FTS rebuilt');
204
+ } else {
205
+ console.log('[UA-ENRICH] No UA enrichment data yet — run ua-import.mjs after /understand');
206
+ }
207
+ } catch { /* FTS may not exist */ }
208
+
209
+ // ── Phase 4: Write enrichment status to .monomind/ ────────────────────────
210
+ try {
211
+ const statusPath = join(projectDir, '.monomind', 'ua-enrich-status.json');
212
+ writeFileSync(statusPath, JSON.stringify({
213
+ lastRun: new Date().toISOString(),
214
+ mode: changedFile ? 'incremental' : 'full',
215
+ file: changedFile || null,
216
+ pluginFound: !!pluginDir,
217
+ graphFound: !!existingGraph,
218
+ }, null, 2));
219
+ } catch {}
220
+
221
+ mg.closeDb(db);
222
+ console.log('[UA-ENRICH] Done');
223
+ }
224
+
225
+ main().catch((e) => {
226
+ console.error('[UA-ENRICH] Error:', e.message);
227
+ process.exit(1);
228
+ });