fathom-mcp 0.5.20 → 0.6.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fathom-mcp",
3
- "version": "0.5.20",
3
+ "version": "0.6.0",
4
4
  "description": "MCP server for Fathom — vault operations, search, rooms, and cross-workspace communication",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,6 +11,7 @@
11
11
  "src/",
12
12
  "scripts/*.sh",
13
13
  "scripts/*.py",
14
+ "scripts/*.js",
14
15
  "fathom-agents.md",
15
16
  "README.md",
16
17
  "CHANGELOG.md",
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Vault frontmatter lint hook for Claude Code PostToolUse (Write/Edit).
5
+ *
6
+ * Reads the tool result from stdin (JSON), checks if the file is inside a
7
+ * vault/ directory and ends in .md. If so, validates frontmatter.
8
+ * Exits 0 on pass, 1 on failure (with error message on stderr).
9
+ *
10
+ * Environment: CLAUDE_TOOL_NAME, CLAUDE_FILE_PATH are set by Claude Code hooks.
11
+ */
12
+
13
+ import fs from "fs";
14
+ import path from "path";
15
+ import { parseFrontmatter, validateFrontmatter } from "../src/frontmatter.js";
16
+
17
+ const toolName = process.env.CLAUDE_TOOL_NAME || "";
18
+ const filePath = process.env.CLAUDE_FILE_PATH || "";
19
+
20
+ // Only check Write and Edit tools
21
+ if (toolName !== "Write" && toolName !== "Edit") {
22
+ process.exit(0);
23
+ }
24
+
25
+ // Only check .md files inside a vault/ directory
26
+ if (!filePath || !filePath.endsWith(".md")) {
27
+ process.exit(0);
28
+ }
29
+
30
+ // Check if the file is inside a vault/ directory
31
+ const parts = filePath.split(path.sep);
32
+ if (!parts.includes("vault")) {
33
+ process.exit(0);
34
+ }
35
+
36
+ // Read the file and validate
37
+ try {
38
+ const content = fs.readFileSync(filePath, "utf-8");
39
+
40
+ // Only validate if file has frontmatter
41
+ if (!content.startsWith("---")) {
42
+ process.exit(0);
43
+ }
44
+
45
+ const { fm } = parseFrontmatter(content);
46
+ if (Object.keys(fm).length === 0) {
47
+ process.exit(0);
48
+ }
49
+
50
+ const errors = validateFrontmatter(fm);
51
+ if (errors.length > 0) {
52
+ process.stderr.write(`Vault frontmatter validation failed for ${path.basename(filePath)}:\n`);
53
+ for (const err of errors) {
54
+ process.stderr.write(` - ${err}\n`);
55
+ }
56
+ process.exit(1);
57
+ }
58
+ } catch (e) {
59
+ // File read errors are non-fatal for the hook
60
+ if (e.code !== "ENOENT") {
61
+ process.stderr.write(`Frontmatter lint error: ${e.message}\n`);
62
+ }
63
+ }
64
+
65
+ process.exit(0);
package/src/cli.js CHANGED
@@ -8,13 +8,7 @@
8
8
  * npx fathom-mcp init — Interactive setup wizard
9
9
  * npx fathom-mcp status — Check server connection + workspace status
10
10
  * npx fathom-mcp update — Update hook scripts + version file
11
- * npx fathom-mcp list — List all agents + running status
12
- * npx fathom-mcp start [name] — Start agent by name or legacy cwd-walk
13
- * npx fathom-mcp stop <name> — Stop an agent
14
- * npx fathom-mcp restart <name> — Restart an agent
15
- * npx fathom-mcp add [name] — Add agent to registry (reads .fathom.json defaults)
16
- * npx fathom-mcp remove <name>— Remove agent from registry
17
- * npx fathom-mcp config <name>— Print agent config JSON
11
+ * npx fathom-mcp list — List workspaces + status (from server)
18
12
  */
19
13
 
20
14
  import fs from "fs";
@@ -25,17 +19,6 @@ import { fileURLToPath } from "url";
25
19
 
26
20
  import { findConfigFile, resolveConfig, writeConfig } from "./config.js";
27
21
  import { createClient } from "./server-client.js";
28
- import {
29
- listAgents,
30
- getAgent,
31
- addAgent as registryAddAgent,
32
- removeAgent as registryRemoveAgent,
33
- isAgentRunning,
34
- startAgent,
35
- stopAgent,
36
- defaultCommand,
37
- buildEntryFromConfig,
38
- } from "./agents.js";
39
22
 
40
23
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
41
24
  const SCRIPTS_DIR = path.join(__dirname, "..", "scripts");
@@ -114,6 +97,33 @@ function appendToGitignore(dir, patterns) {
114
97
  * Works for both Claude Code and Gemini CLI (same JSON structure).
115
98
  * Returns true if a new hook was added, false if already present.
116
99
  */
100
+ /**
101
+ * MCP tool permissions upserted into .claude/settings.local.json during init.
102
+ * Only fathom-vault and memento — the tools init itself provides.
103
+ */
104
+ const INIT_PERMISSIONS = [
105
+ "mcp__fathom-vault__*",
106
+ "mcp__memento__*",
107
+ ];
108
+
109
+ /**
110
+ * Upsert permissions into settings.permissions.allow.
111
+ * Returns true if any new entries were added.
112
+ */
113
+ function ensurePermissions(settings, perms = INIT_PERMISSIONS) {
114
+ if (!settings.permissions) settings.permissions = {};
115
+ if (!Array.isArray(settings.permissions.allow)) settings.permissions.allow = [];
116
+ const existing = new Set(settings.permissions.allow);
117
+ let changed = false;
118
+ for (const perm of perms) {
119
+ if (!existing.has(perm)) {
120
+ settings.permissions.allow.push(perm);
121
+ changed = true;
122
+ }
123
+ }
124
+ return changed;
125
+ }
126
+
117
127
  function ensureHook(settings, eventName, command, timeout) {
118
128
  const existing = settings.hooks?.[eventName] || [];
119
129
  const alreadyRegistered = existing.some((entry) =>
@@ -204,6 +214,7 @@ function parseFlags(argv) {
204
214
  server: null,
205
215
  workspace: null,
206
216
  agent: null,
217
+ vaultMode: null,
207
218
  };
208
219
  for (let i = 0; i < argv.length; i++) {
209
220
  if (argv[i] === "-y" || argv[i] === "--yes") {
@@ -220,6 +231,9 @@ function parseFlags(argv) {
220
231
  } else if (argv[i] === "--agent" && argv[i + 1]) {
221
232
  flags.agent = argv[i + 1];
222
233
  i++;
234
+ } else if (argv[i] === "--vault-mode" && argv[i + 1]) {
235
+ flags.vaultMode = argv[i + 1];
236
+ i++;
223
237
  }
224
238
  }
225
239
  // Check environment variables as fallback
@@ -295,6 +309,7 @@ async function runInit(flags = {}) {
295
309
  server: flagServer = null,
296
310
  workspace: flagWorkspace = null,
297
311
  agent: flagAgent = null,
312
+ vaultMode: flagVaultMode = null,
298
313
  } = flags;
299
314
  const cwd = process.cwd();
300
315
 
@@ -346,7 +361,8 @@ async function runInit(flags = {}) {
346
361
 
347
362
  let selectedAgents;
348
363
  if (nonInteractive) {
349
- if (flagAgent) {
364
+ // In non-interactive mode, always default to claude-code unless explicitly overridden
365
+ if (flagAgent && flagAgent !== "claude-code") {
350
366
  if (!AGENTS[flagAgent]) {
351
367
  const valid = Object.keys(AGENTS).join(", ");
352
368
  console.error(` Error: unknown agent "${flagAgent}". Valid agents: ${valid}`);
@@ -355,8 +371,8 @@ async function runInit(flags = {}) {
355
371
  selectedAgents = [flagAgent];
356
372
  console.log(` Agent: ${AGENTS[flagAgent].name} (--agent flag)`);
357
373
  } else {
358
- selectedAgents = detected.length > 0 ? [detected[0]] : ["claude-code"];
359
- console.log(` Agent: ${AGENTS[selectedAgents[0]].name} (auto-detected)`);
374
+ selectedAgents = ["claude-code"];
375
+ console.log(` Agent: Claude Code`);
360
376
  }
361
377
  } else {
362
378
  console.log("\n Detected agents:");
@@ -430,24 +446,37 @@ async function runInit(flags = {}) {
430
446
 
431
447
  // 8. Vault mode selection
432
448
  let vaultMode;
449
+ const validVaultModes = ["synced", "local", "none"];
433
450
  if (nonInteractive) {
434
451
  if (!serverReachable && flagServer) {
435
452
  console.error(`\n Error: Server at ${serverUrl} is not reachable.`);
436
453
  console.error(" Fix the URL or start the server, then re-run init.");
437
454
  process.exit(1);
438
455
  }
439
- vaultMode = serverReachable ? "hosted" : "local";
440
- console.log(` Vault mode: ${vaultMode} (auto-selected)`);
456
+ if (flagVaultMode) {
457
+ if (!validVaultModes.includes(flagVaultMode)) {
458
+ console.error(` Error: unknown vault mode "${flagVaultMode}". Valid modes: ${validVaultModes.join(", ")}`);
459
+ process.exit(1);
460
+ }
461
+ if (flagVaultMode === "synced" && !serverReachable) {
462
+ console.error(` Error: vault mode "${flagVaultMode}" requires a reachable server.`);
463
+ process.exit(1);
464
+ }
465
+ vaultMode = flagVaultMode;
466
+ console.log(` Vault mode: ${vaultMode} (--vault-mode flag)`);
467
+ } else {
468
+ vaultMode = serverReachable ? "synced" : "local";
469
+ console.log(` Vault mode: ${vaultMode} (auto-selected)`);
470
+ }
441
471
  } else {
442
472
  if (serverReachable) {
443
473
  console.log("\n Vault mode:");
444
- console.log(" 1. Hosted (default) — vault stored on server, accessible everywhere");
445
- console.log(" 2. Syncedlocal vault + server mirror (local is source of truth)");
446
- console.log(" 3. Local — vault on disk only, not visible to server");
447
- console.log(" 4. None — no vault, coordination features only");
474
+ console.log(" 1. Synced (default) — local vault + server indexing");
475
+ console.log(" 2. Local — vault on disk only, not visible to server");
476
+ console.log(" 3. Noneno vault, coordination features only");
448
477
  const modeChoice = await ask(rl, "\n Select mode", "1");
449
- const modeMap = { "1": "hosted", "2": "synced", "3": "local", "4": "none" };
450
- vaultMode = modeMap[modeChoice] || "hosted";
478
+ const modeMap = { "1": "synced", "2": "local", "3": "none" };
479
+ vaultMode = modeMap[modeChoice] || "synced";
451
480
  } else {
452
481
  console.log("\n Vault mode (server not available — hosted/synced require server):");
453
482
  console.log(" 1. Local (default) — vault on disk only");
@@ -547,15 +576,18 @@ async function runInit(flags = {}) {
547
576
  const precompactCmd = "bash ~/.config/fathom-mcp/scripts/fathom-precompact.sh";
548
577
 
549
578
  // Claude Code hooks
579
+ const lintCmd = "node ~/.config/fathom-mcp/scripts/vault-frontmatter-lint.js";
550
580
  if (hasClaude) {
551
581
  const settingsPath = path.join(cwd, ".claude", "settings.local.json");
552
582
  const settings = readJsonFile(settingsPath) || {};
553
- let changed = ensureHook(settings, "SessionStart", sessionStartCmd, 10000);
583
+ let changed = ensurePermissions(settings);
584
+ changed = ensureHook(settings, "SessionStart", sessionStartCmd, 10000) || changed;
554
585
  if (enableRecallHook) changed = ensureHook(settings, "UserPromptSubmit", recallCmd, 10000) || changed;
555
586
  if (enablePrecompactHook) changed = ensureHook(settings, "PreCompact", precompactCmd, 30000) || changed;
587
+ changed = ensureHook(settings, "PostToolUse", lintCmd, 5000) || changed;
556
588
  if (changed) {
557
589
  writeJsonFile(settingsPath, settings);
558
- console.log(" ✓ .claude/settings.local.json (hooks)");
590
+ console.log(" ✓ .claude/settings.local.json (permissions + hooks)");
559
591
  }
560
592
  }
561
593
 
@@ -609,11 +641,6 @@ async function runInit(flags = {}) {
609
641
  }
610
642
  }
611
643
 
612
- // Register in CLI agent registry (for ls/start/stop)
613
- const entry = buildEntryFromConfig(cwd, configData);
614
- registryAddAgent(workspace, entry);
615
- console.log(` ✓ Registered agent "${workspace}" in CLI registry`);
616
-
617
644
  // Context-aware next steps
618
645
  console.log(`\n Done! Fathom MCP is configured for workspace "${workspace}".`);
619
646
  console.log(` Vault mode: ${vaultMode}`);
@@ -629,11 +656,8 @@ async function runInit(flags = {}) {
629
656
 
630
657
  const stepNum = serverReachable ? 1 : 2;
631
658
  switch (vaultMode) {
632
- case "hosted":
633
- console.log(` ${stepNum}. Your vault is stored on the server. Start writing!`);
634
- break;
635
659
  case "synced":
636
- console.log(` ${stepNum}. Local vault syncs to server. Files in ./${vault}/ are the source of truth.`);
660
+ console.log(` ${stepNum}. Local vault indexed by server. Files in ./${vault}/ are the source of truth.`);
637
661
  break;
638
662
  case "local":
639
663
  console.log(` ${stepNum}. Local vault only. Server can't search or peek into it.`);
@@ -653,6 +677,7 @@ async function runInit(flags = {}) {
653
677
  if (apiKey) parts.push(`--api-key "${apiKey}"`);
654
678
  if (serverUrl && serverUrl !== "http://localhost:4243") parts.push(`--server ${serverUrl}`);
655
679
  parts.push(`--workspace ${workspace}`);
680
+ parts.push(`--vault-mode ${vaultMode}`);
656
681
  parts.push(`--agent ${selectedAgents[0]}`);
657
682
  console.log(`\n Non-interactive equivalent:\n ${parts.join(" ")}\n`);
658
683
  }
@@ -809,7 +834,9 @@ async function runUpdate() {
809
834
  if (hasClaude) {
810
835
  const settingsPath = path.join(projectDir, ".claude", "settings.local.json");
811
836
  const settings = readJsonFile(settingsPath) || {};
812
- if (ensureHook(settings, "SessionStart", sessionStartCmd, 10000)) {
837
+ let changed = ensurePermissions(settings);
838
+ changed = ensureHook(settings, "SessionStart", sessionStartCmd, 10000) || changed;
839
+ if (changed) {
813
840
  writeJsonFile(settingsPath, settings);
814
841
  registeredHooks.push("Claude Code → .claude/settings.local.json");
815
842
  }
@@ -846,70 +873,56 @@ async function runUpdate() {
846
873
  console.log("\n Restart your agent session to pick up changes.\n");
847
874
  }
848
875
 
849
- // --- Start command -----------------------------------------------------------
850
-
851
- function runStart(argv) {
852
- // Check if first non-flag arg matches a registry entry
853
- const firstArg = argv.find((a) => !a.startsWith("-"));
854
- if (firstArg) {
855
- const entry = getAgent(firstArg);
856
- if (entry) {
857
- const result = startAgent(firstArg, entry);
858
- console.log(` ${result.message}`);
859
- process.exit(result.ok ? 0 : 1);
860
- return;
861
- }
862
- }
863
-
864
- // Legacy fallback: delegate to fathom-start.sh
865
- const found = findConfigFile(process.cwd());
866
- const projectDir = found?.dir || process.cwd();
876
+ // --- List command (via server API) --------------------------------------------
867
877
 
868
- const centralScript = path.join(process.env.HOME, ".config", "fathom-mcp", "scripts", "fathom-start.sh");
869
- const packageScript = path.join(SCRIPTS_DIR, "fathom-start.sh");
870
- const script = fs.existsSync(centralScript) ? centralScript : packageScript;
878
+ async function runList() {
879
+ const config = resolveConfig();
880
+ const client = createClient(config);
871
881
 
872
- if (!fs.existsSync(script)) {
873
- console.error(" Error: fathom-start.sh not found. Run `npx fathom-mcp update` first.");
882
+ const isUp = await client.healthCheck();
883
+ if (!isUp) {
884
+ console.error(`\n Error: Server not reachable at ${config.server}`);
885
+ console.error(" Start the server or check --server URL.\n");
874
886
  process.exit(1);
875
887
  }
876
888
 
877
- try {
878
- execFileSync("bash", [script, ...argv], {
879
- cwd: projectDir,
880
- stdio: "inherit",
881
- });
882
- } catch (e) {
883
- process.exit(e.status || 1);
889
+ const wsResult = await client.listWorkspaces();
890
+ if (wsResult.error) {
891
+ console.error(`\n Error: ${wsResult.error}\n`);
892
+ process.exit(1);
884
893
  }
885
- }
886
894
 
887
- // --- List command -------------------------------------------------------------
888
-
889
- function runList() {
890
- const agents = listAgents();
891
- const names = Object.keys(agents);
895
+ const profiles = wsResult.profiles || {};
896
+ const names = Object.keys(profiles);
892
897
 
893
898
  if (names.length === 0) {
894
- console.log("\n No agents registered. Run `fathom-mcp add` to register one.\n");
899
+ console.log("\n No workspaces registered on server.\n");
895
900
  return;
896
901
  }
897
902
 
898
- // Header
899
- const cols = { name: 16, type: 13, status: 10 };
903
+ const cols = { name: 20, type: 10, status: 12 };
900
904
  console.log(
901
905
  "\n " +
902
- "NAME".padEnd(cols.name) +
906
+ "WORKSPACE".padEnd(cols.name) +
903
907
  "TYPE".padEnd(cols.type) +
904
908
  "STATUS".padEnd(cols.status) +
905
- "DIR",
909
+ "PATH",
906
910
  );
907
911
 
908
912
  for (const name of names) {
909
- const entry = agents[name];
910
- const type = entry.agentType || "claude-code";
911
- const status = entry.ssh ? "[ssh]" : isAgentRunning(name, entry);
912
- const dir = entry.projectDir.replace(process.env.HOME, "~");
913
+ const p = profiles[name];
914
+ const type = p.type || "local";
915
+ let status;
916
+ if (type === "human") {
917
+ status = "human";
918
+ } else if (p.process && p.connected) {
919
+ status = "running";
920
+ } else if (p.process || p.connected) {
921
+ status = "partial";
922
+ } else {
923
+ status = "stopped";
924
+ }
925
+ const dir = (p.path || "").replace(process.env.HOME, "~");
913
926
 
914
927
  console.log(
915
928
  " " +
@@ -922,134 +935,6 @@ function runList() {
922
935
  console.log();
923
936
  }
924
937
 
925
- // --- Stop command ------------------------------------------------------------
926
-
927
- function runStop(name) {
928
- if (!name) {
929
- console.error(" Usage: fathom-mcp stop <name>");
930
- process.exit(1);
931
- }
932
- const entry = getAgent(name);
933
- if (!entry) {
934
- console.error(` Error: No agent "${name}" in registry. Run \`fathom-mcp list\` to see agents.`);
935
- process.exit(1);
936
- }
937
- const result = stopAgent(name, entry);
938
- console.log(` ${result.message}`);
939
- process.exit(result.ok ? 0 : 1);
940
- }
941
-
942
- // --- Restart command ---------------------------------------------------------
943
-
944
- function runRestart(name) {
945
- if (!name) {
946
- console.error(" Usage: fathom-mcp restart <name>");
947
- process.exit(1);
948
- }
949
- const entry = getAgent(name);
950
- if (!entry) {
951
- console.error(` Error: No agent "${name}" in registry. Run \`fathom-mcp list\` to see agents.`);
952
- process.exit(1);
953
- }
954
-
955
- const stopResult = stopAgent(name, entry);
956
- if (stopResult.ok) {
957
- console.log(` ${stopResult.message}`);
958
- }
959
-
960
- // Brief pause between stop and start
961
- try { execFileSync("sleep", ["1"], { stdio: "pipe" }); } catch { /* */ }
962
-
963
- const startResult = startAgent(name, entry);
964
- console.log(` ${startResult.message}`);
965
- process.exit(startResult.ok ? 0 : 1);
966
- }
967
-
968
- // --- Add command -------------------------------------------------------------
969
-
970
- async function runAdd(argv) {
971
- const flags = parseFlags(argv);
972
- const nameArg = argv.find((a) => !a.startsWith("-"));
973
- const cwd = process.cwd();
974
-
975
- // Try to read .fathom.json from cwd for defaults
976
- const found = findConfigFile(cwd);
977
- const fathomConfig = found?.config || {};
978
- const projectDir = found?.dir || cwd;
979
-
980
- const defaults = buildEntryFromConfig(projectDir, fathomConfig);
981
- const defaultName = fathomConfig.workspace || path.basename(projectDir);
982
-
983
- if (flags.nonInteractive) {
984
- const name = nameArg || defaultName;
985
- registryAddAgent(name, defaults);
986
- console.log(` ✓ Added agent "${name}" to registry.`);
987
- return;
988
- }
989
-
990
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
991
-
992
- const name = await ask(rl, " Agent name", nameArg || defaultName);
993
- const agentProjectDir = await ask(rl, " Project directory", defaults.projectDir);
994
- const agentType = await ask(rl, " Agent type (claude-code|gemini|manual)", defaults.agentType);
995
- const command = await ask(rl, " Command", defaultCommand(agentType));
996
- const server = await ask(rl, " Server URL", defaults.server);
997
- const apiKey = await ask(rl, " API key", defaults.apiKey);
998
- const vault = await ask(rl, " Vault subdirectory", defaults.vault || "vault");
999
- const vaultMode = await ask(rl, " Vault mode (hosted|synced|local|none)", defaults.vaultMode);
1000
- const description = await ask(rl, " Description", defaults.description);
1001
-
1002
- rl.close();
1003
-
1004
- const entry = {
1005
- projectDir: path.resolve(agentProjectDir),
1006
- agentType,
1007
- command,
1008
- server,
1009
- apiKey,
1010
- vault,
1011
- vaultMode,
1012
- description,
1013
- hooks: defaults.hooks || {},
1014
- ssh: null,
1015
- env: {},
1016
- };
1017
-
1018
- registryAddAgent(name, entry);
1019
- console.log(`\n ✓ Added agent "${name}" to registry.`);
1020
- }
1021
-
1022
- // --- Remove command ----------------------------------------------------------
1023
-
1024
- function runRemove(name) {
1025
- if (!name) {
1026
- console.error(" Usage: fathom-mcp remove <name>");
1027
- process.exit(1);
1028
- }
1029
- const removed = registryRemoveAgent(name);
1030
- if (removed) {
1031
- console.log(` ✓ Removed agent "${name}" from registry.`);
1032
- } else {
1033
- console.error(` Error: No agent "${name}" in registry.`);
1034
- process.exit(1);
1035
- }
1036
- }
1037
-
1038
- // --- Config command ----------------------------------------------------------
1039
-
1040
- function runConfigCmd(name) {
1041
- if (!name) {
1042
- console.error(" Usage: fathom-mcp config <name>");
1043
- process.exit(1);
1044
- }
1045
- const entry = getAgent(name);
1046
- if (!entry) {
1047
- console.error(` Error: No agent "${name}" in registry.`);
1048
- process.exit(1);
1049
- }
1050
- console.log(JSON.stringify({ [name]: entry }, null, 2));
1051
- }
1052
-
1053
938
  // --- Main --------------------------------------------------------------------
1054
939
 
1055
940
  // Guard: only run CLI when this module is the entry point (not when imported by tests)
@@ -1071,20 +956,8 @@ if (isMain) {
1071
956
  asyncHandler(runStatus);
1072
957
  } else if (command === "update") {
1073
958
  asyncHandler(runUpdate);
1074
- } else if (command === "start") {
1075
- runStart(process.argv.slice(3));
1076
959
  } else if (command === "list" || command === "ls") {
1077
- runList();
1078
- } else if (command === "stop") {
1079
- runStop(process.argv[3]);
1080
- } else if (command === "restart") {
1081
- runRestart(process.argv[3]);
1082
- } else if (command === "add") {
1083
- asyncHandler(() => runAdd(process.argv.slice(3)));
1084
- } else if (command === "remove" || command === "rm") {
1085
- runRemove(process.argv[3]);
1086
- } else if (command === "config") {
1087
- runConfigCmd(process.argv[3]);
960
+ asyncHandler(runList);
1088
961
  } else if (!command || command === "serve") {
1089
962
  import("./index.js");
1090
963
  } else {
@@ -1093,17 +966,10 @@ if (isMain) {
1093
966
 
1094
967
  fathom-mcp Start MCP server (stdio)
1095
968
  fathom-mcp serve Same as above
1096
- fathom-mcp init [-y --api-key KEY --agent AGENT] Interactive/non-interactive setup
969
+ fathom-mcp init [-y --api-key KEY --vault-mode MODE --agent AGENT] Interactive/non-interactive setup
1097
970
  fathom-mcp status Check connection status
1098
971
  fathom-mcp update Update hooks + version
1099
-
1100
- fathom-mcp list List all agents + status
1101
- fathom-mcp start [name] Start agent (by name or legacy cwd)
1102
- fathom-mcp stop <name> Stop agent
1103
- fathom-mcp restart <name> Stop + start agent
1104
- fathom-mcp add [name] Add agent to registry
1105
- fathom-mcp remove <name> Remove from registry
1106
- fathom-mcp config <name> Print agent config JSON`);
972
+ fathom-mcp list List workspaces + status (from server)`);
1107
973
  process.exit(1);
1108
974
  }
1109
975
  }
package/src/config.js CHANGED
@@ -4,17 +4,15 @@
4
4
  * Precedence (highest wins):
5
5
  * 1. Environment variables (FATHOM_SERVER_URL, FATHOM_API_KEY, FATHOM_WORKSPACE, FATHOM_VAULT_DIR)
6
6
  * 2. .fathom.json (walked up from cwd to filesystem root)
7
- * 3. Central registry (~/.config/fathom-mcp/agents.json, matched by projectDir)
8
- * 4. Built-in defaults
7
+ * 3. Built-in defaults
9
8
  */
10
9
 
11
10
  import fs from "fs";
12
11
  import path from "path";
13
- import { findAgentByDir } from "./agents.js";
14
12
 
15
13
  const CONFIG_FILENAME = ".fathom.json";
16
14
 
17
- const VALID_VAULT_MODES = new Set(["hosted", "synced", "local", "none"]);
15
+ const VALID_VAULT_MODES = new Set(["synced", "local", "none"]);
18
16
 
19
17
  const DEFAULTS = {
20
18
  workspace: "",
@@ -76,31 +74,13 @@ function applyConfig(result, config) {
76
74
  }
77
75
 
78
76
  /**
79
- * Resolve final config by merging: defaults → registry → .fathom.json → env vars.
77
+ * Resolve final config by merging: defaults → .fathom.json → env vars.
80
78
  */
81
79
  export function resolveConfig(startDir = process.cwd()) {
82
80
  const result = { ...DEFAULTS, hooks: { ...DEFAULTS.hooks } };
83
81
  let projectDir = startDir;
84
82
 
85
- // Layer 3: Central registry (matched by projectDir walk-up)
86
- let registryMatch = null;
87
- try {
88
- registryMatch = findAgentByDir(startDir);
89
- } catch {
90
- // Registry not available — skip
91
- }
92
- if (registryMatch) {
93
- const { name, entry } = registryMatch;
94
- projectDir = entry.projectDir;
95
- result.workspace = name;
96
- applyConfig(result, entry);
97
- if (entry.agentType) {
98
- result.agents = [entry.agentType];
99
- }
100
- result._registryName = name;
101
- }
102
-
103
- // Layer 2: .fathom.json (overrides registry)
83
+ // Layer 2: .fathom.json
104
84
  const found = findConfigFile(startDir);
105
85
  if (found) {
106
86
  projectDir = found.dir;