amalfa 1.0.22 → 1.0.23
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 +1 -1
- package/src/cli/enhance-commands.ts +81 -0
- package/src/cli/list-scripts.ts +67 -0
- package/src/cli/sonar-chat.ts +95 -0
- package/src/cli.ts +346 -92
- package/src/config/defaults.ts +135 -26
- package/src/config/scripts-registry.json +72 -0
- package/src/daemon/index.ts +41 -24
- package/src/daemon/sonar-agent.ts +774 -0
- package/src/mcp/index.ts +114 -8
- package/src/resonance/db.ts +13 -0
- package/src/utils/DaemonManager.ts +68 -4
- package/src/utils/ServiceLifecycle.ts +195 -208
- package/src/utils/StatsTracker.ts +8 -4
- package/src/utils/ollama-discovery.ts +190 -0
- package/src/utils/sonar-client.ts +294 -0
- package/src/utils/ZombieDefense.ts +0 -258
package/src/cli.ts
CHANGED
|
@@ -29,6 +29,8 @@ Commands:
|
|
|
29
29
|
setup-mcp Generate MCP configuration JSON
|
|
30
30
|
daemon <action> Manage file watcher (start|stop|status|restart)
|
|
31
31
|
vector <action> Manage vector daemon (start|stop|status|restart)
|
|
32
|
+
sonar <action> Manage Sonar AI agent (start|stop|status|restart)
|
|
33
|
+
scripts list List available scripts and their descriptions
|
|
32
34
|
servers [--dot] Show status of all AMALFA services (--dot for graph)
|
|
33
35
|
|
|
34
36
|
Options:
|
|
@@ -42,6 +44,7 @@ Examples:
|
|
|
42
44
|
amalfa serve # Start MCP server for Claude Desktop
|
|
43
45
|
amalfa stats # Show knowledge graph statistics
|
|
44
46
|
amalfa doctor # Verify installation
|
|
47
|
+
amalfa sonar start # Start Sonar AI agent for enhanced search
|
|
45
48
|
|
|
46
49
|
Documentation: https://github.com/pjsvis/amalfa
|
|
47
50
|
`);
|
|
@@ -53,7 +56,7 @@ function showVersion() {
|
|
|
53
56
|
|
|
54
57
|
async function getDbPath(): Promise<string> {
|
|
55
58
|
if (DB_PATH) return DB_PATH;
|
|
56
|
-
|
|
59
|
+
|
|
57
60
|
// Load from config
|
|
58
61
|
const { loadConfig } = await import("./config/defaults");
|
|
59
62
|
const config = await loadConfig();
|
|
@@ -108,28 +111,68 @@ async function cmdStats() {
|
|
|
108
111
|
// Import database wrapper
|
|
109
112
|
const dbPath = await getDbPath();
|
|
110
113
|
const { ResonanceDB } = await import("./resonance/db");
|
|
114
|
+
const { loadConfig } = await import("./config/defaults");
|
|
115
|
+
|
|
111
116
|
const db = new ResonanceDB(dbPath);
|
|
117
|
+
const config = await loadConfig();
|
|
118
|
+
const sources = config.sources || ["./docs"];
|
|
112
119
|
|
|
113
120
|
try {
|
|
114
121
|
const stats = db.getStats();
|
|
115
|
-
const
|
|
116
|
-
const fileSizeMB = (
|
|
122
|
+
const dbStats = statSync(dbPath);
|
|
123
|
+
const fileSizeMB = (dbStats.size / 1024 / 1024).toFixed(2);
|
|
124
|
+
|
|
125
|
+
// Check for stale files
|
|
126
|
+
let newerFiles = 0;
|
|
127
|
+
let newestFileDate = new Date(0);
|
|
128
|
+
|
|
129
|
+
const { readdirSync, statSync: statSyncFs } = await import("node:fs");
|
|
130
|
+
|
|
131
|
+
function scan(dir: string) {
|
|
132
|
+
if (!existsSync(dir)) return;
|
|
133
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
134
|
+
for (const entry of entries) {
|
|
135
|
+
const fullPath = join(dir, entry.name);
|
|
136
|
+
if (entry.name.startsWith(".")) continue;
|
|
137
|
+
|
|
138
|
+
if (entry.isDirectory()) {
|
|
139
|
+
scan(fullPath);
|
|
140
|
+
} else if (entry.name.endsWith(".md")) {
|
|
141
|
+
const mtime = statSyncFs(fullPath).mtime;
|
|
142
|
+
if (mtime > dbStats.mtime) {
|
|
143
|
+
newerFiles++;
|
|
144
|
+
}
|
|
145
|
+
if (mtime > newestFileDate) {
|
|
146
|
+
newestFileDate = mtime;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
117
151
|
|
|
118
|
-
|
|
152
|
+
for (const source of sources) {
|
|
153
|
+
scan(join(process.cwd(), source));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const isStale = newerFiles > 0;
|
|
157
|
+
const statusIcon = isStale ? "⚠️ STALE" : "✅ FRESH";
|
|
158
|
+
|
|
159
|
+
console.log(`
|
|
119
160
|
📊 AMALFA Database Statistics
|
|
120
161
|
|
|
121
162
|
Database: ${dbPath}
|
|
122
|
-
|
|
163
|
+
Status: ${statusIcon}
|
|
164
|
+
Size: ${fileSizeMB} MB
|
|
123
165
|
|
|
124
|
-
Nodes:
|
|
125
|
-
Edges:
|
|
166
|
+
Nodes: ${stats.nodes.toLocaleString()}
|
|
167
|
+
Edges: ${stats.edges.toLocaleString()}
|
|
126
168
|
Embeddings: ${stats.vectors.toLocaleString()} (384-dim)
|
|
127
169
|
|
|
128
|
-
|
|
129
|
-
|
|
170
|
+
Sources: ${sources.join(", ")}
|
|
171
|
+
DB Modified: ${new Date(dbStats.mtime).toISOString()}
|
|
172
|
+
Latest File: ${newestFileDate.toISOString()}
|
|
173
|
+
Stale Files: ${newerFiles}
|
|
130
174
|
|
|
131
|
-
|
|
132
|
-
📝 To update: Run 'amalfa daemon start' to watch for file changes
|
|
175
|
+
${isStale ? "⚠️ ACTION REQUIRED: Run 'amalfa init' or start 'amalfa daemon' to update!" : "🔍 System is up to date."}
|
|
133
176
|
`);
|
|
134
177
|
} catch (error) {
|
|
135
178
|
console.error("❌ Failed to read database statistics:", error);
|
|
@@ -165,7 +208,9 @@ async function cmdInit() {
|
|
|
165
208
|
console.log(` Total files: ${report.totalFiles}`);
|
|
166
209
|
console.log(` Valid files: ${report.validFiles}`);
|
|
167
210
|
console.log(` Skipped files: ${report.skippedFiles}`);
|
|
168
|
-
console.log(
|
|
211
|
+
console.log(
|
|
212
|
+
` Total size: ${(report.totalSizeBytes / 1024 / 1024).toFixed(2)} MB`,
|
|
213
|
+
);
|
|
169
214
|
console.log(` Estimated nodes: ${report.estimatedNodes}\n`);
|
|
170
215
|
|
|
171
216
|
if (report.hasErrors) {
|
|
@@ -174,7 +219,9 @@ async function cmdInit() {
|
|
|
174
219
|
for (const issue of report.issues.filter((i) => i.severity === "error")) {
|
|
175
220
|
console.error(` - ${issue.path}: ${issue.details}`);
|
|
176
221
|
}
|
|
177
|
-
|
|
222
|
+
console.error(
|
|
223
|
+
"\nSee .amalfa/logs/pre-flight.log for details and recommendations",
|
|
224
|
+
);
|
|
178
225
|
console.error("\nFix these issues and try again.");
|
|
179
226
|
process.exit(1);
|
|
180
227
|
}
|
|
@@ -185,14 +232,14 @@ async function cmdInit() {
|
|
|
185
232
|
for (const issue of report.issues.filter((i) => i.severity === "warning")) {
|
|
186
233
|
console.warn(` - ${issue.path}: ${issue.details}`);
|
|
187
234
|
}
|
|
188
|
-
|
|
235
|
+
console.warn("\nSee .amalfa/logs/pre-flight.log for recommendations");
|
|
189
236
|
console.warn("\nTo proceed anyway, use: amalfa init --force");
|
|
190
237
|
process.exit(1);
|
|
191
238
|
}
|
|
192
239
|
|
|
193
240
|
if (report.validFiles === 0) {
|
|
194
241
|
console.error("\n❌ No valid markdown files found");
|
|
195
|
-
|
|
242
|
+
console.error("See .amalfa/logs/pre-flight.log for details");
|
|
196
243
|
process.exit(1);
|
|
197
244
|
}
|
|
198
245
|
|
|
@@ -231,7 +278,7 @@ async function cmdInit() {
|
|
|
231
278
|
const tracker = new StatsTracker();
|
|
232
279
|
const fileSize = statSync(dbPath).size;
|
|
233
280
|
const dbSizeMB = fileSize / 1024 / 1024;
|
|
234
|
-
|
|
281
|
+
|
|
235
282
|
await tracker.recordSnapshot({
|
|
236
283
|
timestamp: new Date().toISOString(),
|
|
237
284
|
nodes: result.stats.nodes,
|
|
@@ -272,16 +319,168 @@ async function cmdDaemon() {
|
|
|
272
319
|
process.exit(1);
|
|
273
320
|
}
|
|
274
321
|
|
|
275
|
-
|
|
276
|
-
const
|
|
277
|
-
const proc = spawn("bun", ["run", daemonPath, action], {
|
|
278
|
-
stdio: "inherit",
|
|
279
|
-
cwd: process.cwd(),
|
|
280
|
-
});
|
|
322
|
+
const { DaemonManager } = await import("./utils/DaemonManager");
|
|
323
|
+
const manager = new DaemonManager();
|
|
281
324
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
325
|
+
if (action === "status") {
|
|
326
|
+
const status = await manager.checkFileWatcher();
|
|
327
|
+
if (status.running) {
|
|
328
|
+
console.log(`✅ File Watcher: Running (PID: ${status.pid})`);
|
|
329
|
+
} else {
|
|
330
|
+
console.log("❌ File Watcher: Stopped");
|
|
331
|
+
}
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (action === "start") {
|
|
336
|
+
console.log("🚀 Starting File Watcher...");
|
|
337
|
+
try {
|
|
338
|
+
await manager.startFileWatcher();
|
|
339
|
+
console.log("✅ File Watcher started");
|
|
340
|
+
} catch (e) {
|
|
341
|
+
console.error("❌ Failed to start File Watcher:", e);
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
if (action === "stop") {
|
|
348
|
+
console.log("🛑 Stopping File Watcher...");
|
|
349
|
+
try {
|
|
350
|
+
await manager.stopFileWatcher();
|
|
351
|
+
console.log("✅ File Watcher stopped");
|
|
352
|
+
} catch (e) {
|
|
353
|
+
console.error("❌ Failed to stop File Watcher:", e);
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (action === "restart") {
|
|
360
|
+
console.log("🔄 Restarting File Watcher...");
|
|
361
|
+
try {
|
|
362
|
+
await manager.stopFileWatcher();
|
|
363
|
+
await manager.startFileWatcher();
|
|
364
|
+
console.log("✅ File Watcher restarted");
|
|
365
|
+
} catch (e) {
|
|
366
|
+
console.error("❌ Failed to restart File Watcher:", e);
|
|
367
|
+
process.exit(1);
|
|
368
|
+
}
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
async function cmdSonar() {
|
|
374
|
+
const action = args[1] || "status";
|
|
375
|
+
const validActions = ["start", "stop", "status", "restart", "chat"];
|
|
376
|
+
|
|
377
|
+
if (!validActions.includes(action)) {
|
|
378
|
+
console.error(`❌ Invalid action: ${action}`);
|
|
379
|
+
console.error("\nUsage: amalfa sonar <start|stop|status|restart|chat>");
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const { DaemonManager } = await import("./utils/DaemonManager");
|
|
384
|
+
const { discoverOllamaCapabilities } = await import(
|
|
385
|
+
"./utils/ollama-discovery"
|
|
386
|
+
);
|
|
387
|
+
const manager = new DaemonManager();
|
|
388
|
+
|
|
389
|
+
if (action === "status") {
|
|
390
|
+
console.log("🔍 Checking status...");
|
|
391
|
+
|
|
392
|
+
// Check Ollama
|
|
393
|
+
try {
|
|
394
|
+
const ollama = await discoverOllamaCapabilities();
|
|
395
|
+
if (ollama.available) {
|
|
396
|
+
console.log(
|
|
397
|
+
`✅ Ollama: Running (Model: ${ollama.model}, Size: ${ollama.size})`,
|
|
398
|
+
);
|
|
399
|
+
} else {
|
|
400
|
+
console.log("❌ Ollama: Not detected");
|
|
401
|
+
}
|
|
402
|
+
} catch {
|
|
403
|
+
console.log("❌ Ollama: Check failed");
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Check Daemon
|
|
407
|
+
const status = await manager.checkSonarAgent();
|
|
408
|
+
|
|
409
|
+
if (status.running) {
|
|
410
|
+
console.log(
|
|
411
|
+
`✅ Sonar Agent: Running (PID: ${status.pid}, Port: ${status.port})`,
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
// Check health endpoint
|
|
415
|
+
try {
|
|
416
|
+
const res = await fetch(`http://localhost:${status.port}/health`);
|
|
417
|
+
const health = await res.json();
|
|
418
|
+
console.log(` Health: ${JSON.stringify(health)}`);
|
|
419
|
+
} catch {
|
|
420
|
+
console.log(" Health: ⚠️ Unresponsive");
|
|
421
|
+
}
|
|
422
|
+
} else {
|
|
423
|
+
console.log("❌ Sonar Agent: Stopped");
|
|
424
|
+
}
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (action === "start") {
|
|
429
|
+
console.log("🚀 Starting Sonar Agent...");
|
|
430
|
+
try {
|
|
431
|
+
await manager.startSonarAgent();
|
|
432
|
+
console.log("✅ Sonar Agent started");
|
|
433
|
+
} catch (e) {
|
|
434
|
+
console.error("❌ Failed to start Sonar Agent:", e);
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (action === "stop") {
|
|
441
|
+
console.log("🛑 Stopping Sonar Agent...");
|
|
442
|
+
try {
|
|
443
|
+
await manager.stopSonarAgent();
|
|
444
|
+
console.log("✅ Sonar Agent stopped");
|
|
445
|
+
} catch (e) {
|
|
446
|
+
console.error("❌ Failed to stop Sonar Agent:", e);
|
|
447
|
+
process.exit(1);
|
|
448
|
+
}
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (action === "chat") {
|
|
453
|
+
const { chatLoop } = await import("./cli/sonar-chat");
|
|
454
|
+
await chatLoop();
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
if (action === "restart") {
|
|
459
|
+
console.log("🔄 Restarting Sonar Agent...");
|
|
460
|
+
try {
|
|
461
|
+
await manager.stopSonarAgent();
|
|
462
|
+
await manager.startSonarAgent();
|
|
463
|
+
console.log("✅ Sonar Agent restarted");
|
|
464
|
+
} catch (e) {
|
|
465
|
+
console.error("❌ Failed to restart Sonar Agent:", e);
|
|
466
|
+
process.exit(1);
|
|
467
|
+
}
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
async function cmdScripts() {
|
|
473
|
+
const action = args[1] || "list";
|
|
474
|
+
|
|
475
|
+
if (action === "list") {
|
|
476
|
+
// Dynamic import to avoid loading everything at startup
|
|
477
|
+
await import("./cli/list-scripts");
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
console.error(`❌ Invalid action: ${action}`);
|
|
482
|
+
console.error("Usage: amalfa scripts list");
|
|
483
|
+
process.exit(1);
|
|
285
484
|
}
|
|
286
485
|
|
|
287
486
|
async function cmdVector() {
|
|
@@ -295,7 +494,10 @@ async function cmdVector() {
|
|
|
295
494
|
}
|
|
296
495
|
|
|
297
496
|
// Run vector daemon with the specified action
|
|
298
|
-
const vectorPath = join(
|
|
497
|
+
const vectorPath = join(
|
|
498
|
+
import.meta.dir,
|
|
499
|
+
"resonance/services/vector-daemon.ts",
|
|
500
|
+
);
|
|
299
501
|
const proc = spawn("bun", ["run", vectorPath, action], {
|
|
300
502
|
stdio: "inherit",
|
|
301
503
|
cwd: process.cwd(),
|
|
@@ -308,22 +510,22 @@ async function cmdVector() {
|
|
|
308
510
|
|
|
309
511
|
async function cmdSetupMcp() {
|
|
310
512
|
const { resolve } = await import("node:path");
|
|
311
|
-
|
|
513
|
+
|
|
312
514
|
const cwd = resolve(process.cwd());
|
|
313
515
|
const mcpScript = resolve(cwd, "src/mcp/index.ts");
|
|
314
|
-
|
|
516
|
+
|
|
315
517
|
// Minimal PATH for MCP - only include essential directories
|
|
316
|
-
const bunPath = process.execPath.replace(/\/bun$/,
|
|
518
|
+
const bunPath = process.execPath.replace(/\/bun$/, ""); // Directory containing bun
|
|
317
519
|
const minimalPath = [
|
|
318
520
|
bunPath,
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
].join(
|
|
326
|
-
|
|
521
|
+
"/usr/local/bin",
|
|
522
|
+
"/usr/bin",
|
|
523
|
+
"/bin",
|
|
524
|
+
"/usr/sbin",
|
|
525
|
+
"/sbin",
|
|
526
|
+
"/opt/homebrew/bin", // Apple Silicon Homebrew
|
|
527
|
+
].join(":");
|
|
528
|
+
|
|
327
529
|
const config = {
|
|
328
530
|
mcpServers: {
|
|
329
531
|
amalfa: {
|
|
@@ -335,19 +537,21 @@ async function cmdSetupMcp() {
|
|
|
335
537
|
},
|
|
336
538
|
},
|
|
337
539
|
};
|
|
338
|
-
|
|
540
|
+
|
|
339
541
|
console.log("\n✅ AMALFA MCP Configuration");
|
|
340
542
|
console.log("=".repeat(60));
|
|
341
543
|
console.log(`📂 Installation: ${cwd}`);
|
|
342
544
|
console.log("=".repeat(60));
|
|
343
545
|
console.log("\n📋 Copy this JSON to your MCP client config:");
|
|
344
|
-
console.log(
|
|
546
|
+
console.log(
|
|
547
|
+
" Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json",
|
|
548
|
+
);
|
|
345
549
|
console.log(" Warp Preview: MCP settings\n");
|
|
346
550
|
console.log("=".repeat(60));
|
|
347
551
|
console.log();
|
|
348
|
-
|
|
552
|
+
|
|
349
553
|
console.log(JSON.stringify(config, null, 2));
|
|
350
|
-
|
|
554
|
+
|
|
351
555
|
console.log();
|
|
352
556
|
console.log("=".repeat(60));
|
|
353
557
|
console.log("💡 Tip: If you move this folder, run 'amalfa setup-mcp' again");
|
|
@@ -358,11 +562,36 @@ async function cmdSetupMcp() {
|
|
|
358
562
|
async function cmdServers() {
|
|
359
563
|
const showDot = args.includes("--dot");
|
|
360
564
|
const { AMALFA_DIRS } = await import("./config/defaults");
|
|
361
|
-
|
|
565
|
+
|
|
362
566
|
const SERVICES = [
|
|
363
|
-
{
|
|
364
|
-
|
|
365
|
-
|
|
567
|
+
{
|
|
568
|
+
name: "MCP Server",
|
|
569
|
+
pidFile: join(AMALFA_DIRS.runtime, "mcp.pid"),
|
|
570
|
+
port: "stdio",
|
|
571
|
+
id: "mcp",
|
|
572
|
+
cmd: "amalfa serve",
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: "Vector Daemon",
|
|
576
|
+
pidFile: join(AMALFA_DIRS.runtime, "vector-daemon.pid"),
|
|
577
|
+
port: "3010",
|
|
578
|
+
id: "vector",
|
|
579
|
+
cmd: "amalfa vector start",
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
name: "File Watcher",
|
|
583
|
+
pidFile: join(AMALFA_DIRS.runtime, "daemon.pid"),
|
|
584
|
+
port: "-",
|
|
585
|
+
id: "watcher",
|
|
586
|
+
cmd: "amalfa daemon start",
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
name: "Sonar Agent",
|
|
590
|
+
pidFile: join(AMALFA_DIRS.runtime, "sonar.pid"),
|
|
591
|
+
port: "3012",
|
|
592
|
+
id: "sonar",
|
|
593
|
+
cmd: "amalfa sonar start",
|
|
594
|
+
},
|
|
366
595
|
];
|
|
367
596
|
|
|
368
597
|
async function isRunning(pid: number): Promise<boolean> {
|
|
@@ -373,22 +602,22 @@ async function cmdServers() {
|
|
|
373
602
|
return false;
|
|
374
603
|
}
|
|
375
604
|
}
|
|
376
|
-
|
|
605
|
+
|
|
377
606
|
if (showDot) {
|
|
378
607
|
// Generate DOT diagram
|
|
379
608
|
const statuses = new Map<string, { status: string; pid: string }>();
|
|
380
|
-
|
|
609
|
+
|
|
381
610
|
for (const svc of SERVICES) {
|
|
382
611
|
let status = "stopped";
|
|
383
612
|
let pidStr = "-";
|
|
384
|
-
|
|
613
|
+
|
|
385
614
|
if (existsSync(svc.pidFile)) {
|
|
386
615
|
try {
|
|
387
616
|
const { readFileSync } = await import("node:fs");
|
|
388
617
|
const text = readFileSync(svc.pidFile, "utf-8");
|
|
389
618
|
const pid = Number.parseInt(text.trim(), 10);
|
|
390
|
-
|
|
391
|
-
if (!Number.isNaN(pid) && await isRunning(pid)) {
|
|
619
|
+
|
|
620
|
+
if (!Number.isNaN(pid) && (await isRunning(pid))) {
|
|
392
621
|
status = "running";
|
|
393
622
|
pidStr = pid.toString();
|
|
394
623
|
} else {
|
|
@@ -399,32 +628,39 @@ async function cmdServers() {
|
|
|
399
628
|
// Ignore
|
|
400
629
|
}
|
|
401
630
|
}
|
|
402
|
-
|
|
631
|
+
|
|
403
632
|
statuses.set(svc.id, { status, pid: pidStr });
|
|
404
633
|
}
|
|
405
|
-
|
|
634
|
+
|
|
406
635
|
console.log("digraph AMALFA {");
|
|
407
636
|
console.log(" rankdir=LR;");
|
|
408
637
|
console.log(" node [shape=box, style=filled];");
|
|
409
638
|
console.log("");
|
|
410
639
|
console.log(" // Nodes");
|
|
411
|
-
|
|
640
|
+
|
|
412
641
|
for (const svc of SERVICES) {
|
|
413
642
|
const st = statuses.get(svc.id);
|
|
414
|
-
const color =
|
|
643
|
+
const color =
|
|
644
|
+
st?.status === "running"
|
|
645
|
+
? "lightgreen"
|
|
646
|
+
: st?.status === "stale"
|
|
647
|
+
? "orange"
|
|
648
|
+
: "lightgray";
|
|
415
649
|
const label = `${svc.name}\\nPort: ${svc.port}\\nPID: ${st?.pid || "-"}`;
|
|
416
650
|
console.log(` ${svc.id} [label="${label}", fillcolor=${color}];`);
|
|
417
651
|
}
|
|
418
|
-
|
|
652
|
+
|
|
419
653
|
console.log("");
|
|
420
654
|
console.log(" // Database");
|
|
421
|
-
console.log(
|
|
655
|
+
console.log(
|
|
656
|
+
' db [label="SQLite\\n.amalfa/resonance.db", shape=cylinder, fillcolor=lightyellow];',
|
|
657
|
+
);
|
|
422
658
|
console.log("");
|
|
423
659
|
console.log(" // Connections");
|
|
424
|
-
console.log(
|
|
425
|
-
console.log(
|
|
426
|
-
console.log(
|
|
427
|
-
console.log(
|
|
660
|
+
console.log(' mcp -> db [label="read/write"];');
|
|
661
|
+
console.log(' vector -> db [label="embeddings"];');
|
|
662
|
+
console.log(' watcher -> db [label="updates"];');
|
|
663
|
+
console.log(' mcp -> vector [label="query", style=dashed];');
|
|
428
664
|
console.log("}");
|
|
429
665
|
console.log("");
|
|
430
666
|
console.log("# Save to file: amalfa servers --dot > amalfa.dot");
|
|
@@ -436,10 +672,10 @@ async function cmdServers() {
|
|
|
436
672
|
console.log("─".repeat(95));
|
|
437
673
|
console.log(
|
|
438
674
|
"SERVICE".padEnd(18) +
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
675
|
+
"COMMAND".padEnd(25) +
|
|
676
|
+
"PORT".padEnd(12) +
|
|
677
|
+
"STATUS".padEnd(15) +
|
|
678
|
+
"PID".padEnd(10),
|
|
443
679
|
);
|
|
444
680
|
console.log("─".repeat(95));
|
|
445
681
|
|
|
@@ -453,7 +689,7 @@ async function cmdServers() {
|
|
|
453
689
|
const text = readFileSync(svc.pidFile, "utf-8");
|
|
454
690
|
const pid = Number.parseInt(text.trim(), 10);
|
|
455
691
|
|
|
456
|
-
if (!Number.isNaN(pid) && await isRunning(pid)) {
|
|
692
|
+
if (!Number.isNaN(pid) && (await isRunning(pid))) {
|
|
457
693
|
status = "🟢 RUNNING";
|
|
458
694
|
pidStr = pid.toString();
|
|
459
695
|
} else {
|
|
@@ -467,15 +703,17 @@ async function cmdServers() {
|
|
|
467
703
|
|
|
468
704
|
console.log(
|
|
469
705
|
svc.name.padEnd(18) +
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
706
|
+
svc.cmd.padEnd(25) +
|
|
707
|
+
svc.port.padEnd(12) +
|
|
708
|
+
status.padEnd(15) +
|
|
709
|
+
pidStr.padEnd(10),
|
|
474
710
|
);
|
|
475
711
|
}
|
|
476
712
|
|
|
477
713
|
console.log("─".repeat(95));
|
|
478
|
-
console.log(
|
|
714
|
+
console.log(
|
|
715
|
+
"\n💡 Commands: amalfa serve | amalfa vector start | amalfa daemon start\n",
|
|
716
|
+
);
|
|
479
717
|
}
|
|
480
718
|
|
|
481
719
|
async function cmdValidate() {
|
|
@@ -635,7 +873,9 @@ async function cmdDoctor() {
|
|
|
635
873
|
console.log(" amalfa serve # Start MCP server");
|
|
636
874
|
console.log(" amalfa stats # View database statistics");
|
|
637
875
|
} else {
|
|
638
|
-
console.log(
|
|
876
|
+
console.log(
|
|
877
|
+
`❌ Found ${issues} issue(s). Please resolve them and try again.`,
|
|
878
|
+
);
|
|
639
879
|
process.exit(1);
|
|
640
880
|
}
|
|
641
881
|
}
|
|
@@ -651,33 +891,47 @@ async function main() {
|
|
|
651
891
|
await cmdStats();
|
|
652
892
|
break;
|
|
653
893
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
894
|
+
case "doctor":
|
|
895
|
+
await cmdDoctor();
|
|
896
|
+
break;
|
|
657
897
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
898
|
+
case "validate":
|
|
899
|
+
await cmdValidate();
|
|
900
|
+
break;
|
|
661
901
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
902
|
+
case "init":
|
|
903
|
+
await cmdInit();
|
|
904
|
+
break;
|
|
665
905
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
906
|
+
case "daemon":
|
|
907
|
+
await cmdDaemon();
|
|
908
|
+
break;
|
|
669
909
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
910
|
+
case "vector":
|
|
911
|
+
await cmdVector();
|
|
912
|
+
break;
|
|
673
913
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
914
|
+
case "setup-mcp":
|
|
915
|
+
await cmdSetupMcp();
|
|
916
|
+
break;
|
|
677
917
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
918
|
+
case "servers":
|
|
919
|
+
await cmdServers();
|
|
920
|
+
break;
|
|
921
|
+
|
|
922
|
+
case "sonar":
|
|
923
|
+
await cmdSonar();
|
|
924
|
+
break;
|
|
925
|
+
|
|
926
|
+
case "scripts":
|
|
927
|
+
await cmdScripts();
|
|
928
|
+
break;
|
|
929
|
+
|
|
930
|
+
case "enhance": {
|
|
931
|
+
const { cmdEnhance } = await import("./cli/enhance-commands");
|
|
932
|
+
await cmdEnhance(args);
|
|
933
|
+
break;
|
|
934
|
+
}
|
|
681
935
|
|
|
682
936
|
case "version":
|
|
683
937
|
case "--version":
|