amalfa 1.0.1 → 1.0.2

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 (83) hide show
  1. package/README.md +226 -263
  2. package/docs/AGENT-METADATA-PATTERNS.md +1021 -0
  3. package/docs/CONFIG_E2E_VALIDATION.md +147 -0
  4. package/docs/CONFIG_UNIFICATION.md +187 -0
  5. package/docs/CONFIG_VALIDATION.md +103 -0
  6. package/docs/LEGACY_DEPRECATION.md +174 -0
  7. package/docs/MCP_SETUP.md +317 -0
  8. package/docs/QUICK_START_MCP.md +168 -0
  9. package/docs/SESSION-2026-01-06-METADATA-PATTERNS.md +346 -0
  10. package/docs/SETUP.md +464 -0
  11. package/docs/SETUP_COMPLETE.md +464 -0
  12. package/docs/VISION-AGENT-LEARNING.md +1242 -0
  13. package/docs/_current-config-status.md +93 -0
  14. package/package.json +6 -3
  15. package/polyvis.settings.json.bak +38 -0
  16. package/src/cli.ts +103 -21
  17. package/src/config/defaults.ts +52 -12
  18. package/src/core/VectorEngine.ts +18 -9
  19. package/src/mcp/index.ts +62 -7
  20. package/src/resonance/DatabaseFactory.ts +3 -4
  21. package/src/resonance/db.ts +4 -4
  22. package/src/resonance/services/vector-daemon.ts +151 -0
  23. package/src/utils/DaemonManager.ts +147 -0
  24. package/src/utils/ZombieDefense.ts +5 -1
  25. package/:memory: +0 -0
  26. package/:memory:-shm +0 -0
  27. package/:memory:-wal +0 -0
  28. package/CHANGELOG.md.old +0 -43
  29. package/README.old.md +0 -112
  30. package/ROADMAP.md +0 -316
  31. package/TEST_PLAN.md +0 -561
  32. package/agents.config.json +0 -11
  33. package/drizzle/0000_minor_iron_fist.sql +0 -19
  34. package/drizzle/meta/0000_snapshot.json +0 -139
  35. package/drizzle/meta/_journal.json +0 -13
  36. package/example_usage.ts +0 -39
  37. package/experiment.sh +0 -35
  38. package/hello +0 -2
  39. package/index.html +0 -52
  40. package/knowledge/excalibur.md +0 -12
  41. package/plans/experience-graph-integration.md +0 -60
  42. package/prompts/gemini-king-mode-prompt.md +0 -46
  43. package/public/docs/MCP_TOOLS.md +0 -372
  44. package/schemas/README.md +0 -20
  45. package/schemas/cda.schema.json +0 -84
  46. package/schemas/conceptual-lexicon.schema.json +0 -75
  47. package/scratchpads/dummy-debrief-boxed.md +0 -39
  48. package/scratchpads/dummy-debrief.md +0 -27
  49. package/scratchpads/scratchpad-design.md +0 -50
  50. package/scratchpads/scratchpad-scrolling.md +0 -20
  51. package/scratchpads/scratchpad-toc-disappearance.md +0 -23
  52. package/scratchpads/scratchpad-toc.md +0 -28
  53. package/scratchpads/test_gardener.md +0 -7
  54. package/src/core/LLMClient.ts +0 -93
  55. package/src/core/TagEngine.ts +0 -56
  56. package/src/db/schema.ts +0 -46
  57. package/src/gardeners/AutoTagger.ts +0 -116
  58. package/src/pipeline/HarvesterPipeline.ts +0 -101
  59. package/src/pipeline/Ingestor.ts +0 -555
  60. package/src/resonance/cli/ingest.ts +0 -41
  61. package/src/resonance/cli/migrate.ts +0 -54
  62. package/src/resonance/config.ts +0 -40
  63. package/src/resonance/daemon.ts +0 -236
  64. package/src/resonance/pipeline/extract.ts +0 -89
  65. package/src/resonance/pipeline/transform_docs.ts +0 -60
  66. package/src/resonance/services/tokenizer.ts +0 -159
  67. package/src/resonance/transform/cda.ts +0 -393
  68. package/src/utils/EnvironmentVerifier.ts +0 -67
  69. package/substack/substack-playbook-1.md +0 -95
  70. package/substack/substack-playbook-2.md +0 -78
  71. package/tasks/ui-investigation.md +0 -26
  72. package/test-db +0 -0
  73. package/test-db-shm +0 -0
  74. package/test-db-wal +0 -0
  75. package/tests/canary/verify_pinch_check.ts +0 -44
  76. package/tests/fixtures/ingest_test.md +0 -12
  77. package/tests/fixtures/ingest_test_boxed.md +0 -13
  78. package/tests/fixtures/safety_test.md +0 -45
  79. package/tests/fixtures/safety_test_boxed.md +0 -49
  80. package/tests/fixtures/tagged_output.md +0 -49
  81. package/tests/fixtures/tagged_test.md +0 -49
  82. package/tests/mcp-server-settings.json +0 -8
  83. package/verify-embedder.ts +0 -54
@@ -0,0 +1,93 @@
1
+ # Current Configuration Status
2
+
3
+ **Last Updated:** 2026-01-06
4
+
5
+ ## Configuration Files (Single Source of Truth)
6
+
7
+ ### 1. AMALFA Core Configuration
8
+ **File:** `amalfa.config.json` (root)
9
+ **Purpose:** Main AMALFA system settings
10
+ **Loaded by:** `src/config/defaults.ts`
11
+
12
+ ```json
13
+ {
14
+ "sources": ["../polyvis/docs", "../polyvis/playbooks"],
15
+ "database": ".amalfa/multi-source-test.db",
16
+ "embeddings": {
17
+ "model": "BAAI/bge-small-en-v1.5",
18
+ "dimensions": 384
19
+ },
20
+ "watch": {
21
+ "enabled": true,
22
+ "debounce": 1000
23
+ },
24
+ "excludePatterns": ["node_modules", ".git", ".amalfa"]
25
+ }
26
+ ```
27
+
28
+ **Status:** ✅ Active, primary config for AMALFA operations
29
+
30
+ ### 2. Resonance Configuration (Legacy)
31
+ **File:** `polyvis.settings.json` (root)
32
+ **Purpose:** Resonance database and pipeline settings
33
+ **Loaded by:** `src/resonance/config.ts`
34
+
35
+ ```json
36
+ {
37
+ "paths": {
38
+ "database": { "resonance": "public/resonance.db" },
39
+ "docs": { ... },
40
+ "sources": {
41
+ "experience": [...],
42
+ "persona": { ... }
43
+ }
44
+ },
45
+ "graph": { "tuning": { ... } },
46
+ "schema": { "version": "1.0.0" }
47
+ }
48
+ ```
49
+
50
+ **Status:** 🔄 Legacy, used by resonance pipeline only
51
+
52
+ ### 3. Beads Issue Tracking
53
+ **File:** `.beads/config.yaml`
54
+ **Purpose:** Beads issue tracking system configuration
55
+ **Loaded by:** Beads CLI (`bd` commands)
56
+
57
+ **Status:** ✅ Active, separate system
58
+
59
+ ## Configuration Strategy
60
+
61
+ ### Current Approach (2026-01-06)
62
+ - **AMALFA** and **Resonance** have separate configs
63
+ - This is **intentional** - they serve different purposes:
64
+ - AMALFA: User-facing MCP server and CLI
65
+ - Resonance: Internal database/pipeline layer
66
+
67
+ ### Future Unification (v1.0+)
68
+ - Consider consolidating if Resonance becomes AMALFA-exclusive
69
+ - Keep separate if Resonance remains standalone library
70
+
71
+ ## Configuration Hierarchy
72
+
73
+ ```
74
+ Project Root
75
+ ├── amalfa.config.json ← AMALFA core (MCP, CLI, daemon)
76
+ ├── polyvis.settings.json ← Resonance (database, pipeline)
77
+ └── .beads/
78
+ └── config.yaml ← Beads (issue tracking)
79
+ ```
80
+
81
+ ## Action Items
82
+
83
+ - [x] Decide: Keep separate, deprecate polyvis.settings.json gradually (See CONFIG_UNIFICATION.md)
84
+ - [x] Document: Which config controls what (See CONFIG_UNIFICATION.md)
85
+ - [ ] Add deprecation warning when polyvis.settings.json is loaded
86
+ - [ ] Migrate graph tuning to amalfa.config.json (optional)
87
+ - [ ] Remove polyvis.settings.json in v2.0
88
+
89
+ ## Notes
90
+
91
+ - No `_current-*` files existed prior to 2026-01-06
92
+ - Configuration is stable but not unified
93
+ - Each system has clear ownership of its config file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amalfa",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
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",
@@ -10,7 +10,7 @@
10
10
  "author": "Peter John Smith <729613+pjsvis@users.noreply.github.com>",
11
11
  "repository": {
12
12
  "type": "git",
13
- "url": "https://github.com/pjsvis/amalfa"
13
+ "url": "git+https://github.com/pjsvis/amalfa.git"
14
14
  },
15
15
  "keywords": [
16
16
  "mcp",
@@ -29,7 +29,7 @@
29
29
  "pkm"
30
30
  ],
31
31
  "bin": {
32
- "amalfa": "./src/cli.ts"
32
+ "amalfa": "src/cli.ts"
33
33
  },
34
34
  "engines": {
35
35
  "bun": "1.3.x",
@@ -48,6 +48,9 @@
48
48
  "start": "bun run src/mcp/index.ts start",
49
49
  "stop": "bun run src/mcp/index.ts stop",
50
50
  "status": "bun run src/mcp/index.ts status",
51
+ "servers": "bun run scripts/cli/servers.ts",
52
+ "validate-config": "bun run scripts/validate-config.ts",
53
+ "release": "bun run scripts/release.ts",
51
54
  "check": "biome check .",
52
55
  "format": "biome format --write ."
53
56
  },
@@ -0,0 +1,38 @@
1
+ {
2
+ "_comment": "âš ī¸ DEPRECATED: This file will be removed in v2.0. Use amalfa.config.json for user-facing settings. See docs/CONFIG_UNIFICATION.md",
3
+ "paths": {
4
+ "database": {
5
+ "resonance": "public/resonance.db"
6
+ },
7
+ "docs": {
8
+ "root": "docs",
9
+ "webdocs": "docs/webdocs",
10
+ "architecture": "docs/architecture",
11
+ "public": "public/docs"
12
+ },
13
+ "sources": {
14
+ "experience": [
15
+ { "path": "debriefs", "name": "Debrief" },
16
+ { "path": "playbooks", "name": "Playbook" },
17
+ { "path": "briefs", "name": "Brief" },
18
+ { "path": "docs", "name": "Docs" }
19
+ ],
20
+ "persona": {
21
+ "lexicon": "scripts/fixtures/conceptual-lexicon-ref-v1.79.json",
22
+ "cda": "scripts/fixtures/cda-ref-v63.json"
23
+ }
24
+ }
25
+ },
26
+ "graph": {
27
+ "tuning": {
28
+ "louvain": {
29
+ "persona": 0.3,
30
+ "experience": 0.25
31
+ }
32
+ }
33
+ },
34
+
35
+ "schema": {
36
+ "version": "1.0.0"
37
+ }
38
+ }
package/src/cli.ts CHANGED
@@ -3,8 +3,10 @@ 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.1";
7
- const DB_PATH = join(process.cwd(), ".amalfa/resonance.db");
6
+ const VERSION = "1.0.2";
7
+
8
+ // Database path loaded from config (lazy loaded per command)
9
+ let DB_PATH: string | null = null;
8
10
 
9
11
  // Parse command line arguments
10
12
  const args = process.argv.slice(2);
@@ -22,6 +24,7 @@ Commands:
22
24
  serve Start MCP server (stdio transport)
23
25
  stats Show database statistics
24
26
  doctor Check installation and configuration
27
+ setup-mcp Generate MCP configuration JSON
25
28
  daemon <action> Manage file watcher (start|stop|status|restart)
26
29
 
27
30
  Options:
@@ -44,10 +47,21 @@ function showVersion() {
44
47
  console.log(`amalfa v${VERSION}`);
45
48
  }
46
49
 
50
+ async function getDbPath(): Promise<string> {
51
+ if (DB_PATH) return DB_PATH;
52
+
53
+ // Load from config
54
+ const { loadConfig } = await import("./config/defaults");
55
+ const config = await loadConfig();
56
+ DB_PATH = join(process.cwd(), config.database);
57
+ return DB_PATH;
58
+ }
59
+
47
60
  async function checkDatabase(): Promise<boolean> {
48
- if (!existsSync(DB_PATH)) {
61
+ const dbPath = await getDbPath();
62
+ if (!existsSync(dbPath)) {
49
63
  console.error(`
50
- ❌ Database not found at: ${DB_PATH}
64
+ ❌ Database not found at: ${dbPath}
51
65
 
52
66
  To initialize AMALFA:
53
67
  1. Create markdown files in ./docs/ (or your preferred location)
@@ -66,8 +80,9 @@ async function cmdServe() {
66
80
  process.exit(1);
67
81
  }
68
82
 
83
+ const dbPath = await getDbPath();
69
84
  console.error("🚀 Starting AMALFA MCP Server...");
70
- console.error(`📊 Database: ${DB_PATH}`);
85
+ console.error(`📊 Database: ${dbPath}`);
71
86
  console.error("");
72
87
 
73
88
  // Run MCP server (it handles stdio transport)
@@ -89,18 +104,19 @@ async function cmdStats() {
89
104
  }
90
105
 
91
106
  // Import database wrapper
107
+ const dbPath = await getDbPath();
92
108
  const { ResonanceDB } = await import("./resonance/db");
93
- const db = new ResonanceDB(DB_PATH);
109
+ const db = new ResonanceDB(dbPath);
94
110
 
95
111
  try {
96
112
  const stats = db.getStats();
97
- const fileSize = statSync(DB_PATH).size;
113
+ const fileSize = statSync(dbPath).size;
98
114
  const fileSizeMB = (fileSize / 1024 / 1024).toFixed(2);
99
115
 
100
- console.log(`
116
+ console.log(`
101
117
  📊 AMALFA Database Statistics
102
118
 
103
- Database: ${DB_PATH}
119
+ Database: ${dbPath}
104
120
  Size: ${fileSizeMB} MB
105
121
 
106
122
  Nodes: ${stats.nodes.toLocaleString()}
@@ -108,7 +124,7 @@ Edges: ${stats.edges.toLocaleString()}
108
124
  Embeddings: ${stats.vectors.toLocaleString()} (384-dim)
109
125
 
110
126
  Source: ./docs (markdown files)
111
- Last modified: ${new Date(statSync(DB_PATH).mtime).toISOString()}
127
+ Last modified: ${new Date(statSync(dbPath).mtime).toISOString()}
112
128
 
113
129
  🔍 To search: Use with Claude Desktop or other MCP client
114
130
  📝 To update: Run 'amalfa daemon start' (coming soon)
@@ -250,6 +266,55 @@ async function cmdDaemon() {
250
266
  });
251
267
  }
252
268
 
269
+ async function cmdSetupMcp() {
270
+ const { resolve } = await import("node:path");
271
+
272
+ const cwd = resolve(process.cwd());
273
+ const mcpScript = resolve(cwd, "src/mcp/index.ts");
274
+
275
+ // Minimal PATH for MCP - only include essential directories
276
+ const bunPath = process.execPath.replace(/\/bun$/, ''); // Directory containing bun
277
+ const minimalPath = [
278
+ bunPath,
279
+ '/usr/local/bin',
280
+ '/usr/bin',
281
+ '/bin',
282
+ '/usr/sbin',
283
+ '/sbin',
284
+ '/opt/homebrew/bin', // Apple Silicon Homebrew
285
+ ].join(':');
286
+
287
+ const config = {
288
+ mcpServers: {
289
+ amalfa: {
290
+ command: "bun",
291
+ args: ["run", mcpScript],
292
+ env: {
293
+ PATH: minimalPath,
294
+ },
295
+ },
296
+ },
297
+ };
298
+
299
+ console.log("\n✅ AMALFA MCP Configuration");
300
+ console.log("=".repeat(60));
301
+ console.log(`📂 Installation: ${cwd}`);
302
+ console.log("=".repeat(60));
303
+ console.log("\n📋 Copy this JSON to your MCP client config:");
304
+ console.log(" Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json");
305
+ console.log(" Warp Preview: MCP settings\n");
306
+ console.log("=".repeat(60));
307
+ console.log();
308
+
309
+ console.log(JSON.stringify(config, null, 2));
310
+
311
+ console.log();
312
+ console.log("=".repeat(60));
313
+ console.log("💡 Tip: If you move this folder, run 'amalfa setup-mcp' again");
314
+ console.log("=".repeat(60));
315
+ console.log();
316
+ }
317
+
253
318
  async function cmdDoctor() {
254
319
  console.log("đŸŠē AMALFA Health Check\n");
255
320
 
@@ -258,12 +323,14 @@ async function cmdDoctor() {
258
323
  // Check Bun runtime
259
324
  console.log("✓ Bun runtime: OK");
260
325
 
261
- // Check database
262
- if (existsSync(DB_PATH)) {
263
- const fileSizeMB = (statSync(DB_PATH).size / 1024 / 1024).toFixed(2);
264
- console.log(`✓ Database found: ${DB_PATH} (${fileSizeMB} MB)`);
326
+ // Check config and database
327
+ const dbPath = await getDbPath();
328
+ if (existsSync(dbPath)) {
329
+ const fileSizeMB = (statSync(dbPath).size / 1024 / 1024).toFixed(2);
330
+ console.log(`✓ Database found: ${dbPath} (${fileSizeMB} MB)`);
265
331
  } else {
266
- console.log(`✗ Database not found: ${DB_PATH}`);
332
+ console.log(`✗ Database not found: ${dbPath}`);
333
+ console.log(` Run: amalfa init`);
267
334
  issues++;
268
335
  }
269
336
 
@@ -276,12 +343,23 @@ async function cmdDoctor() {
276
343
  issues++;
277
344
  }
278
345
 
279
- // Check source directories
280
- const docsDir = join(process.cwd(), "docs");
281
- if (existsSync(docsDir)) {
282
- console.log(`✓ Source directory: ${docsDir}`);
283
- } else {
284
- console.log(`⚠ Source directory not found: ${docsDir} (optional)`);
346
+ // Check source directories from config
347
+ const { loadConfig } = await import("./config/defaults");
348
+ const config = await loadConfig();
349
+ const sources = config.sources || ["./docs"];
350
+ let sourcesFound = 0;
351
+ for (const source of sources) {
352
+ const sourcePath = join(process.cwd(), source);
353
+ if (existsSync(sourcePath)) {
354
+ console.log(`✓ Source directory: ${sourcePath}`);
355
+ sourcesFound++;
356
+ } else {
357
+ console.log(`✗ Source directory not found: ${sourcePath}`);
358
+ issues++;
359
+ }
360
+ }
361
+ if (sourcesFound === 0) {
362
+ console.log(` Configure sources in amalfa.config.json`);
285
363
  }
286
364
 
287
365
  // Check dependencies (FastEmbed)
@@ -338,6 +416,10 @@ async function main() {
338
416
  await cmdDaemon();
339
417
  break;
340
418
 
419
+ case "setup-mcp":
420
+ await cmdSetupMcp();
421
+ break;
422
+
341
423
  case "version":
342
424
  case "--version":
343
425
  case "-v":
@@ -17,6 +17,20 @@ export interface AmalfaConfig {
17
17
  debounce: number;
18
18
  };
19
19
  excludePatterns: string[];
20
+ /** Graph analysis tuning parameters (optional) */
21
+ graph?: {
22
+ tuning?: {
23
+ louvain?: {
24
+ persona?: number;
25
+ experience?: number;
26
+ };
27
+ };
28
+ };
29
+ /** Persona fixture paths (optional, for legacy Resonance features) */
30
+ fixtures?: {
31
+ lexicon?: string;
32
+ cda?: string;
33
+ };
20
34
  }
21
35
 
22
36
  export const DEFAULT_CONFIG: AmalfaConfig = {
@@ -31,6 +45,20 @@ export const DEFAULT_CONFIG: AmalfaConfig = {
31
45
  debounce: 1000,
32
46
  },
33
47
  excludePatterns: ["node_modules", ".git", ".amalfa"],
48
+ // Optional graph tuning (for advanced use)
49
+ graph: {
50
+ tuning: {
51
+ louvain: {
52
+ persona: 0.3,
53
+ experience: 0.25,
54
+ },
55
+ },
56
+ },
57
+ // Optional fixtures (for legacy Resonance features)
58
+ fixtures: {
59
+ lexicon: "scripts/fixtures/conceptual-lexicon-ref-v1.79.json",
60
+ cda: "scripts/fixtures/cda-ref-v63.json",
61
+ },
34
62
  };
35
63
 
36
64
  /**
@@ -58,19 +86,31 @@ export async function loadConfig(): Promise<AmalfaConfig> {
58
86
  userConfig = imported.default || imported;
59
87
  }
60
88
 
61
- // Merge with defaults
62
- const merged = {
63
- ...DEFAULT_CONFIG,
64
- ...userConfig,
65
- embeddings: {
66
- ...DEFAULT_CONFIG.embeddings,
67
- ...(userConfig.embeddings || {}),
68
- },
69
- watch: {
70
- ...DEFAULT_CONFIG.watch,
71
- ...(userConfig.watch || {}),
89
+ // Merge with defaults
90
+ const merged = {
91
+ ...DEFAULT_CONFIG,
92
+ ...userConfig,
93
+ embeddings: {
94
+ ...DEFAULT_CONFIG.embeddings,
95
+ ...(userConfig.embeddings || {}),
96
+ },
97
+ watch: {
98
+ ...DEFAULT_CONFIG.watch,
99
+ ...(userConfig.watch || {}),
100
+ },
101
+ graph: {
102
+ ...DEFAULT_CONFIG.graph,
103
+ ...(userConfig.graph || {}),
104
+ tuning: {
105
+ ...(DEFAULT_CONFIG.graph?.tuning || {}),
106
+ ...(userConfig.graph?.tuning || {}),
72
107
  },
73
- };
108
+ },
109
+ fixtures: {
110
+ ...DEFAULT_CONFIG.fixtures,
111
+ ...(userConfig.fixtures || {}),
112
+ },
113
+ };
74
114
 
75
115
  // Normalize: Convert legacy 'source' to 'sources' array
76
116
  if (merged.source && !merged.sources) {
@@ -1,7 +1,5 @@
1
1
  import { Database } from "bun:sqlite";
2
- import { join } from "node:path";
3
2
  import { EmbeddingModel, FlagEmbedding } from "fastembed";
4
- import settings from "@/polyvis.settings.json";
5
3
 
6
4
  // Types
7
5
  export interface SearchResult {
@@ -89,9 +87,7 @@ export class VectorEngine {
89
87
  console.warn(
90
88
  "âš ī¸ DEPRECATED: VectorEngine string path constructor bypasses DatabaseFactory. Pass Database object instead. Will be removed in v2.0.",
91
89
  );
92
- const path =
93
- dbOrPath || join(process.cwd(), settings.paths.database.resonance);
94
- this.db = new Database(path);
90
+ this.db = new Database(dbOrPath);
95
91
 
96
92
  // Apply Safeguards if we created it
97
93
  this.db.run("PRAGMA journal_mode = WAL;");
@@ -192,22 +188,35 @@ export class VectorEngine {
192
188
  const topK = scored.sort((a, b) => b.score - a.score).slice(0, limit);
193
189
 
194
190
  // 5. Hydrate Content (for top K only)
191
+ // Note: Hollow Nodes have content=NULL, use meta.source to read from filesystem if needed
195
192
  const results: SearchResult[] = [];
196
193
  const contentStmt = this.db.prepare(
197
- "SELECT title, content FROM nodes WHERE id = ?",
194
+ "SELECT title, content, meta FROM nodes WHERE id = ?",
198
195
  );
199
196
 
200
197
  for (const item of topK) {
201
198
  const row = contentStmt.get(item.id) as {
202
199
  title: string;
203
- content: string;
200
+ content: string | null;
201
+ meta: string | null;
204
202
  };
205
203
  if (row) {
204
+ // For hollow nodes, extract a preview from title or meta
205
+ let content = row.content || "";
206
+ if (!content && row.meta) {
207
+ try {
208
+ const meta = JSON.parse(row.meta);
209
+ // Provide source path as content placeholder for hollow nodes
210
+ content = `[Hollow Node: ${meta.source || "no source"}]`;
211
+ } catch {
212
+ content = "[Hollow Node: parse error]";
213
+ }
214
+ }
206
215
  results.push({
207
216
  id: item.id,
208
217
  score: item.score,
209
- title: row.title, // Add title
210
- content: row.content,
218
+ title: row.title,
219
+ content: content,
211
220
  });
212
221
  }
213
222
  }
package/src/mcp/index.ts CHANGED
@@ -8,9 +8,10 @@ import {
8
8
  ListToolsRequestSchema,
9
9
  ReadResourceRequestSchema,
10
10
  } from "@modelcontextprotocol/sdk/types.js";
11
+ import { loadConfig } from "@src/config/defaults";
11
12
  import { VectorEngine } from "@src/core/VectorEngine";
12
13
  import { ResonanceDB } from "@src/resonance/db";
13
- import { EnvironmentVerifier } from "../utils/EnvironmentVerifier";
14
+ import { DaemonManager } from "../utils/DaemonManager";
14
15
  import { getLogger } from "../utils/Logger";
15
16
  import { ServiceLifecycle } from "../utils/ServiceLifecycle";
16
17
 
@@ -29,18 +30,68 @@ const lifecycle = new ServiceLifecycle({
29
30
 
30
31
  // --- Server Logic ---
31
32
 
33
+ // Database path from config (loaded once at startup)
34
+ let DB_PATH: string;
35
+
32
36
  // Helper function to create fresh database connection per request
33
37
  function createConnection() {
34
- const dbPath = join(import.meta.dir, "../../.amalfa/resonance.db");
35
- const db = new ResonanceDB(dbPath);
38
+ const db = new ResonanceDB(DB_PATH);
36
39
  const vectorEngine = new VectorEngine(db.getRawDb());
37
40
  return { db, vectorEngine };
38
41
  }
39
42
 
40
43
  async function runServer() {
41
- // 0. Verify Environment
42
- // TODO: Update EnvironmentVerifier for AMALFA paths (not PolyVis)
43
- // await EnvironmentVerifier.verifyOrExit();
44
+ // 0. Load configuration
45
+ const config = await loadConfig();
46
+ DB_PATH = join(process.cwd(), config.database);
47
+ log.info({ database: DB_PATH }, "📁 Database path loaded from config");
48
+
49
+ // 1. Start daemons if needed
50
+ const daemonManager = new DaemonManager();
51
+
52
+ // Check file watcher status
53
+ if (config.watch?.enabled) {
54
+ const watcherStatus = await daemonManager.checkFileWatcher();
55
+ if (!watcherStatus.running) {
56
+ log.info("🔄 Starting file watcher daemon...");
57
+ try {
58
+ await daemonManager.startFileWatcher();
59
+ log.info("✅ File watcher daemon started");
60
+ } catch (e) {
61
+ log.warn(
62
+ { err: e },
63
+ "âš ī¸ Failed to start file watcher, continuing without it",
64
+ );
65
+ }
66
+ } else {
67
+ log.info(
68
+ { pid: watcherStatus.pid },
69
+ "✅ File watcher daemon already running",
70
+ );
71
+ }
72
+ } else {
73
+ log.info("â„šī¸ File watching disabled in config");
74
+ }
75
+
76
+ // Start vector daemon for fast embeddings
77
+ const vectorStatus = await daemonManager.checkVectorDaemon();
78
+ if (!vectorStatus.running) {
79
+ log.info("🔄 Starting vector daemon...");
80
+ try {
81
+ await daemonManager.startVectorDaemon();
82
+ log.info("✅ Vector daemon started");
83
+ } catch (e) {
84
+ log.warn(
85
+ { err: e },
86
+ "âš ī¸ Failed to start vector daemon, searches will be slower",
87
+ );
88
+ }
89
+ } else {
90
+ log.info(
91
+ { pid: vectorStatus.pid, port: vectorStatus.port },
92
+ "✅ Vector daemon already running",
93
+ );
94
+ }
44
95
 
45
96
  log.info("🚀 AMALFA MCP Server Initializing...");
46
97
 
@@ -138,10 +189,14 @@ async function runServer() {
138
189
  try {
139
190
  const vectorResults = await vectorEngine.search(query, limit);
140
191
  for (const r of vectorResults) {
192
+ // Handle hollow nodes: content may be placeholder text
193
+ const preview = r.content
194
+ ? r.content.slice(0, 200).replace(/\n/g, " ")
195
+ : "[No preview available]";
141
196
  candidates.set(r.id, {
142
197
  id: r.id,
143
198
  score: r.score,
144
- preview: r.content.slice(0, 200).replace(/\n/g, " "),
199
+ preview: preview,
145
200
  source: "vector",
146
201
  });
147
202
  }
@@ -1,5 +1,4 @@
1
1
  import { Database } from "bun:sqlite";
2
- import settings from "@/polyvis.settings.json";
3
2
 
4
3
  /**
5
4
  * 🏭 DATABASE FACTORY (The Enforcer)
@@ -14,10 +13,10 @@ import settings from "@/polyvis.settings.json";
14
13
  export const DatabaseFactory = {
15
14
  /**
16
15
  * Connects specifically to the main Resonance Graph database.
17
- * Uses path from `polyvis.settings.json`.
16
+ * @deprecated Use connect() with explicit path from config instead.
18
17
  */
19
- connectToResonance(options: { readonly?: boolean } = {}): Database {
20
- return DatabaseFactory.connect(settings.paths.database.resonance, options);
18
+ connectToResonance(dbPath: string = ".amalfa/resonance.db", options: { readonly?: boolean } = {}): Database {
19
+ return DatabaseFactory.connect(dbPath, options);
21
20
  },
22
21
  /**
23
22
  * Creates a fully configured, concurrent-safe SQLite connection.
@@ -1,6 +1,5 @@
1
1
  import type { Database } from "bun:sqlite";
2
2
  import { getLogger } from "@src/utils/Logger";
3
- import settings from "@/polyvis.settings.json";
4
3
  import { DatabaseFactory } from "./DatabaseFactory";
5
4
  import { CURRENT_SCHEMA_VERSION, MIGRATIONS } from "./schema";
6
5
 
@@ -25,10 +24,11 @@ export class ResonanceDB {
25
24
  private dbPath: string;
26
25
 
27
26
  /**
28
- * Factory method to load the default Resonance Graph based on settings.
27
+ * Factory method to load the default Resonance Graph.
28
+ * @deprecated Use constructor with explicit path from config instead.
29
29
  */
30
- static init(): ResonanceDB {
31
- return new ResonanceDB(settings.paths.database.resonance);
30
+ static init(dbPath: string = ".amalfa/resonance.db"): ResonanceDB {
31
+ return new ResonanceDB(dbPath);
32
32
  }
33
33
 
34
34
  /**