@fml-inc/fml 0.7.0 → 0.8.0-rc.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 (45) hide show
  1. package/.claude-plugin/plugin.json +2 -4
  2. package/README.md +323 -79
  3. package/agent-assets/fml/SKILL.md +180 -0
  4. package/bin/fml +4 -18
  5. package/bin/hook-handler +7 -0
  6. package/bin/mcp-server +4 -18
  7. package/bin/observer +6 -0
  8. package/dist/chunk-3KISRT57.js +140 -0
  9. package/dist/chunk-56TPYMNK.js +4657 -0
  10. package/dist/chunk-7V7G7NVF.js +142 -0
  11. package/dist/chunk-A6HAEVJX.js +1606 -0
  12. package/dist/chunk-DZKYW327.js +1037 -0
  13. package/dist/chunk-ENW2GJNO.js +109 -0
  14. package/dist/{chunk-PZ5AY32C.js → chunk-MLKGABMK.js} +0 -1
  15. package/dist/{chunk-PTUZMKSN.js → chunk-R45CWWZO.js} +34 -23
  16. package/dist/chunk-WZLLO563.js +953 -0
  17. package/dist/chunk-YSU5CS2V.js +3331 -0
  18. package/dist/cli.js +7742 -1072
  19. package/dist/{esm-2EUDFZWP.js → esm-4YKLKFRX.js} +546 -741
  20. package/dist/hook-handler.js +440 -0
  21. package/dist/mcp/server.js +112 -98
  22. package/dist/observer-server.js +1566 -0
  23. package/dist/sync-cycle.js +31 -0
  24. package/dist/{token-store-3IQSLOTQ.js → token-store-BT5THIAJ.js} +10 -3
  25. package/hooks/hooks.json +290 -0
  26. package/package.json +24 -27
  27. package/dist/chunk-PTUZMKSN.js.map +0 -1
  28. package/dist/chunk-PZ5AY32C.js.map +0 -1
  29. package/dist/chunk-S4TJNLEN.js +0 -61
  30. package/dist/chunk-S4TJNLEN.js.map +0 -1
  31. package/dist/chunk-VV4JHH4H.js +0 -437
  32. package/dist/chunk-VV4JHH4H.js.map +0 -1
  33. package/dist/chunk-YH5ROR7G.js +0 -69
  34. package/dist/chunk-YH5ROR7G.js.map +0 -1
  35. package/dist/cli.js.map +0 -1
  36. package/dist/daemon-MSLDHLMH.js +0 -21
  37. package/dist/daemon-MSLDHLMH.js.map +0 -1
  38. package/dist/daemon-utils-OODBIYB7.js +0 -10
  39. package/dist/daemon-utils-OODBIYB7.js.map +0 -1
  40. package/dist/esm-2EUDFZWP.js.map +0 -1
  41. package/dist/mcp/server.js.map +0 -1
  42. package/dist/postinstall.js +0 -16
  43. package/dist/postinstall.js.map +0 -1
  44. package/dist/token-store-3IQSLOTQ.js.map +0 -1
  45. package/scripts/postinstall.cjs +0 -25
@@ -2,22 +2,23 @@
2
2
  import {
3
3
  createFmlClient,
4
4
  getAuthenticatedClient
5
- } from "../chunk-PTUZMKSN.js";
5
+ } from "../chunk-R45CWWZO.js";
6
+ import {
7
+ resolveRepoFromCwd
8
+ } from "../chunk-7V7G7NVF.js";
6
9
  import {
7
10
  FML_DATA_DIR,
8
11
  FML_LOG_DIR,
9
12
  Sentry,
10
- authStorePath,
13
+ authStorePathFor,
14
+ getActiveEnv,
11
15
  getSelectedOrg,
16
+ getSiteUrlForConvexUrl,
12
17
  getValidToken,
13
18
  initSentry,
14
19
  readTokens
15
- } from "../chunk-VV4JHH4H.js";
16
- import {
17
- parsePanopticonRunning
18
- } from "../chunk-S4TJNLEN.js";
19
- import "../chunk-YH5ROR7G.js";
20
- import "../chunk-PZ5AY32C.js";
20
+ } from "../chunk-A6HAEVJX.js";
21
+ import "../chunk-MLKGABMK.js";
21
22
 
22
23
  // src/mcp/server.ts
23
24
  import fs from "fs";
@@ -27,7 +28,6 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
27
28
 
28
29
  // src/mcp/tools.ts
29
30
  import { z } from "zod";
30
- import { resolveRepoFromCwd } from "@fml-inc/panopticon/repo";
31
31
  function textResult(data) {
32
32
  return {
33
33
  content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
@@ -39,38 +39,76 @@ function errorResult(message) {
39
39
  isError: true
40
40
  };
41
41
  }
42
- var PANOPTICON_HINT = "\n\nFor local data, use the panopticon MCP tools instead (e.g. panopticon__sessions, panopticon__costs, panopticon__search, panopticon__timeline).";
43
- async function toolHandler(toolName, args) {
42
+ function restrictedSessionToolError(toolName, args) {
43
+ const allowedSessionId = process.env.FML_MCP_ALLOWED_SESSION_ID;
44
+ if (allowedSessionId === void 0) return null;
45
+ if (toolName !== "get-session-timeline" || args.sessionId !== allowedSessionId) {
46
+ return errorResult(
47
+ "This system task is restricted to its assigned session timeline."
48
+ );
49
+ }
50
+ return null;
51
+ }
52
+ async function toolHandler(toolName, args, environment) {
44
53
  try {
45
- const api = await getAuthenticatedClient();
54
+ const restrictionError = restrictedSessionToolError(toolName, args);
55
+ if (restrictionError) return restrictionError;
56
+ const api = await getAuthenticatedClient(environment);
46
57
  if (!api) {
47
58
  return errorResult(
48
- "Not authenticated. Run `fml login` to sign in, then restart Claude Code." + PANOPTICON_HINT
59
+ "Not authenticated. Run `fml login` to sign in, then restart Claude Code."
49
60
  );
50
61
  }
51
62
  const result = await api.callBackend(toolName, args);
52
63
  if (!result.ok) {
53
- return errorResult((result.error ?? "Unknown error") + PANOPTICON_HINT);
64
+ return errorResult(result.error ?? "Unknown error");
54
65
  }
55
66
  return textResult(result.result);
56
67
  } catch (err) {
57
68
  Sentry.captureException(err);
58
69
  const msg = err instanceof Error ? err.message : String(err);
59
- return errorResult(`Unexpected error: ${msg}` + PANOPTICON_HINT);
70
+ return errorResult(`Unexpected error: ${msg}`);
60
71
  }
61
72
  }
62
- function registerTools(server2) {
73
+ function registerTools(server2, environment = getActiveEnv()) {
74
+ const handleTool = (toolName, args) => toolHandler(toolName, args, environment);
75
+ const registerSessionTimelineTool = () => {
76
+ server2.tool(
77
+ "get_session_timeline",
78
+ 'Get chronological timeline for a coding agent session. Returns messages, hook events, and transcript-derived scanner events interleaved by timestamp. Use source="events" for the scanner event stream (thinking, tool_call, permission_mode, stop_hook_summary, ...); source="hooks" only returns live hook-capture events and is empty for scanner-only sessions. REQUIRES: sessionId from list_engineering_sessions.',
79
+ {
80
+ sessionId: z.string().describe("The session ID to get details for."),
81
+ includeSystemMessages: z.boolean().optional().describe("Include system messages (default false)."),
82
+ source: z.enum(["messages", "hooks", "events", "all"]).optional().describe(
83
+ 'Filter by source: "messages" (scanner messages only), "hooks" (live hook-capture events only), "events" (transcript-derived scanner events), "all" (everything interleaved). Default "all".'
84
+ ),
85
+ eventType: z.string().optional().describe(
86
+ 'Filter hook/scanner events by type (hook: "PermissionRequest", "PreToolUse", "Stop"; scanner: "tool_call", "permission_mode", "stop_hook_summary"). Only applies when source includes hooks or events.'
87
+ ),
88
+ limit: z.number().optional().describe("Max entries to return (default 50, max 200)."),
89
+ offset: z.number().optional().describe("Skip first N entries for pagination.")
90
+ },
91
+ async (args) => handleTool("get-session-timeline", args)
92
+ );
93
+ };
94
+ if (process.env.FML_MCP_ALLOWED_SESSION_ID !== void 0) {
95
+ registerSessionTimelineTool();
96
+ return;
97
+ }
63
98
  server2.tool(
64
99
  "whoami",
65
100
  "Show current authenticated user and available organizations",
66
101
  {},
67
102
  async () => {
68
- const tokens = readTokens();
69
- const token = tokens ? await getValidToken() : null;
103
+ const tokens = readTokens(environment.name);
104
+ const token = tokens ? await getValidToken({ env: environment.name }) : null;
70
105
  let orgs = null;
71
106
  if (token) {
72
107
  try {
73
- orgs = await createFmlClient(token).queryOrgs();
108
+ orgs = await createFmlClient(token, {
109
+ envName: environment.name,
110
+ siteUrl: getSiteUrlForConvexUrl(environment.convexUrl)
111
+ }).queryOrgs();
74
112
  } catch {
75
113
  }
76
114
  }
@@ -96,7 +134,7 @@ function registerTools(server2) {
96
134
  ),
97
135
  cursor: z.string().optional().describe("Pagination cursor from previous result to get next page")
98
136
  },
99
- async (args) => toolHandler("list-messages", args)
137
+ async (args) => handleTool("list-messages", args)
100
138
  );
101
139
  server2.tool(
102
140
  "fml_list_messages_with_context",
@@ -110,7 +148,7 @@ function registerTools(server2) {
110
148
  "Number of messages to retrieve after the target (default: 5)"
111
149
  )
112
150
  },
113
- async (args) => toolHandler("list-messages-with-context", args)
151
+ async (args) => handleTool("list-messages-with-context", args)
114
152
  );
115
153
  server2.tool(
116
154
  "fml_get_slack_channel_history",
@@ -121,7 +159,7 @@ function registerTools(server2) {
121
159
  ),
122
160
  limit: z.number().optional().describe("Number of messages to fetch (default 20, max 50)")
123
161
  },
124
- async (args) => toolHandler("get-slack-channel-history", args)
162
+ async (args) => handleTool("get-slack-channel-history", args)
125
163
  );
126
164
  server2.tool(
127
165
  "fml_get_slack_message",
@@ -140,13 +178,13 @@ function registerTools(server2) {
140
178
  "If true, fetches thread replies along with the message (default false)"
141
179
  )
142
180
  },
143
- async (args) => toolHandler("get-slack-message", args)
181
+ async (args) => handleTool("get-slack-message", args)
144
182
  );
145
183
  server2.tool(
146
184
  "fml_list_skills",
147
185
  "List all available FML skills. Skills provide detailed knowledge about FML features.",
148
186
  {},
149
- async () => toolHandler("list-skills", {})
187
+ async () => handleTool("list-skills", {})
150
188
  );
151
189
  server2.tool(
152
190
  "fml_load_skill",
@@ -154,7 +192,7 @@ function registerTools(server2) {
154
192
  {
155
193
  skillId: z.string().describe("The skill ID to load (e.g., 'fml-help', 'fml-navigation')")
156
194
  },
157
- async (args) => toolHandler("load-skill", args)
195
+ async (args) => handleTool("load-skill", args)
158
196
  );
159
197
  server2.tool(
160
198
  "fml_search_analysis",
@@ -172,7 +210,7 @@ function registerTools(server2) {
172
210
  "Limit to one repository. Omit to search every repo in the caller's organization."
173
211
  )
174
212
  },
175
- async (args) => toolHandler("search-analysis", args)
213
+ async (args) => handleTool("search-analysis", args)
176
214
  );
177
215
  server2.tool(
178
216
  "fml_run_analysis_workflow",
@@ -197,23 +235,13 @@ function registerTools(server2) {
197
235
  "Target repository ID. Omit for single-repo orgs (auto-pick). Required when the org has multiple repos \u2014 the tool will list them in its error if not provided."
198
236
  )
199
237
  },
200
- async (args) => toolHandler("run-analysis-workflow", args)
201
- );
202
- server2.tool(
203
- "fml_run_team_analysis",
204
- "Run a team-wide AI coding practice analysis for the caller's organization. Produces a structured report with per-person narratives covering tool usage, collaboration patterns, configuration, and notable changes. Use for prompts like 'run team analysis for the last 14 days' or 'how is the team doing?'. Kicks a durable Convex workflow and returns immediately with a reportId; when called inside a conversation, a completion message is posted back to that conversation when the workflow finishes \u2014 wait for that follow-up turn, do not poll. Idempotent: if a run is already in flight, returns the existing reportId with started=false. Requires org-owner access and at least one running dev environment.",
205
- {
206
- windowDays: z.number().int().min(1).max(90).optional().describe(
207
- "Size of the analysis window in days. Defaults to 30. Clamped to [1, 90]. Match the user's phrasing: 'last 14 days' \u2192 14, 'last month' \u2192 30, 'this week' \u2192 7."
208
- )
209
- },
210
- async (args) => toolHandler("run-team-analysis", args)
238
+ async (args) => handleTool("run-analysis-workflow", args)
211
239
  );
212
240
  server2.tool(
213
241
  "fml_list_integrations",
214
242
  "List integrations for this project \u2014 both connected and available. Use to check what integrations are active.",
215
243
  {},
216
- async () => toolHandler("list-integrations", {})
244
+ async () => handleTool("list-integrations", {})
217
245
  );
218
246
  const integrationTools = [
219
247
  {
@@ -271,7 +299,7 @@ function registerTools(server2) {
271
299
  "Optional project ID to scope this query. Required for GitHub (needs repo context)."
272
300
  )
273
301
  },
274
- async (args) => toolHandler(tool.mastraId, args)
302
+ async (args) => handleTool(tool.mastraId, args)
275
303
  );
276
304
  }
277
305
  server2.tool(
@@ -285,7 +313,7 @@ function registerTools(server2) {
285
313
  scope: z.enum(["org", "project", "user"]).optional().describe("Query scope. 'org' (default), 'project', or 'user'."),
286
314
  githubUsername: z.string().optional().describe("Required when scope is 'user'. GitHub username.")
287
315
  },
288
- async (args) => toolHandler("get-engineering-activity", args)
316
+ async (args) => handleTool("get-engineering-activity", args)
289
317
  );
290
318
  server2.tool(
291
319
  "list_engineering_sessions",
@@ -299,25 +327,9 @@ function registerTools(server2) {
299
327
  until: z.string().optional().describe("ISO 8601 end timestamp."),
300
328
  limit: z.number().optional().describe("Max sessions to return (default 20, max 50).")
301
329
  },
302
- async (args) => toolHandler("list-engineering-sessions", args)
303
- );
304
- server2.tool(
305
- "get_session_timeline",
306
- 'Get chronological timeline for a coding agent session. Returns messages and/or hook events interleaved by timestamp. Use source="hooks" to see permission requests, tool approvals, etc. REQUIRES: sessionId from list_engineering_sessions.',
307
- {
308
- sessionId: z.string().describe("The session ID to get details for."),
309
- includeSystemMessages: z.boolean().optional().describe("Include system messages (default false)."),
310
- source: z.enum(["messages", "hooks", "all"]).optional().describe(
311
- 'Filter by source: "messages" (scanner messages only), "hooks" (hook events only), "all" (both interleaved). Default "all".'
312
- ),
313
- eventType: z.string().optional().describe(
314
- 'Filter hook events by type (e.g. "PermissionRequest", "PreToolUse", "Stop"). Only applies when source includes hooks.'
315
- ),
316
- limit: z.number().optional().describe("Max entries to return (default 50, max 200)."),
317
- offset: z.number().optional().describe("Skip first N entries for pagination.")
318
- },
319
- async (args) => toolHandler("get-session-timeline", args)
330
+ async (args) => handleTool("list-engineering-sessions", args)
320
331
  );
332
+ registerSessionTimelineTool();
321
333
  server2.tool(
322
334
  "get_session_turns",
323
335
  "Get per-turn token accounting for a coding agent session. USE FOR: understanding cost per turn, model used at each step, cache hit analysis. REQUIRES: sessionId from list_engineering_sessions.",
@@ -326,7 +338,7 @@ function registerTools(server2) {
326
338
  limit: z.number().optional().describe("Max turns to return (default 50, max 200)."),
327
339
  offset: z.number().optional().describe("Skip first N turns for pagination.")
328
340
  },
329
- async (args) => toolHandler("get-session-turns", args)
341
+ async (args) => handleTool("get-session-turns", args)
330
342
  );
331
343
  server2.tool(
332
344
  "get_ai_spending",
@@ -335,10 +347,12 @@ function registerTools(server2) {
335
347
  timeRange: z.enum(["1h", "6h", "24h", "7d", "30d"]).optional().describe("Default '7d'."),
336
348
  since: z.string().optional().describe("ISO 8601 start timestamp."),
337
349
  until: z.string().optional().describe("ISO 8601 end timestamp."),
338
- groupBy: z.enum(["session", "model", "day"]).optional().describe("How to group results."),
339
- target: z.enum(["claude", "codex", "gemini", "mastra"]).optional().describe("Filter by agent type. Omit for all.")
350
+ groupBy: z.enum(["session", "model", "day", "user"]).optional().describe("How to group results."),
351
+ target: z.enum(["claude", "codex", "gemini", "mastra"]).optional().describe("Filter by agent type. Omit for all."),
352
+ limit: z.number().int().min(1).max(500).optional().describe("Maximum groups to return, default 100."),
353
+ offset: z.number().int().min(0).optional().describe("Groups to skip; use nextOffset from the previous page.")
340
354
  },
341
- async (args) => toolHandler("get-ai-spending", args)
355
+ async (args) => handleTool("get-ai-spending", args)
342
356
  );
343
357
  server2.tool(
344
358
  "search_engineering_sessions",
@@ -351,7 +365,7 @@ function registerTools(server2) {
351
365
  target: z.enum(["claude", "codex", "gemini", "mastra"]).optional().describe("Filter by agent type. Omit for all."),
352
366
  limit: z.number().optional().describe("Max results (default 20).")
353
367
  },
354
- async (args) => toolHandler("search-engineering-sessions", args)
368
+ async (args) => handleTool("search-engineering-sessions", args)
355
369
  );
356
370
  server2.tool(
357
371
  "list_user_configs",
@@ -359,9 +373,9 @@ function registerTools(server2) {
359
373
  {},
360
374
  async () => {
361
375
  try {
362
- const orgSlug = getSelectedOrg();
376
+ const orgSlug = getSelectedOrg(environment.name);
363
377
  if (!orgSlug) return errorResult("No org selected. Run `fml login`.");
364
- const api = await getAuthenticatedClient();
378
+ const api = await getAuthenticatedClient(environment);
365
379
  if (!api) return errorResult("Not authenticated. Run `fml login`.");
366
380
  const result = await api.listUserConfigSnapshots(orgSlug);
367
381
  return textResult(result);
@@ -381,9 +395,9 @@ function registerTools(server2) {
381
395
  },
382
396
  async (args) => {
383
397
  try {
384
- const orgSlug = getSelectedOrg();
398
+ const orgSlug = getSelectedOrg(environment.name);
385
399
  if (!orgSlug) return errorResult("No org selected. Run `fml login`.");
386
- const api = await getAuthenticatedClient();
400
+ const api = await getAuthenticatedClient(environment);
387
401
  if (!api) return errorResult("Not authenticated. Run `fml login`.");
388
402
  const result = await api.getUserConfigDetail(
389
403
  orgSlug,
@@ -408,9 +422,9 @@ function registerTools(server2) {
408
422
  },
409
423
  async (args) => {
410
424
  try {
411
- const orgSlug = getSelectedOrg();
425
+ const orgSlug = getSelectedOrg(environment.name);
412
426
  if (!orgSlug) return errorResult("No org selected. Run `fml login`.");
413
- const api = await getAuthenticatedClient();
427
+ const api = await getAuthenticatedClient(environment);
414
428
  if (!api) return errorResult("Not authenticated. Run `fml login`.");
415
429
  const result = await api.listRepoConfigSnapshots(
416
430
  orgSlug,
@@ -433,9 +447,9 @@ function registerTools(server2) {
433
447
  },
434
448
  async (args) => {
435
449
  try {
436
- const orgSlug = getSelectedOrg();
450
+ const orgSlug = getSelectedOrg(environment.name);
437
451
  if (!orgSlug) return errorResult("No org selected. Run `fml login`.");
438
- const api = await getAuthenticatedClient();
452
+ const api = await getAuthenticatedClient(environment);
439
453
  if (!api) return errorResult("Not authenticated. Run `fml login`.");
440
454
  const result = await api.getRepoConfigDetail(orgSlug, args.repository);
441
455
  if (!result)
@@ -458,6 +472,16 @@ function registerTools(server2) {
458
472
  }
459
473
  };
460
474
  const anamnesisResult = (result) => result.ok ? textResult(result.result) : errorResult(result.error ?? "Unknown error");
475
+ const withAnamnesisErrors = async (handler) => {
476
+ try {
477
+ return await handler();
478
+ } catch (err) {
479
+ Sentry.captureException(err);
480
+ return errorResult(
481
+ `Error: ${err instanceof Error ? err.message : String(err)}`
482
+ );
483
+ }
484
+ };
461
485
  server2.tool(
462
486
  "fml_path_history",
463
487
  "Ground-truth history for a file path: PRs that touched it and review comments on it (newest first). USE FOR: understanding why a file looks the way it does, who changed it, related PRs.",
@@ -466,13 +490,13 @@ function registerTools(server2) {
466
490
  repo: z.string().optional().describe("owner/repo. Defaults to the current working repo."),
467
491
  limit: z.number().optional().describe("Max items per list (default 25).")
468
492
  },
469
- async (args) => {
493
+ async (args) => await withAnamnesisErrors(async () => {
470
494
  const repo = resolveRepoArg(args.repo);
471
495
  if (!repo)
472
496
  return errorResult(
473
497
  "Could not determine repo \u2014 pass `repo` (owner/repo)."
474
498
  );
475
- const api = await getAuthenticatedClient();
499
+ const api = await getAuthenticatedClient(environment);
476
500
  if (!api) return errorResult("Not authenticated. Run `fml login`.");
477
501
  return anamnesisResult(
478
502
  await api.anamnesisContext("path", {
@@ -481,7 +505,7 @@ function registerTools(server2) {
481
505
  limit: args.limit
482
506
  })
483
507
  );
484
- }
508
+ })
485
509
  );
486
510
  server2.tool(
487
511
  "fml_commit_context",
@@ -490,18 +514,18 @@ function registerTools(server2) {
490
514
  sha: z.string().describe("Commit sha (full or abbreviated)."),
491
515
  repo: z.string().optional().describe("owner/repo. Defaults to the current working repo.")
492
516
  },
493
- async (args) => {
517
+ async (args) => await withAnamnesisErrors(async () => {
494
518
  const repo = resolveRepoArg(args.repo);
495
519
  if (!repo)
496
520
  return errorResult(
497
521
  "Could not determine repo \u2014 pass `repo` (owner/repo)."
498
522
  );
499
- const api = await getAuthenticatedClient();
523
+ const api = await getAuthenticatedClient(environment);
500
524
  if (!api) return errorResult("Not authenticated. Run `fml login`.");
501
525
  return anamnesisResult(
502
526
  await api.anamnesisContext("commit", { repo, sha: args.sha })
503
527
  );
504
- }
528
+ })
505
529
  );
506
530
  server2.tool(
507
531
  "fml_pr_context",
@@ -511,13 +535,13 @@ function registerTools(server2) {
511
535
  repo: z.string().optional().describe("owner/repo. Defaults to the current working repo."),
512
536
  limit: z.number().optional().describe("Max items per list (default 25).")
513
537
  },
514
- async (args) => {
538
+ async (args) => await withAnamnesisErrors(async () => {
515
539
  const repo = resolveRepoArg(args.repo);
516
540
  if (!repo)
517
541
  return errorResult(
518
542
  "Could not determine repo \u2014 pass `repo` (owner/repo)."
519
543
  );
520
- const api = await getAuthenticatedClient();
544
+ const api = await getAuthenticatedClient(environment);
521
545
  if (!api) return errorResult("Not authenticated. Run `fml login`.");
522
546
  return anamnesisResult(
523
547
  await api.anamnesisContext("pr", {
@@ -526,7 +550,7 @@ function registerTools(server2) {
526
550
  limit: args.limit
527
551
  })
528
552
  );
529
- }
553
+ })
530
554
  );
531
555
  server2.tool(
532
556
  "fml_anamnesis_query",
@@ -540,16 +564,16 @@ function registerTools(server2) {
540
564
  repo: z.string().optional().describe("owner/repo. Defaults to the current working repo."),
541
565
  limit: z.number().optional().describe("Max facts (default 25).")
542
566
  },
543
- async (args) => {
567
+ async (args) => await withAnamnesisErrors(async () => {
544
568
  const repo = resolveRepoArg(args.repo);
545
569
  if (!repo)
546
570
  return errorResult(
547
571
  "Could not determine repo \u2014 pass `repo` (owner/repo)."
548
572
  );
549
- const api = await getAuthenticatedClient();
573
+ const api = await getAuthenticatedClient(environment);
550
574
  if (!api) return errorResult("Not authenticated. Run `fml login`.");
551
575
  return anamnesisResult(await api.anamnesisQuery({ ...args, repo }));
552
- }
576
+ })
553
577
  );
554
578
  }
555
579
 
@@ -559,9 +583,10 @@ var server = new McpServer({
559
583
  name: "fml",
560
584
  version: "0.1.0"
561
585
  });
562
- registerTools(server);
563
586
  async function main() {
564
587
  await initSentry();
588
+ const environment = getActiveEnv();
589
+ registerTools(server, environment);
565
590
  if (!fs.existsSync(FML_LOG_DIR)) {
566
591
  fs.mkdirSync(FML_LOG_DIR, { recursive: true });
567
592
  }
@@ -570,10 +595,10 @@ async function main() {
570
595
  process.stderr.write = logStream.write.bind(
571
596
  logStream
572
597
  );
573
- const tokens = readTokens();
574
- const tokenValid = tokens ? await getValidToken() : null;
598
+ const tokens = readTokens(environment.name);
599
+ const tokenValid = tokens ? await getValidToken({ env: environment.name }) : null;
575
600
  if (!tokens) {
576
- const storePath = authStorePath();
601
+ const storePath = authStorePathFor(environment.name);
577
602
  try {
578
603
  const files = fs.readdirSync(FML_DATA_DIR).join(", ") || "(empty)";
579
604
  console.error(
@@ -596,20 +621,9 @@ async function main() {
596
621
  }
597
622
  const transport = new StdioServerTransport();
598
623
  await server.connect(transport);
599
- if (!parsePanopticonRunning()) {
600
- try {
601
- const { handlePanopticonStart } = await import("../daemon-MSLDHLMH.js");
602
- await handlePanopticonStart();
603
- } catch {
604
- console.error(
605
- "[fml] Warning: could not start panopticon. Run `fml start` manually"
606
- );
607
- }
608
- }
609
624
  }
610
625
  main().catch((err) => {
611
626
  Sentry.captureException(err);
612
627
  console.error("FML MCP server error:", err);
613
628
  process.exit(1);
614
629
  });
615
- //# sourceMappingURL=server.js.map