rex-claude 4.0.0 → 6.0.0
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/dist/agents-JIZXXASP.js +853 -0
- package/dist/app-3VWDSH5F.js +248 -0
- package/dist/audio-US2J627E.js +196 -0
- package/dist/audit-ZVTGE4L4.js +8 -0
- package/dist/call-AQZ3Z5SE.js +143 -0
- package/dist/chunk-5ND7JYY3.js +62 -0
- package/dist/chunk-6SRV2I2H.js +56 -0
- package/dist/{setup-AO3MW46W.js → chunk-A7ZLQUOX.js} +93 -16
- package/dist/chunk-E5UYN3W7.js +105 -0
- package/dist/chunk-HAHJD3QH.js +147 -0
- package/dist/{init-DLFEGD6O.js → chunk-KR7ISYZH.js} +328 -29
- package/dist/chunk-LTOM55UV.js +154 -0
- package/dist/chunk-PDX44BCA.js +11 -0
- package/dist/chunk-PPGYFMU5.js +67 -0
- package/dist/{chunk-7AGI43F5.js → chunk-WBMVBMWB.js} +4 -2
- package/dist/{context-FN5O5YBI.js → context-XNCG2M5Q.js} +2 -1
- package/dist/daemon-5KNSNFTD.js +208 -0
- package/dist/gateway-YLP66MCQ.js +2273 -0
- package/dist/hammerspoon/rex-call-watcher.lua +186 -0
- package/dist/index.js +309 -15
- package/dist/init-RDZFIBLA.js +30 -0
- package/dist/install-63JBDPRU.js +41 -0
- package/dist/{llm-YRORUH7E.js → llm-RALIPIMI.js} +2 -1
- package/dist/mcp_registry-DX4GGSP6.js +514 -0
- package/dist/migrate-GDO37TI5.js +87 -0
- package/dist/{optimize-UKMAGQQE.js → optimize-5TE5RKZV.js} +2 -1
- package/dist/paths-4SECM6E6.js +38 -0
- package/dist/preload-I3MYBVNU.js +78 -0
- package/dist/projects-V6TSLO7E.js +17 -0
- package/dist/{prune-2PPIVDXK.js → prune-B7F5B5OF.js} +2 -1
- package/dist/recategorize-YXYIMQLZ.js +155 -0
- package/dist/router-2JD34COX.js +12 -0
- package/dist/self-improve-YK7RCYF4.js +197 -0
- package/dist/setup-KNDTVFO6.js +8 -0
- package/dist/skills-AIWFY5NH.js +374 -0
- package/dist/voice-RITC3EVC.js +248 -0
- package/package.json +12 -3
- package/dist/gateway-EKMU5D7J.js +0 -784
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/init.ts
|
|
4
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync, readdirSync, statSync } from "fs";
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, copyFileSync, chmodSync, unlinkSync, readdirSync, statSync } from "fs";
|
|
5
5
|
import { join, dirname } from "path";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
import { execSync } from "child_process";
|
|
@@ -43,6 +43,8 @@ function ensureDir(dir) {
|
|
|
43
43
|
var PLIST_LABEL = "com.dstudio.rex";
|
|
44
44
|
var INGEST_PLIST_LABEL = "com.dstudio.rex-ingest";
|
|
45
45
|
var GATEWAY_PLIST_LABEL = "com.dstudio.rex-gateway";
|
|
46
|
+
var CALL_WATCH_PLIST_LABEL = "com.dstudio.rex-call-watch";
|
|
47
|
+
var DAEMON_PLIST_LABEL = "com.dstudio.rex-daemon";
|
|
46
48
|
function installIngestAgent() {
|
|
47
49
|
if (process.platform !== "darwin") {
|
|
48
50
|
info("Auto-ingest only supported on macOS");
|
|
@@ -193,6 +195,74 @@ function installGatewayAgent() {
|
|
|
193
195
|
}
|
|
194
196
|
ok("Gateway LaunchAgent installed \u2014 Telegram bot always-on (auto-restart)");
|
|
195
197
|
}
|
|
198
|
+
function installDaemonAgent() {
|
|
199
|
+
if (process.platform !== "darwin") {
|
|
200
|
+
info("Daemon LaunchAgent only supported on macOS");
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const launchAgentsDir = join(homedir(), "Library", "LaunchAgents");
|
|
204
|
+
ensureDir(launchAgentsDir);
|
|
205
|
+
const plistPath = join(launchAgentsDir, `${DAEMON_PLIST_LABEL}.plist`);
|
|
206
|
+
let rexBin = "";
|
|
207
|
+
try {
|
|
208
|
+
rexBin = execSync("which rex", { encoding: "utf-8" }).trim();
|
|
209
|
+
} catch {
|
|
210
|
+
}
|
|
211
|
+
if (!rexBin) {
|
|
212
|
+
info("rex binary not in PATH \u2014 skipping daemon LaunchAgent");
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (existsSync(plistPath)) {
|
|
216
|
+
skip("Daemon LaunchAgent already installed (unified background daemon)");
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const settingsPath = join(homedir(), ".claude", "settings.json");
|
|
220
|
+
let botToken = "";
|
|
221
|
+
let chatIdVal = "";
|
|
222
|
+
try {
|
|
223
|
+
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
224
|
+
botToken = settings.env?.REX_TELEGRAM_BOT_TOKEN || "";
|
|
225
|
+
chatIdVal = settings.env?.REX_TELEGRAM_CHAT_ID || "";
|
|
226
|
+
} catch {
|
|
227
|
+
}
|
|
228
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
229
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
230
|
+
<plist version="1.0">
|
|
231
|
+
<dict>
|
|
232
|
+
<key>Label</key>
|
|
233
|
+
<string>${DAEMON_PLIST_LABEL}</string>
|
|
234
|
+
<key>ProgramArguments</key>
|
|
235
|
+
<array>
|
|
236
|
+
<string>${rexBin}</string>
|
|
237
|
+
<string>daemon</string>
|
|
238
|
+
</array>
|
|
239
|
+
<key>RunAtLoad</key>
|
|
240
|
+
<true/>
|
|
241
|
+
<key>KeepAlive</key>
|
|
242
|
+
<true/>
|
|
243
|
+
<key>StandardOutPath</key>
|
|
244
|
+
<string>${join(homedir(), ".claude", "rex", "daemon.log")}</string>
|
|
245
|
+
<key>StandardErrorPath</key>
|
|
246
|
+
<string>${join(homedir(), ".claude", "rex", "daemon.log")}</string>
|
|
247
|
+
<key>EnvironmentVariables</key>
|
|
248
|
+
<dict>
|
|
249
|
+
<key>PATH</key>
|
|
250
|
+
<string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${dirname(rexBin)}</string>
|
|
251
|
+
<key>REX_TELEGRAM_BOT_TOKEN</key>
|
|
252
|
+
<string>${botToken}</string>
|
|
253
|
+
<key>REX_TELEGRAM_CHAT_ID</key>
|
|
254
|
+
<string>${chatIdVal}</string>
|
|
255
|
+
</dict>
|
|
256
|
+
</dict>
|
|
257
|
+
</plist>
|
|
258
|
+
`;
|
|
259
|
+
writeFileSync(plistPath, plist);
|
|
260
|
+
try {
|
|
261
|
+
execSync(`launchctl load ${plistPath}`, { stdio: "ignore" });
|
|
262
|
+
} catch {
|
|
263
|
+
}
|
|
264
|
+
ok("Daemon LaunchAgent installed \u2014 unified background daemon (KeepAlive)");
|
|
265
|
+
}
|
|
196
266
|
function uninstallGatewayAgent() {
|
|
197
267
|
if (process.platform !== "darwin") return;
|
|
198
268
|
const plistPath = join(homedir(), "Library", "LaunchAgents", `${GATEWAY_PLIST_LABEL}.plist`);
|
|
@@ -210,30 +280,173 @@ function uninstallGatewayAgent() {
|
|
|
210
280
|
}
|
|
211
281
|
ok("Gateway LaunchAgent removed");
|
|
212
282
|
}
|
|
283
|
+
function installHammerspoonCallWatcher() {
|
|
284
|
+
if (process.platform !== "darwin") {
|
|
285
|
+
info("Hammerspoon call watcher only supported on macOS");
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
let hsBin = "";
|
|
289
|
+
try {
|
|
290
|
+
hsBin = execSync("which hs", { encoding: "utf-8" }).trim();
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
if (!hsBin) {
|
|
294
|
+
info("Hammerspoon CLI (hs) not found \u2014 skipping call watcher install");
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const hsDir = join(homedir(), ".hammerspoon");
|
|
298
|
+
ensureDir(hsDir);
|
|
299
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
300
|
+
const sourceWatcher = join(thisDir, "hammerspoon", "rex-call-watcher.lua");
|
|
301
|
+
const targetWatcher = join(hsDir, "rex-call-watcher.lua");
|
|
302
|
+
if (!existsSync(sourceWatcher)) {
|
|
303
|
+
info("Bundled call watcher not found in rex-cli package");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
copyFileSync(sourceWatcher, targetWatcher);
|
|
308
|
+
chmodSync(targetWatcher, 420);
|
|
309
|
+
ok("Hammerspoon call watcher installed");
|
|
310
|
+
} catch {
|
|
311
|
+
info("Could not install Hammerspoon call watcher");
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const initLuaPath = join(hsDir, "init.lua");
|
|
315
|
+
const loaderBlock = `
|
|
316
|
+
-- REX Call Watcher (installed by rex init)
|
|
317
|
+
do
|
|
318
|
+
local ok_rex_call, rex_call = pcall(dofile, os.getenv("HOME") .. "/.hammerspoon/rex-call-watcher.lua")
|
|
319
|
+
if ok_rex_call and rex_call and rex_call.start then
|
|
320
|
+
rex_call.start()
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
`;
|
|
324
|
+
try {
|
|
325
|
+
const current = existsSync(initLuaPath) ? readFileSync(initLuaPath, "utf-8") : "";
|
|
326
|
+
if (current.includes("rex-call-watcher.lua")) {
|
|
327
|
+
skip("Hammerspoon init.lua already loads REX call watcher");
|
|
328
|
+
} else {
|
|
329
|
+
const next = current.endsWith("\n") || current.length === 0 ? current + loaderBlock : `${current}
|
|
330
|
+
${loaderBlock}`;
|
|
331
|
+
writeFileSync(initLuaPath, next);
|
|
332
|
+
ok("Hammerspoon init.lua patched to start call watcher");
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
info("Could not patch ~/.hammerspoon/init.lua automatically");
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
execSync(`${hsBin} -c "hs.reload()"`, { stdio: "ignore" });
|
|
339
|
+
ok("Hammerspoon config reloaded");
|
|
340
|
+
} catch {
|
|
341
|
+
skip("Could not auto-reload Hammerspoon config (reload manually in Hammerspoon)");
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function installCallWatchAgent() {
|
|
345
|
+
if (process.platform !== "darwin") {
|
|
346
|
+
info("Call watch LaunchAgent only supported on macOS");
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const watcherPath = join(homedir(), ".hammerspoon", "rex-call-watcher.lua");
|
|
350
|
+
if (!existsSync(watcherPath)) {
|
|
351
|
+
info("Hammerspoon call watcher not installed \u2014 skipping call watch LaunchAgent");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const launchAgentsDir = join(homedir(), "Library", "LaunchAgents");
|
|
355
|
+
ensureDir(launchAgentsDir);
|
|
356
|
+
const plistPath = join(launchAgentsDir, `${CALL_WATCH_PLIST_LABEL}.plist`);
|
|
357
|
+
let rexBin = "";
|
|
358
|
+
try {
|
|
359
|
+
rexBin = execSync("which rex", { encoding: "utf-8" }).trim();
|
|
360
|
+
} catch {
|
|
361
|
+
}
|
|
362
|
+
if (!rexBin) {
|
|
363
|
+
info("rex binary not in PATH \u2014 skipping call watch LaunchAgent");
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
if (existsSync(plistPath)) {
|
|
367
|
+
skip("Call watch LaunchAgent already installed (auto audio logger)");
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
371
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
372
|
+
<plist version="1.0">
|
|
373
|
+
<dict>
|
|
374
|
+
<key>Label</key>
|
|
375
|
+
<string>${CALL_WATCH_PLIST_LABEL}</string>
|
|
376
|
+
<key>ProgramArguments</key>
|
|
377
|
+
<array>
|
|
378
|
+
<string>${rexBin}</string>
|
|
379
|
+
<string>call</string>
|
|
380
|
+
<string>watch</string>
|
|
381
|
+
</array>
|
|
382
|
+
<key>RunAtLoad</key>
|
|
383
|
+
<true/>
|
|
384
|
+
<key>KeepAlive</key>
|
|
385
|
+
<true/>
|
|
386
|
+
<key>StandardOutPath</key>
|
|
387
|
+
<string>${join(homedir(), ".claude", "rex-call-watch.log")}</string>
|
|
388
|
+
<key>StandardErrorPath</key>
|
|
389
|
+
<string>${join(homedir(), ".claude", "rex-call-watch.log")}</string>
|
|
390
|
+
<key>EnvironmentVariables</key>
|
|
391
|
+
<dict>
|
|
392
|
+
<key>PATH</key>
|
|
393
|
+
<string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin:${dirname(rexBin)}</string>
|
|
394
|
+
</dict>
|
|
395
|
+
</dict>
|
|
396
|
+
</plist>
|
|
397
|
+
`;
|
|
398
|
+
writeFileSync(plistPath, plist);
|
|
399
|
+
try {
|
|
400
|
+
execSync(`launchctl load ${plistPath}`, { stdio: "ignore" });
|
|
401
|
+
} catch {
|
|
402
|
+
}
|
|
403
|
+
ok("Call watch LaunchAgent installed \u2014 auto audio logger on call start/end");
|
|
404
|
+
}
|
|
405
|
+
function uninstallCallWatchAgent() {
|
|
406
|
+
if (process.platform !== "darwin") return;
|
|
407
|
+
const plistPath = join(homedir(), "Library", "LaunchAgents", `${CALL_WATCH_PLIST_LABEL}.plist`);
|
|
408
|
+
if (!existsSync(plistPath)) return;
|
|
409
|
+
try {
|
|
410
|
+
execSync(`launchctl unload ${plistPath}`, { stdio: "ignore" });
|
|
411
|
+
} catch {
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
unlinkSync(plistPath);
|
|
415
|
+
} catch {
|
|
416
|
+
}
|
|
417
|
+
ok("Call watch LaunchAgent removed");
|
|
418
|
+
}
|
|
213
419
|
function installApp() {
|
|
214
420
|
if (process.platform !== "darwin") return;
|
|
215
421
|
const thisDir = new URL(".", import.meta.url).pathname;
|
|
216
|
-
const
|
|
217
|
-
const
|
|
422
|
+
const releaseBuild = join(thisDir, "..", "..", "flutter_app", "build", "macos", "Build", "Products", "Release", "rex_app.app");
|
|
423
|
+
const debugBuild = join(thisDir, "..", "..", "flutter_app", "build", "macos", "Build", "Products", "Debug", "rex_app.app");
|
|
424
|
+
const buildApp = existsSync(releaseBuild) ? releaseBuild : debugBuild;
|
|
425
|
+
const installedApp = "/Applications/rex_app.app";
|
|
426
|
+
const legacyApp = "/Applications/REX.app";
|
|
218
427
|
if (existsSync(installedApp)) {
|
|
219
|
-
skip("
|
|
428
|
+
skip("rex_app.app already in /Applications");
|
|
220
429
|
} else if (existsSync(buildApp)) {
|
|
221
430
|
try {
|
|
222
|
-
execSync(`
|
|
223
|
-
ok("
|
|
431
|
+
execSync(`ditto "${buildApp}" "${installedApp}"`, { stdio: "ignore" });
|
|
432
|
+
ok("rex_app.app installed to /Applications");
|
|
433
|
+
try {
|
|
434
|
+
execSync(`ln -sfn "${installedApp}" "${legacyApp}"`, { stdio: "ignore" });
|
|
435
|
+
} catch {
|
|
436
|
+
}
|
|
224
437
|
} catch {
|
|
225
|
-
info("Could not copy
|
|
438
|
+
info("Could not copy rex_app.app to /Applications (try manually)");
|
|
226
439
|
return;
|
|
227
440
|
}
|
|
228
441
|
} else {
|
|
229
|
-
info("
|
|
442
|
+
info("App not built \u2014 run `rex app update` (or `flutter build macos --debug`) in packages/flutter_app first");
|
|
230
443
|
return;
|
|
231
444
|
}
|
|
232
445
|
try {
|
|
233
446
|
execSync(`osascript -e 'tell application "System Events" to make login item at end with properties {path:"${installedApp}", hidden:false}'`, { stdio: "ignore" });
|
|
234
|
-
ok("
|
|
447
|
+
ok("rex_app.app added to Login Items (auto-start on login)");
|
|
235
448
|
} catch {
|
|
236
|
-
skip("
|
|
449
|
+
skip("rex_app.app already in Login Items");
|
|
237
450
|
}
|
|
238
451
|
}
|
|
239
452
|
function installStartup() {
|
|
@@ -310,6 +523,7 @@ function uninstallStartup() {
|
|
|
310
523
|
}
|
|
311
524
|
uninstallIngestAgent();
|
|
312
525
|
uninstallGatewayAgent();
|
|
526
|
+
uninstallCallWatchAgent();
|
|
313
527
|
}
|
|
314
528
|
async function init() {
|
|
315
529
|
const claudeDir = join(homedir(), ".claude");
|
|
@@ -351,6 +565,30 @@ ${line}`);
|
|
|
351
565
|
} else {
|
|
352
566
|
info("Memory package not found \u2014 install @rex/memory or run from monorepo");
|
|
353
567
|
}
|
|
568
|
+
const registryPath = join(homedir(), ".rex-memory", "mcp-registry.json");
|
|
569
|
+
if (existsSync(registryPath)) {
|
|
570
|
+
try {
|
|
571
|
+
const registry = readJson(registryPath);
|
|
572
|
+
const registryServers = registry?.servers;
|
|
573
|
+
if (registryServers && typeof registryServers === "object") {
|
|
574
|
+
let added = 0;
|
|
575
|
+
for (const [name, cfg] of Object.entries(registryServers)) {
|
|
576
|
+
if (!settings.mcpServers[name]) {
|
|
577
|
+
settings.mcpServers[name] = cfg;
|
|
578
|
+
added++;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
if (added > 0) {
|
|
582
|
+
writeJson(settingsPath, settings);
|
|
583
|
+
ok(`MCP registry synced (${added} server${added > 1 ? "s" : ""} added)`);
|
|
584
|
+
} else {
|
|
585
|
+
skip("MCP registry already synced");
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
} catch {
|
|
589
|
+
info("MCP registry exists but could not be parsed");
|
|
590
|
+
}
|
|
591
|
+
}
|
|
354
592
|
if (!settings.hooks) settings.hooks = {};
|
|
355
593
|
const hasIngestHook = settings.hooks.SessionEnd?.some?.(
|
|
356
594
|
(h) => h.hooks?.some?.((hh) => hh.command?.includes("rex") && hh.command?.includes("ingest"))
|
|
@@ -371,12 +609,8 @@ ${line}`);
|
|
|
371
609
|
const hasContextHook = settings.hooks.SessionStart?.some?.(
|
|
372
610
|
(h) => h.hooks?.some?.((hh) => hh.command?.includes("rex-context"))
|
|
373
611
|
);
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
} else {
|
|
377
|
-
const contextScript = join(claudeDir, "rex-context.sh");
|
|
378
|
-
if (!existsSync(contextScript)) {
|
|
379
|
-
writeFileSync(contextScript, `#!/bin/bash
|
|
612
|
+
const contextScript = join(claudeDir, "rex-context.sh");
|
|
613
|
+
writeFileSync(contextScript, `#!/bin/bash
|
|
380
614
|
# REX Context Injection \u2014 runs at session start
|
|
381
615
|
# Outputs relevant memory context to CLAUDE_ENV_FILE
|
|
382
616
|
|
|
@@ -384,20 +618,31 @@ if [ -z "$CLAUDE_ENV_FILE" ]; then
|
|
|
384
618
|
exit 0
|
|
385
619
|
fi
|
|
386
620
|
|
|
621
|
+
PROJECT_PATH="\${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
622
|
+
|
|
387
623
|
# Quick check if rex-memory MCP is available
|
|
388
624
|
if command -v npx &>/dev/null; then
|
|
389
625
|
# Context will be loaded via MCP rex_context tool
|
|
390
626
|
# This hook just ensures the env is ready
|
|
391
627
|
echo "REX_MEMORY_AVAILABLE=true" >> "$CLAUDE_ENV_FILE"
|
|
392
628
|
fi
|
|
629
|
+
|
|
630
|
+
# Auto tool/skill recommendations (LLM-assisted)
|
|
631
|
+
if command -v rex &>/dev/null; then
|
|
632
|
+
rex agents recommend "$PROJECT_PATH" --quiet --env-file "$CLAUDE_ENV_FILE" >/dev/null 2>&1 || true
|
|
633
|
+
elif command -v npx &>/dev/null; then
|
|
634
|
+
npx rex-cli agents recommend "$PROJECT_PATH" --quiet --env-file "$CLAUDE_ENV_FILE" >/dev/null 2>&1 || true
|
|
635
|
+
fi
|
|
393
636
|
`, { mode: 493 });
|
|
394
|
-
|
|
637
|
+
if (hasContextHook) {
|
|
638
|
+
skip("Context injection hook (SessionStart) already configured");
|
|
639
|
+
} else {
|
|
395
640
|
if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
|
|
396
641
|
settings.hooks.SessionStart.push({
|
|
397
642
|
hooks: [{
|
|
398
643
|
type: "command",
|
|
399
644
|
command: `bash ${contextScript}`,
|
|
400
|
-
timeout:
|
|
645
|
+
timeout: 20
|
|
401
646
|
}]
|
|
402
647
|
});
|
|
403
648
|
ok("Context injection hook configured (SessionStart)");
|
|
@@ -491,6 +736,9 @@ fi
|
|
|
491
736
|
installStartup();
|
|
492
737
|
installIngestAgent();
|
|
493
738
|
installGatewayAgent();
|
|
739
|
+
installDaemonAgent();
|
|
740
|
+
installHammerspoonCallWatcher();
|
|
741
|
+
installCallWatchAgent();
|
|
494
742
|
installApp();
|
|
495
743
|
let ollamaOk = false;
|
|
496
744
|
try {
|
|
@@ -551,12 +799,58 @@ fi
|
|
|
551
799
|
}
|
|
552
800
|
const memoryMonorepoDir = join(thisDir, "..", "..", "memory");
|
|
553
801
|
const memoryTargetDir = join(homedir(), ".rex-memory");
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
802
|
+
ensureDir(memoryTargetDir);
|
|
803
|
+
const runtimePkgPath = join(memoryTargetDir, "package.json");
|
|
804
|
+
const runtimePkg = {
|
|
805
|
+
name: "rex-memory-runtime",
|
|
806
|
+
private: true,
|
|
807
|
+
type: "module",
|
|
808
|
+
dependencies: {
|
|
809
|
+
"better-sqlite3": "^11.8.1",
|
|
810
|
+
"sqlite-vec": "^0.1.6"
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
try {
|
|
814
|
+
let shouldInstall = false;
|
|
815
|
+
if (!existsSync(runtimePkgPath)) {
|
|
816
|
+
writeJson(runtimePkgPath, runtimePkg);
|
|
817
|
+
shouldInstall = true;
|
|
818
|
+
} else {
|
|
819
|
+
const existing = readJson(runtimePkgPath) || {};
|
|
820
|
+
const deps = existing.dependencies || {};
|
|
821
|
+
if (!deps["better-sqlite3"] || !deps["sqlite-vec"]) {
|
|
822
|
+
writeJson(runtimePkgPath, {
|
|
823
|
+
...existing,
|
|
824
|
+
private: true,
|
|
825
|
+
type: existing.type || "module",
|
|
826
|
+
dependencies: {
|
|
827
|
+
...deps,
|
|
828
|
+
"better-sqlite3": deps["better-sqlite3"] || "^11.8.1",
|
|
829
|
+
"sqlite-vec": deps["sqlite-vec"] || "^0.1.6"
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
shouldInstall = true;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
if (!existsSync(join(memoryTargetDir, "node_modules", "better-sqlite3"))) {
|
|
836
|
+
shouldInstall = true;
|
|
837
|
+
}
|
|
838
|
+
if (shouldInstall) {
|
|
557
839
|
execSync("npm install --production 2>/dev/null", { cwd: memoryTargetDir, stdio: "ignore" });
|
|
558
|
-
ok("
|
|
559
|
-
}
|
|
840
|
+
ok("Memory runtime dependencies installed in ~/.rex-memory/");
|
|
841
|
+
} else {
|
|
842
|
+
skip("Memory runtime dependencies already present");
|
|
843
|
+
}
|
|
844
|
+
} catch {
|
|
845
|
+
if (existsSync(join(memoryMonorepoDir, "package.json")) && !existsSync(join(memoryTargetDir, "src"))) {
|
|
846
|
+
try {
|
|
847
|
+
execSync(`cp -R "${memoryMonorepoDir}/." "${memoryTargetDir}/"`, { stdio: "ignore" });
|
|
848
|
+
execSync("npm install --production 2>/dev/null", { cwd: memoryTargetDir, stdio: "ignore" });
|
|
849
|
+
ok("@rex/memory synced to ~/.rex-memory/");
|
|
850
|
+
} catch {
|
|
851
|
+
info("Could not sync @rex/memory \u2014 install manually");
|
|
852
|
+
}
|
|
853
|
+
} else {
|
|
560
854
|
info("Could not sync @rex/memory \u2014 install manually");
|
|
561
855
|
}
|
|
562
856
|
}
|
|
@@ -577,13 +871,18 @@ ${COLORS.bold} REX initialized!${COLORS.reset}`);
|
|
|
577
871
|
console.log(` \u2022 Run ${COLORS.cyan}rex doctor${COLORS.reset} to verify setup`);
|
|
578
872
|
console.log();
|
|
579
873
|
}
|
|
874
|
+
|
|
580
875
|
export {
|
|
581
|
-
init,
|
|
582
|
-
installApp,
|
|
583
|
-
installGatewayAgent,
|
|
584
876
|
installIngestAgent,
|
|
585
|
-
installStartup,
|
|
586
|
-
uninstallGatewayAgent,
|
|
587
877
|
uninstallIngestAgent,
|
|
588
|
-
|
|
878
|
+
installGatewayAgent,
|
|
879
|
+
installDaemonAgent,
|
|
880
|
+
uninstallGatewayAgent,
|
|
881
|
+
installHammerspoonCallWatcher,
|
|
882
|
+
installCallWatchAgent,
|
|
883
|
+
uninstallCallWatchAgent,
|
|
884
|
+
installApp,
|
|
885
|
+
installStartup,
|
|
886
|
+
uninstallStartup,
|
|
887
|
+
init
|
|
589
888
|
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
loadConfig
|
|
4
|
+
} from "./chunk-PPGYFMU5.js";
|
|
5
|
+
import {
|
|
6
|
+
createLogger
|
|
7
|
+
} from "./chunk-5ND7JYY3.js";
|
|
8
|
+
import {
|
|
9
|
+
PROJECTS_DIR,
|
|
10
|
+
ensureRexDirs
|
|
11
|
+
} from "./chunk-6SRV2I2H.js";
|
|
12
|
+
|
|
13
|
+
// src/projects.ts
|
|
14
|
+
import { readdirSync, readFileSync, existsSync, statSync, writeFileSync } from "fs";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
import { execSync } from "child_process";
|
|
17
|
+
var log = createLogger("projects");
|
|
18
|
+
function detectStack(projectPath) {
|
|
19
|
+
const stack = [];
|
|
20
|
+
const pkgPath = join(projectPath, "package.json");
|
|
21
|
+
if (existsSync(pkgPath)) {
|
|
22
|
+
try {
|
|
23
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
24
|
+
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
25
|
+
if (allDeps["next"]) stack.push("next.js");
|
|
26
|
+
else if (allDeps["react"]) stack.push("react");
|
|
27
|
+
if (allDeps["vue"]) stack.push("vue");
|
|
28
|
+
if (allDeps["@angular/core"]) stack.push("angular");
|
|
29
|
+
if (allDeps["@ionic/angular"] || allDeps["@ionic/react"]) stack.push("ionic");
|
|
30
|
+
if (allDeps["typescript"]) stack.push("typescript");
|
|
31
|
+
if (allDeps["tailwindcss"]) stack.push("tailwind");
|
|
32
|
+
if (allDeps["drizzle-orm"]) stack.push("drizzle");
|
|
33
|
+
if (allDeps["hono"]) stack.push("hono");
|
|
34
|
+
if (allDeps["wrangler"] || allDeps["@cloudflare/workers-types"]) stack.push("cloudflare-workers");
|
|
35
|
+
if (allDeps["better-sqlite3"] || allDeps["sqlite3"]) stack.push("sqlite");
|
|
36
|
+
if (allDeps["express"]) stack.push("express");
|
|
37
|
+
if (stack.length === 0) stack.push("node");
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (existsSync(join(projectPath, "pubspec.yaml"))) {
|
|
42
|
+
stack.push("flutter");
|
|
43
|
+
try {
|
|
44
|
+
const pubspec = readFileSync(join(projectPath, "pubspec.yaml"), "utf-8");
|
|
45
|
+
if (pubspec.includes("macos_ui")) stack.push("macos");
|
|
46
|
+
} catch {
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (existsSync(join(projectPath, "composer.json"))) {
|
|
50
|
+
stack.push("php");
|
|
51
|
+
try {
|
|
52
|
+
const composer = JSON.parse(readFileSync(join(projectPath, "composer.json"), "utf-8"));
|
|
53
|
+
if (composer.require?.["cakephp/cakephp"]) stack.push("cakephp");
|
|
54
|
+
if (composer.require?.["laravel/framework"]) stack.push("laravel");
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (existsSync(join(projectPath, "Cargo.toml"))) stack.push("rust");
|
|
59
|
+
if (existsSync(join(projectPath, "go.mod"))) stack.push("go");
|
|
60
|
+
return stack;
|
|
61
|
+
}
|
|
62
|
+
function getLastModified(projectPath) {
|
|
63
|
+
try {
|
|
64
|
+
const date = execSync("git log -1 --format=%ci 2>/dev/null", { cwd: projectPath, encoding: "utf-8" }).trim();
|
|
65
|
+
if (date) return date.split(" ")[0];
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return statSync(projectPath).mtime.toISOString().split("T")[0];
|
|
70
|
+
} catch {
|
|
71
|
+
return (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function getGitRemote(projectPath) {
|
|
75
|
+
try {
|
|
76
|
+
return execSync("git remote get-url origin 2>/dev/null", { cwd: projectPath, encoding: "utf-8" }).trim() || void 0;
|
|
77
|
+
} catch {
|
|
78
|
+
return void 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
var MANIFEST_FILES = ["package.json", "pubspec.yaml", "composer.json", "Cargo.toml", "go.mod"];
|
|
82
|
+
function scanProjects() {
|
|
83
|
+
const config = loadConfig();
|
|
84
|
+
const HOME = process.env.HOME || "~";
|
|
85
|
+
const projects = [];
|
|
86
|
+
for (const scanPath of config.ingest.scanPaths) {
|
|
87
|
+
const resolved = scanPath.replace("~", HOME);
|
|
88
|
+
if (!existsSync(resolved)) continue;
|
|
89
|
+
const entries = readdirSync(resolved, { withFileTypes: true });
|
|
90
|
+
for (const entry of entries) {
|
|
91
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
92
|
+
if (config.ingest.excludePaths.includes(entry.name)) continue;
|
|
93
|
+
const fullPath = join(resolved, entry.name);
|
|
94
|
+
const hasManifest = MANIFEST_FILES.some((f) => existsSync(join(fullPath, f)));
|
|
95
|
+
if (hasManifest) {
|
|
96
|
+
const status = entry.name.startsWith("_") ? entry.name === "_templates" ? "template" : "archived" : "active";
|
|
97
|
+
projects.push({
|
|
98
|
+
name: entry.name,
|
|
99
|
+
path: fullPath,
|
|
100
|
+
stack: detectStack(fullPath),
|
|
101
|
+
lastActive: getLastModified(fullPath),
|
|
102
|
+
status,
|
|
103
|
+
repo: getGitRemote(fullPath)
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
try {
|
|
107
|
+
const subEntries = readdirSync(fullPath, { withFileTypes: true });
|
|
108
|
+
for (const sub of subEntries) {
|
|
109
|
+
if (!sub.isDirectory() || sub.name.startsWith(".")) continue;
|
|
110
|
+
if (config.ingest.excludePaths.includes(sub.name)) continue;
|
|
111
|
+
const subPath = join(fullPath, sub.name);
|
|
112
|
+
if (MANIFEST_FILES.some((f) => existsSync(join(subPath, f)))) {
|
|
113
|
+
projects.push({
|
|
114
|
+
name: sub.name,
|
|
115
|
+
path: subPath,
|
|
116
|
+
stack: detectStack(subPath),
|
|
117
|
+
lastActive: getLastModified(subPath),
|
|
118
|
+
status: "active",
|
|
119
|
+
repo: getGitRemote(subPath)
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
log.info(`Scanned ${projects.length} projects across ${config.ingest.scanPaths.length} paths`);
|
|
129
|
+
return projects.sort((a, b) => b.lastActive.localeCompare(a.lastActive));
|
|
130
|
+
}
|
|
131
|
+
function saveProjectIndex(projects) {
|
|
132
|
+
ensureRexDirs();
|
|
133
|
+
writeFileSync(join(PROJECTS_DIR, "index.json"), JSON.stringify(projects, null, 2));
|
|
134
|
+
}
|
|
135
|
+
function loadProjectIndex() {
|
|
136
|
+
const indexPath = join(PROJECTS_DIR, "index.json");
|
|
137
|
+
if (!existsSync(indexPath)) return [];
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(readFileSync(indexPath, "utf-8"));
|
|
140
|
+
} catch {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function findProject(cwd) {
|
|
145
|
+
const projects = loadProjectIndex();
|
|
146
|
+
return projects.find((p) => cwd === p.path || cwd.startsWith(p.path + "/"));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export {
|
|
150
|
+
scanProjects,
|
|
151
|
+
saveProjectIndex,
|
|
152
|
+
loadProjectIndex,
|
|
153
|
+
findProject
|
|
154
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
__require
|
|
11
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CONFIG_PATH
|
|
4
|
+
} from "./chunk-6SRV2I2H.js";
|
|
5
|
+
|
|
6
|
+
// src/config.ts
|
|
7
|
+
import { readFileSync, writeFileSync, copyFileSync, existsSync } from "fs";
|
|
8
|
+
var DEFAULTS = {
|
|
9
|
+
llm: {
|
|
10
|
+
embedModel: "nomic-embed-text",
|
|
11
|
+
classifyModel: "auto",
|
|
12
|
+
routing: "ollama-first",
|
|
13
|
+
claudeFallback: "haiku"
|
|
14
|
+
},
|
|
15
|
+
ingest: {
|
|
16
|
+
scanPaths: ["~/Documents/Developer/"],
|
|
17
|
+
excludePaths: ["node_modules", ".git", "_archive", "dist", "build"],
|
|
18
|
+
autoIngestInterval: 1800
|
|
19
|
+
},
|
|
20
|
+
selfImprovement: {
|
|
21
|
+
enabled: true,
|
|
22
|
+
ruleThreshold: 3,
|
|
23
|
+
reviewInterval: 86400
|
|
24
|
+
},
|
|
25
|
+
daemon: {
|
|
26
|
+
healthCheckInterval: 300,
|
|
27
|
+
ingestInterval: 1800,
|
|
28
|
+
maintenanceInterval: 3600,
|
|
29
|
+
selfReviewInterval: 86400
|
|
30
|
+
},
|
|
31
|
+
notifications: {
|
|
32
|
+
silent: ["ollama-restart", "pending-flush", "categorize-batch"],
|
|
33
|
+
warn: ["db-corrupt", "disk-low", "config-corrupt"],
|
|
34
|
+
daily: true,
|
|
35
|
+
weekly: true
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
function loadConfig() {
|
|
39
|
+
if (!existsSync(CONFIG_PATH)) return DEFAULTS;
|
|
40
|
+
try {
|
|
41
|
+
const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
|
|
42
|
+
return {
|
|
43
|
+
...DEFAULTS,
|
|
44
|
+
...raw,
|
|
45
|
+
llm: { ...DEFAULTS.llm, ...raw.llm },
|
|
46
|
+
ingest: { ...DEFAULTS.ingest, ...raw.ingest },
|
|
47
|
+
selfImprovement: { ...DEFAULTS.selfImprovement, ...raw.selfImprovement },
|
|
48
|
+
daemon: { ...DEFAULTS.daemon, ...raw.daemon },
|
|
49
|
+
notifications: { ...DEFAULTS.notifications, ...raw.notifications }
|
|
50
|
+
};
|
|
51
|
+
} catch {
|
|
52
|
+
const bakPath = CONFIG_PATH + ".bak";
|
|
53
|
+
if (existsSync(bakPath)) {
|
|
54
|
+
try {
|
|
55
|
+
const bak = JSON.parse(readFileSync(bakPath, "utf-8"));
|
|
56
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(bak, null, 2));
|
|
57
|
+
return { ...DEFAULTS, ...bak };
|
|
58
|
+
} catch {
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return DEFAULTS;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
loadConfig
|
|
67
|
+
};
|