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 +3 -1
- package/packages/@monomind/cli/package.json +1 -1
- package/scripts/install.sh +392 -0
- package/scripts/ua-enrich.mjs +228 -0
- package/scripts/ua-import.mjs +288 -0
- package/scripts/verify-appliance.sh +592 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monomind",
|
|
3
|
-
"version": "1.10.
|
|
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.
|
|
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
|
+
});
|