amalfa 1.0.7 ā 1.0.9
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/README.md +20 -4
- package/package.json +1 -1
- package/src/cli.ts +126 -1
package/README.md
CHANGED
|
@@ -106,6 +106,12 @@ for cluster in clusters:
|
|
|
106
106
|
bun install -g amalfa
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
+
**Why Bun?**
|
|
110
|
+
- ā” **Fast startup** - Critical for stdio-based MCP servers that spawn on every request
|
|
111
|
+
- š **Built-in daemon management** - Runs background processes for file watching and vector embeddings
|
|
112
|
+
- š¦ **Native TypeScript** - No compilation step, direct execution from source
|
|
113
|
+
- šÆ **SQLite performance** - Optimized native bindings for database operations
|
|
114
|
+
|
|
109
115
|
**From source** (for development):
|
|
110
116
|
```bash
|
|
111
117
|
git clone https://github.com/pjsvis/amalfa.git
|
|
@@ -264,10 +270,20 @@ bun run dev
|
|
|
264
270
|
### Commands
|
|
265
271
|
|
|
266
272
|
```bash
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
273
|
+
# CLI commands (available after global install)
|
|
274
|
+
amalfa init # Initialize database from markdown
|
|
275
|
+
amalfa serve # Start MCP server (stdio)
|
|
276
|
+
amalfa stats # Show database statistics
|
|
277
|
+
amalfa doctor # Health check
|
|
278
|
+
amalfa servers # Show all service status
|
|
279
|
+
amalfa daemon start # Start file watcher daemon
|
|
280
|
+
amalfa daemon stop # Stop file watcher daemon
|
|
281
|
+
amalfa daemon status # Check daemon status
|
|
282
|
+
amalfa setup-mcp # Generate MCP config
|
|
283
|
+
|
|
284
|
+
# Development commands
|
|
285
|
+
bun test # Run tests
|
|
286
|
+
bun run precommit # TypeScript + Biome checks
|
|
271
287
|
```
|
|
272
288
|
|
|
273
289
|
---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "amalfa",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "Local-first knowledge graph engine for AI agents. Transforms markdown into searchable memory with MCP protocol.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/pjsvis/amalfa#readme",
|
package/src/cli.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync, statSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
|
|
6
|
-
const VERSION = "1.0.
|
|
6
|
+
const VERSION = "1.0.9";
|
|
7
7
|
|
|
8
8
|
// Database path loaded from config (lazy loaded per command)
|
|
9
9
|
let DB_PATH: string | null = null;
|
|
@@ -26,6 +26,7 @@ Commands:
|
|
|
26
26
|
doctor Check installation and configuration
|
|
27
27
|
setup-mcp Generate MCP configuration JSON
|
|
28
28
|
daemon <action> Manage file watcher (start|stop|status|restart)
|
|
29
|
+
servers [--dot] Show status of all AMALFA services (--dot for graph)
|
|
29
30
|
|
|
30
31
|
Options:
|
|
31
32
|
--force Override pre-flight warnings (errors still block)
|
|
@@ -315,6 +316,126 @@ async function cmdSetupMcp() {
|
|
|
315
316
|
console.log();
|
|
316
317
|
}
|
|
317
318
|
|
|
319
|
+
async function cmdServers() {
|
|
320
|
+
const showDot = args.includes("--dot");
|
|
321
|
+
|
|
322
|
+
const SERVICES = [
|
|
323
|
+
{ name: "MCP Server", pidFile: ".mcp.pid", port: "stdio", id: "mcp" },
|
|
324
|
+
{ name: "Vector Daemon", pidFile: ".vector-daemon.pid", port: "3010", id: "vector" },
|
|
325
|
+
{ name: "File Watcher", pidFile: ".amalfa-daemon.pid", port: "-", id: "watcher" },
|
|
326
|
+
];
|
|
327
|
+
|
|
328
|
+
async function isRunning(pid: number): Promise<boolean> {
|
|
329
|
+
try {
|
|
330
|
+
process.kill(pid, 0);
|
|
331
|
+
return true;
|
|
332
|
+
} catch {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (showDot) {
|
|
338
|
+
// Generate DOT diagram
|
|
339
|
+
const statuses = new Map<string, { status: string; pid: string }>();
|
|
340
|
+
|
|
341
|
+
for (const svc of SERVICES) {
|
|
342
|
+
let status = "stopped";
|
|
343
|
+
let pidStr = "-";
|
|
344
|
+
|
|
345
|
+
if (existsSync(svc.pidFile)) {
|
|
346
|
+
try {
|
|
347
|
+
const { readFileSync } = await import("node:fs");
|
|
348
|
+
const text = readFileSync(svc.pidFile, "utf-8");
|
|
349
|
+
const pid = Number.parseInt(text.trim(), 10);
|
|
350
|
+
|
|
351
|
+
if (!Number.isNaN(pid) && await isRunning(pid)) {
|
|
352
|
+
status = "running";
|
|
353
|
+
pidStr = pid.toString();
|
|
354
|
+
} else {
|
|
355
|
+
status = "stale";
|
|
356
|
+
pidStr = `${pid}`;
|
|
357
|
+
}
|
|
358
|
+
} catch {
|
|
359
|
+
// Ignore
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
statuses.set(svc.id, { status, pid: pidStr });
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
console.log("digraph AMALFA {");
|
|
367
|
+
console.log(" rankdir=LR;");
|
|
368
|
+
console.log(" node [shape=box, style=filled];");
|
|
369
|
+
console.log("");
|
|
370
|
+
console.log(" // Nodes");
|
|
371
|
+
|
|
372
|
+
for (const svc of SERVICES) {
|
|
373
|
+
const st = statuses.get(svc.id);
|
|
374
|
+
const color = st?.status === "running" ? "lightgreen" : st?.status === "stale" ? "orange" : "lightgray";
|
|
375
|
+
const label = `${svc.name}\\nPort: ${svc.port}\\nPID: ${st?.pid || "-"}`;
|
|
376
|
+
console.log(` ${svc.id} [label="${label}", fillcolor=${color}];`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
console.log("");
|
|
380
|
+
console.log(" // Database");
|
|
381
|
+
console.log(" db [label=\"SQLite\\n.amalfa/resonance.db\", shape=cylinder, fillcolor=lightyellow];");
|
|
382
|
+
console.log("");
|
|
383
|
+
console.log(" // Connections");
|
|
384
|
+
console.log(" mcp -> db [label=\"read/write\"];");
|
|
385
|
+
console.log(" vector -> db [label=\"embeddings\"];");
|
|
386
|
+
console.log(" watcher -> db [label=\"updates\"];");
|
|
387
|
+
console.log(" mcp -> vector [label=\"query\", style=dashed];");
|
|
388
|
+
console.log("}");
|
|
389
|
+
console.log("");
|
|
390
|
+
console.log("# Save to file: amalfa servers --dot > amalfa.dot");
|
|
391
|
+
console.log("# Render: dot -Tpng amalfa.dot -o amalfa.png");
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
console.log("\nš” AMALFA Service Status\n");
|
|
396
|
+
console.log("ā".repeat(70));
|
|
397
|
+
console.log(
|
|
398
|
+
"SERVICE".padEnd(18) +
|
|
399
|
+
"PORT".padEnd(12) +
|
|
400
|
+
"STATUS".padEnd(15) +
|
|
401
|
+
"PID".padEnd(10)
|
|
402
|
+
);
|
|
403
|
+
console.log("ā".repeat(70));
|
|
404
|
+
|
|
405
|
+
for (const svc of SERVICES) {
|
|
406
|
+
const { readFileSync } = await import("node:fs");
|
|
407
|
+
let status = "āŖļø STOPPED";
|
|
408
|
+
let pidStr = "-";
|
|
409
|
+
|
|
410
|
+
if (existsSync(svc.pidFile)) {
|
|
411
|
+
try {
|
|
412
|
+
const text = readFileSync(svc.pidFile, "utf-8");
|
|
413
|
+
const pid = Number.parseInt(text.trim(), 10);
|
|
414
|
+
|
|
415
|
+
if (!Number.isNaN(pid) && await isRunning(pid)) {
|
|
416
|
+
status = "š¢ RUNNING";
|
|
417
|
+
pidStr = pid.toString();
|
|
418
|
+
} else {
|
|
419
|
+
status = "š“ STALE";
|
|
420
|
+
pidStr = `${pid} (?)`;
|
|
421
|
+
}
|
|
422
|
+
} catch {
|
|
423
|
+
// Ignore read errors
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
console.log(
|
|
428
|
+
svc.name.padEnd(18) +
|
|
429
|
+
svc.port.padEnd(12) +
|
|
430
|
+
status.padEnd(15) +
|
|
431
|
+
pidStr.padEnd(10)
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
console.log("ā".repeat(70));
|
|
436
|
+
console.log("\nš” Tip: Use 'amalfa daemon start' to start the file watcher\n");
|
|
437
|
+
}
|
|
438
|
+
|
|
318
439
|
async function cmdDoctor() {
|
|
319
440
|
console.log("𩺠AMALFA Health Check\n");
|
|
320
441
|
|
|
@@ -420,6 +541,10 @@ async function main() {
|
|
|
420
541
|
await cmdSetupMcp();
|
|
421
542
|
break;
|
|
422
543
|
|
|
544
|
+
case "servers":
|
|
545
|
+
await cmdServers();
|
|
546
|
+
break;
|
|
547
|
+
|
|
423
548
|
case "version":
|
|
424
549
|
case "--version":
|
|
425
550
|
case "-v":
|