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.
Files changed (3) hide show
  1. package/README.md +20 -4
  2. package/package.json +1 -1
  3. 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
- bun run dev # Start dev server
268
- bun run build # Build for production
269
- bun test # Run tests
270
- bun run precommit # TypeScript + Biome checks
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.7",
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.7";
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":