amalfa 1.0.8 → 1.0.10

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 +14 -4
  2. package/package.json +8 -2
  3. package/src/cli.ts +126 -1
package/README.md CHANGED
@@ -270,10 +270,20 @@ bun run dev
270
270
  ### Commands
271
271
 
272
272
  ```bash
273
- bun run dev # Start dev server
274
- bun run build # Build for production
275
- bun test # Run tests
276
- 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
277
287
  ```
278
288
 
279
289
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amalfa",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
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",
@@ -43,7 +43,13 @@
43
43
  "scripts": {
44
44
  "precommit": "bun run scripts/maintenance/pre-commit.ts",
45
45
  "check": "biome check .",
46
- "format": "biome format --write ."
46
+ "format": "biome format --write .",
47
+ "cli": "bun run src/cli.ts",
48
+ "cli:servers": "bun run src/cli.ts servers",
49
+ "cli:servers:dot": "bun run src/cli.ts servers --dot",
50
+ "cli:stats": "bun run src/cli.ts stats",
51
+ "cli:doctor": "bun run src/cli.ts doctor",
52
+ "cli:daemon": "bun run src/cli.ts daemon status"
47
53
  },
48
54
  "dependencies": {
49
55
  "@modelcontextprotocol/sdk": "1.25.0",
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.8";
6
+ const VERSION = "1.0.10";
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":