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 +1 -1
- package/scripts/fathom-start.sh +226 -0
- package/src/cli.js +35 -1
- package/src/ws-connection.js +0 -0
package/package.json
CHANGED
|
@@ -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
|
}
|
package/src/ws-connection.js
CHANGED
|
File without changes
|