fathom-mcp 0.5.0 → 0.5.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fathom-mcp",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "MCP server for Fathom — vault operations, search, rooms, and cross-workspace communication",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,226 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # fathom-start.sh — Launch an agent in a correctly-named tmux session.
4
+ #
5
+ # Reads .fathom.json for workspace name and agent, creates
6
+ # {workspace}_fathom-session, saves pane ID for WebSocket push injection.
7
+ #
8
+ # Usage:
9
+ # fathom-start.sh Start agent, save pane ID, attach
10
+ # fathom-start.sh --detach Start agent, save pane ID, don't attach
11
+ # fathom-start.sh --agent X Override agent (claude-code|codex|gemini|opencode)
12
+ # fathom-start.sh --kill Kill existing session
13
+ # fathom-start.sh --status Show session status
14
+
15
+ set -euo pipefail
16
+
17
+ if ! command -v tmux &>/dev/null; then
18
+ echo "Error: tmux is not installed. Install it first:" >&2
19
+ echo " apt install tmux | brew install tmux | dnf install tmux" >&2
20
+ exit 1
21
+ fi
22
+
23
+ # ── Defaults ──────────────────────────────────────────────────────────────────
24
+
25
+ ATTACH=true
26
+ AGENT_OVERRIDE=""
27
+ ACTION="start"
28
+
29
+ # ── Parse flags ───────────────────────────────────────────────────────────────
30
+
31
+ while [[ $# -gt 0 ]]; do
32
+ case "$1" in
33
+ --detach) ATTACH=false; shift ;;
34
+ --attach) ATTACH=true; shift ;;
35
+ --agent) AGENT_OVERRIDE="$2"; shift 2 ;;
36
+ --kill) ACTION="kill"; shift ;;
37
+ --status) ACTION="status"; shift ;;
38
+ -h|--help)
39
+ echo "Usage: fathom-start.sh [--attach] [--detach] [--agent NAME] [--kill] [--status]"
40
+ echo ""
41
+ echo " (default) Start agent in tmux, save pane ID, attach"
42
+ echo " --detach Start but don't attach"
43
+ echo " --agent X Override agent: claude-code, codex, gemini, opencode"
44
+ echo " --kill Kill existing session"
45
+ echo " --status Show if session is running"
46
+ exit 0
47
+ ;;
48
+ *)
49
+ echo "Unknown flag: $1" >&2
50
+ exit 1
51
+ ;;
52
+ esac
53
+ done
54
+
55
+ # ── Find .fathom.json ────────────────────────────────────────────────────────
56
+
57
+ find_config() {
58
+ local dir="$PWD"
59
+ while [[ "$dir" != "/" ]]; do
60
+ if [[ -f "$dir/.fathom.json" ]]; then
61
+ echo "$dir/.fathom.json"
62
+ return 0
63
+ fi
64
+ dir="$(dirname "$dir")"
65
+ done
66
+ return 1
67
+ }
68
+
69
+ CONFIG_FILE=$(find_config) || {
70
+ echo "Error: No .fathom.json found (searched from $PWD to /)" >&2
71
+ echo "Run 'npx fathom-mcp init' first." >&2
72
+ exit 1
73
+ }
74
+
75
+ PROJECT_DIR="$(dirname "$CONFIG_FILE")"
76
+
77
+ # ── Parse config ──────────────────────────────────────────────────────────────
78
+
79
+ read_json_field() {
80
+ local file="$1" field="$2"
81
+ if command -v jq &>/dev/null; then
82
+ jq -r ".$field // empty" "$file" 2>/dev/null
83
+ else
84
+ # Fallback: simple grep/sed for flat string fields
85
+ sed -n "s/.*\"$field\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" "$file" | head -1
86
+ fi
87
+ }
88
+
89
+ read_json_array_first() {
90
+ local file="$1" field="$2"
91
+ if command -v jq &>/dev/null; then
92
+ jq -r ".$field[0] // empty" "$file" 2>/dev/null
93
+ else
94
+ # Fallback: grab first quoted string after the array field
95
+ sed -n "/\"$field\"/,/\]/{ s/.*\"\([^\"]*\)\".*/\1/p; }" "$file" | head -1
96
+ fi
97
+ }
98
+
99
+ WORKSPACE=$(read_json_field "$CONFIG_FILE" "workspace")
100
+ if [[ -z "$WORKSPACE" ]]; then
101
+ WORKSPACE="$(basename "$PROJECT_DIR")"
102
+ fi
103
+
104
+ SESSION="${WORKSPACE}_fathom-session"
105
+ PANE_DIR="$HOME/.config/fathom"
106
+ PANE_FILE="$PANE_DIR/${WORKSPACE}-pane-id"
107
+
108
+ # ── Resolve agent command ─────────────────────────────────────────────────────
109
+
110
+ resolve_agent_cmd() {
111
+ local agent="${AGENT_OVERRIDE:-}"
112
+ if [[ -z "$agent" ]]; then
113
+ agent=$(read_json_array_first "$CONFIG_FILE" "agents")
114
+ fi
115
+ if [[ -z "$agent" ]]; then
116
+ agent="claude-code"
117
+ fi
118
+
119
+ case "$agent" in
120
+ claude-code)
121
+ echo "claude --model opus --permission-mode bypassPermissions"
122
+ ;;
123
+ codex)
124
+ echo "codex"
125
+ ;;
126
+ gemini)
127
+ echo "gemini"
128
+ ;;
129
+ opencode)
130
+ echo "opencode"
131
+ ;;
132
+ *)
133
+ echo "Warning: Unknown agent '$agent', falling back to claude" >&2
134
+ echo "claude"
135
+ ;;
136
+ esac
137
+ }
138
+
139
+ # ── Save pane ID ──────────────────────────────────────────────────────────────
140
+
141
+ save_pane_id() {
142
+ local pane_id
143
+ pane_id=$(tmux list-panes -t "$SESSION" -F '#{pane_id}' 2>/dev/null | head -1)
144
+ if [[ -n "$pane_id" ]]; then
145
+ mkdir -p "$PANE_DIR"
146
+ echo "$pane_id" > "$PANE_FILE"
147
+ echo "Pane ID: $pane_id → $PANE_FILE"
148
+ fi
149
+ }
150
+
151
+ # ── Session check ─────────────────────────────────────────────────────────────
152
+
153
+ session_exists() {
154
+ tmux has-session -t "$SESSION" 2>/dev/null
155
+ }
156
+
157
+ # ── Actions ───────────────────────────────────────────────────────────────────
158
+
159
+ do_status() {
160
+ echo "Workspace: $WORKSPACE"
161
+ echo "Session: $SESSION"
162
+ if session_exists; then
163
+ echo "Status: running"
164
+ if [[ -f "$PANE_FILE" ]]; then
165
+ echo "Pane ID: $(cat "$PANE_FILE")"
166
+ fi
167
+ else
168
+ echo "Status: not running"
169
+ fi
170
+ }
171
+
172
+ do_kill() {
173
+ if session_exists; then
174
+ tmux kill-session -t "$SESSION"
175
+ rm -f "$PANE_FILE"
176
+ echo "Killed session: $SESSION"
177
+ else
178
+ echo "Session not running: $SESSION"
179
+ fi
180
+ }
181
+
182
+ do_start() {
183
+ if session_exists; then
184
+ echo "Session already running: $SESSION"
185
+ save_pane_id
186
+ if [[ "$ATTACH" == true ]]; then
187
+ exec tmux attach-session -t "$SESSION"
188
+ fi
189
+ return 0
190
+ fi
191
+
192
+ local agent_cmd
193
+ agent_cmd=$(resolve_agent_cmd)
194
+
195
+ echo "Starting: $SESSION"
196
+ echo "Agent: $agent_cmd"
197
+ echo "Dir: $PROJECT_DIR"
198
+
199
+ # Unset CLAUDECODE to avoid nested session detection
200
+ unset CLAUDECODE 2>/dev/null || true
201
+
202
+ # Create detached tmux session running the agent
203
+ tmux new-session -d -s "$SESSION" -c "$PROJECT_DIR" $agent_cmd
204
+
205
+ # Wait briefly for session to stabilize
206
+ sleep 2
207
+
208
+ if session_exists; then
209
+ save_pane_id
210
+ echo "Session started."
211
+ if [[ "$ATTACH" == true ]]; then
212
+ exec tmux attach-session -t "$SESSION"
213
+ fi
214
+ else
215
+ echo "Error: Session failed to start" >&2
216
+ exit 1
217
+ fi
218
+ }
219
+
220
+ # ── Main ──────────────────────────────────────────────────────────────────────
221
+
222
+ case "$ACTION" in
223
+ status) do_status ;;
224
+ kill) do_kill ;;
225
+ start) do_start ;;
226
+ esac
package/src/cli.js CHANGED
@@ -845,6 +845,35 @@ async function runUpdate() {
845
845
  console.log(" Restart your agent session to pick up changes.\n");
846
846
  }
847
847
 
848
+ // --- Start command -----------------------------------------------------------
849
+
850
+ function runStart(argv) {
851
+ // Find the installed fathom-start.sh script
852
+ const found = findConfigFile(process.cwd());
853
+ const projectDir = found?.dir || process.cwd();
854
+
855
+ // Check .fathom/scripts/ first (installed by init/update), then package scripts/
856
+ const localScript = path.join(projectDir, ".fathom", "scripts", "fathom-start.sh");
857
+ const packageScript = path.join(SCRIPTS_DIR, "fathom-start.sh");
858
+ const script = fs.existsSync(localScript) ? localScript : packageScript;
859
+
860
+ if (!fs.existsSync(script)) {
861
+ console.error(" Error: fathom-start.sh not found. Run `npx fathom-mcp update` first.");
862
+ process.exit(1);
863
+ }
864
+
865
+ // Pass remaining args through to the shell script
866
+ try {
867
+ execFileSync("bash", [script, ...argv], {
868
+ cwd: projectDir,
869
+ stdio: "inherit",
870
+ });
871
+ } catch (e) {
872
+ // Script already printed its own errors; just propagate exit code
873
+ process.exit(e.status || 1);
874
+ }
875
+ }
876
+
848
877
  // --- Main --------------------------------------------------------------------
849
878
 
850
879
  // Guard: only run CLI when this module is the entry point (not when imported by tests)
@@ -871,12 +900,14 @@ if (isMain) {
871
900
  console.error(`Error: ${e.message}`);
872
901
  process.exit(1);
873
902
  });
903
+ } else if (command === "start") {
904
+ runStart(process.argv.slice(3));
874
905
  } else if (!command || command === "serve") {
875
906
  // Default: start MCP server
876
907
  import("./index.js");
877
908
  } else {
878
909
  console.error(`Unknown command: ${command}`);
879
- console.error(`Usage: fathom-mcp [init|status|update|serve]
910
+ console.error(`Usage: fathom-mcp [init|status|update|start|serve]
880
911
 
881
912
  fathom-mcp init Interactive setup
882
913
  fathom-mcp init -y --api-key KEY Non-interactive setup
@@ -884,6 +915,9 @@ if (isMain) {
884
915
  fathom-mcp init -y --api-key KEY --workspace NAME Custom workspace name
885
916
  fathom-mcp status Check connection status
886
917
  fathom-mcp update Update hooks + version
918
+ fathom-mcp start Start agent in tmux session
919
+ fathom-mcp start --detach Start without attaching
920
+ fathom-mcp start --kill Kill agent session
887
921
  fathom-mcp Start MCP server`);
888
922
  process.exit(1);
889
923
  }
File without changes