@mgsoftwarebv/mg-dashboard-mcp 7.0.13 → 7.0.15

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/dist/index.js CHANGED
@@ -683,10 +683,10 @@ var TRIGGER_TOOL_MODULE_MAP = {
683
683
  "trigger-run": "ci_cd"
684
684
  };
685
685
  async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
686
- const sql23 = `SELECT re.\\"apiKey\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
686
+ const sql25 = `SELECT re.\\"apiKey\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
687
687
  const cmd = [
688
688
  `PORT=$(docker port "${WA_CONTAINER}" 3000/tcp 2>/dev/null | head -1 | sed 's/.*://')`,
689
- `KEY=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql23}" 2>/dev/null | tr -d '[:space:]')`,
689
+ `KEY=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql25}" 2>/dev/null | tr -d '[:space:]')`,
690
690
  'echo "$PORT|$KEY"'
691
691
  ].join(" && ");
692
692
  const result = await sshExec2(conn, cmd, proxy);
@@ -707,8 +707,8 @@ async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
707
707
  return { port, apiKey: apiKey2 };
708
708
  }
709
709
  async function fetchRunLogs(runId, conn, proxy, sshExec2) {
710
- const sql23 = `SELECT level, message, \\"isError\\", \\"createdAt\\" FROM \\"TaskEvent\\" WHERE \\"runId\\" = '${runId}' AND level IN ('INFO','WARN','ERROR','DEBUG','LOG','TRACE') ORDER BY \\"startTime\\" ASC LIMIT 200`;
711
- const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql23}" 2>/dev/null`;
710
+ const sql25 = `SELECT level, message, \\"isError\\", \\"createdAt\\" FROM \\"TaskEvent\\" WHERE \\"runId\\" = '${runId}' AND level IN ('INFO','WARN','ERROR','DEBUG','LOG','TRACE') ORDER BY \\"startTime\\" ASC LIMIT 200`;
711
+ const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql25}" 2>/dev/null`;
712
712
  const result = await sshExec2(conn, cmd, proxy);
713
713
  const output = result.stdout.trim();
714
714
  if (!output) return "";
@@ -790,8 +790,8 @@ async function handleTriggerTool(name, args2, deps) {
790
790
  switch (name) {
791
791
  // -----------------------------------------------------------------
792
792
  case "trigger-list": {
793
- const sql23 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
794
- const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql23}" 2>/dev/null`;
793
+ const sql25 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
794
+ const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql25}" 2>/dev/null`;
795
795
  const result = await sshExec2(conn, cmd, proxy);
796
796
  const output = result.stdout.trim();
797
797
  if (!output) {
@@ -6188,6 +6188,145 @@ async function reconfigureQuickStages(profileId, releaseType, detectedApps) {
6188
6188
  `);
6189
6189
  }
6190
6190
  }
6191
+ function normalizeRepoIdentifier(value) {
6192
+ if (!value) return null;
6193
+ let s = value.trim().toLowerCase();
6194
+ if (!s) return null;
6195
+ s = s.replace(/^ssh:\/\/git@github\.com\//, "");
6196
+ s = s.replace(/^git@github\.com:/, "");
6197
+ s = s.replace(/^https?:\/\/(www\.)?github\.com\//, "");
6198
+ s = s.replace(/^github\.com\//, "");
6199
+ s = s.replace(/\.git$/, "");
6200
+ s = s.replace(/[/]+$/, "");
6201
+ const parts = s.split("/").filter(Boolean);
6202
+ if (parts.length >= 2) {
6203
+ s = parts.slice(-2).join("/");
6204
+ }
6205
+ return s || null;
6206
+ }
6207
+ async function queueAgentRun(args2) {
6208
+ const db2 = getDb();
6209
+ const gitMode = args2.gitMode ?? "worktree";
6210
+ const model = args2.model ?? null;
6211
+ const metadata = args2.metadata ?? {};
6212
+ const conversationRows = await db2.execute(sql`
6213
+ INSERT INTO agent_conversation (
6214
+ user_id, host_id, workspace_id, runtime, source, title, status, model, metadata
6215
+ ) VALUES (
6216
+ ${args2.userId}, ${args2.workspace.host_id}, ${args2.workspace.id},
6217
+ 'local', ${args2.source}, ${args2.title}, 'running', ${model},
6218
+ ${JSON.stringify(metadata)}::jsonb
6219
+ )
6220
+ RETURNING id
6221
+ `);
6222
+ const conversationId = conversationRows[0]?.id;
6223
+ if (!conversationId) return null;
6224
+ const runRows = await db2.execute(sql`
6225
+ INSERT INTO agent_run (
6226
+ conversation_id, workspace_id, host_id, user_id, runtime, status,
6227
+ prompt, model, git_mode, source
6228
+ ) VALUES (
6229
+ ${conversationId}, ${args2.workspace.id}, ${args2.workspace.host_id},
6230
+ ${args2.userId}, 'local', 'queued', ${args2.prompt}, ${model}, ${gitMode},
6231
+ ${args2.source}
6232
+ )
6233
+ RETURNING id
6234
+ `);
6235
+ const runId = runRows[0]?.id;
6236
+ if (!runId) return null;
6237
+ const payload = {
6238
+ runId,
6239
+ conversationId,
6240
+ workspacePath: args2.workspace.path,
6241
+ prompt: args2.prompt,
6242
+ model,
6243
+ gitMode,
6244
+ sdkAgentId: null,
6245
+ contextReplay: null,
6246
+ ...metadata
6247
+ };
6248
+ await db2.execute(sql`
6249
+ INSERT INTO agent_host_command (host_id, user_id, run_id, command_type, payload)
6250
+ VALUES (
6251
+ ${args2.workspace.host_id}, ${args2.userId}, ${runId}, 'start_run',
6252
+ ${JSON.stringify(payload)}::jsonb
6253
+ )
6254
+ `);
6255
+ return { conversationId, runId };
6256
+ }
6257
+ function toTarget(row) {
6258
+ return {
6259
+ workspaceId: row.workspace_id,
6260
+ hostId: row.host_id,
6261
+ hostName: row.host_name,
6262
+ hostOnline: row.host_online === true,
6263
+ ownerUserId: row.owner_user_id,
6264
+ ownerName: row.owner_name,
6265
+ ownerEmail: row.owner_email,
6266
+ path: row.path,
6267
+ name: row.name,
6268
+ gitRepository: row.git_repository,
6269
+ gitBranch: row.git_branch,
6270
+ defaultGitMode: row.default_git_mode === "in_place" ? "in_place" : "worktree",
6271
+ lastSyncedAt: row.last_synced_at
6272
+ };
6273
+ }
6274
+ async function listRemoteWorkspaces() {
6275
+ const rows = await getDb().execute(sql`
6276
+ SELECT
6277
+ w.id AS workspace_id,
6278
+ w.host_id,
6279
+ h.name AS host_name,
6280
+ (h.last_seen_at IS NOT NULL AND h.last_seen_at > now() - interval '45 seconds')
6281
+ AS host_online,
6282
+ w.user_id AS owner_user_id,
6283
+ u.full_name AS owner_name,
6284
+ u.email AS owner_email,
6285
+ w.path, w.name, w.git_repository, w.git_branch,
6286
+ w.default_git_mode, w.last_synced_at
6287
+ FROM agent_workspace w
6288
+ INNER JOIN agent_host h ON h.id = w.host_id
6289
+ LEFT JOIN "user" u ON u.id = w.user_id
6290
+ WHERE w.status = 'active' AND h.revoked_at IS NULL
6291
+ ORDER BY host_online DESC, w.last_synced_at DESC NULLS LAST
6292
+ `);
6293
+ return Array.from(rows).map(toTarget);
6294
+ }
6295
+ function matchesWho(target, who) {
6296
+ const needle = who.trim().toLowerCase();
6297
+ if (!needle) return true;
6298
+ const name = (target.ownerName ?? "").toLowerCase();
6299
+ const email = (target.ownerEmail ?? "").toLowerCase();
6300
+ return name.includes(needle) || email.includes(needle);
6301
+ }
6302
+ function matchesProject(target, project) {
6303
+ const repoTarget = normalizeRepoIdentifier(project);
6304
+ if (repoTarget && normalizeRepoIdentifier(target.gitRepository) === repoTarget) {
6305
+ return true;
6306
+ }
6307
+ const needle = project.trim().toLowerCase();
6308
+ if (!needle) return false;
6309
+ return target.name.toLowerCase().includes(needle) || target.path.toLowerCase().includes(needle);
6310
+ }
6311
+ async function resolveRemoteTarget(args2) {
6312
+ const online = (await listRemoteWorkspaces()).filter((t) => t.hostOnline);
6313
+ let matches = online.filter((t) => matchesProject(t, args2.project));
6314
+ if (args2.who) {
6315
+ matches = matches.filter((t) => matchesWho(t, args2.who));
6316
+ }
6317
+ if (matches.length === 0) {
6318
+ return { ok: false, reason: "none", candidates: online };
6319
+ }
6320
+ if (matches.length === 1) {
6321
+ return { ok: true, target: matches[0] };
6322
+ }
6323
+ if (args2.branch) {
6324
+ const onBranch = matches.filter((t) => t.gitBranch === args2.branch);
6325
+ if (onBranch.length === 1) return { ok: true, target: onBranch[0] };
6326
+ if (onBranch.length > 1) matches = onBranch;
6327
+ }
6328
+ return { ok: false, reason: "ambiguous", candidates: matches };
6329
+ }
6191
6330
  var args = process.argv.slice(2);
6192
6331
  function getArg2(name) {
6193
6332
  return args.find((a) => a.startsWith(`--${name}=`))?.split("=").slice(1).join("=");
@@ -6370,7 +6509,9 @@ var MODULE_KEYS = [
6370
6509
  "wiki",
6371
6510
  "ci_cd",
6372
6511
  "domains",
6373
- "settings"
6512
+ "settings",
6513
+ "cursor_remote",
6514
+ "code_battle"
6374
6515
  ];
6375
6516
  var FULL_PERMISSIONS = {
6376
6517
  modules: Object.fromEntries(
@@ -6433,8 +6574,15 @@ var TOOL_MODULE_MAP = {
6433
6574
  "domain-list": "domains",
6434
6575
  "dns-list": "domains",
6435
6576
  "dns-record": "domains",
6577
+ "cursor-remote-list": "cursor_remote",
6578
+ "cursor-remote-run": "cursor_remote",
6436
6579
  ...TRIGGER_TOOL_MODULE_MAP
6437
6580
  };
6581
+ function normalizeAllowedTools(value) {
6582
+ if (!Array.isArray(value)) return null;
6583
+ const tools = value.filter((t) => typeof t === "string").map((t) => t.trim()).filter(Boolean);
6584
+ return tools.length > 0 ? tools : null;
6585
+ }
6438
6586
  var authContext = null;
6439
6587
  var currentClientIp;
6440
6588
  function extractClientIp(req) {
@@ -6463,7 +6611,7 @@ async function validateApiKey(key) {
6463
6611
  return null;
6464
6612
  }
6465
6613
  const apiKeyRows = await db.execute(sql`
6466
- SELECT id, name, created_by, allowed_server_ids, is_active, expires_at
6614
+ SELECT id, name, created_by, allowed_server_ids, allowed_tools, is_active, expires_at
6467
6615
  FROM dashboard_mcp_api_key
6468
6616
  WHERE api_key_hash = ${keyHash} AND is_active = true
6469
6617
  LIMIT 1
@@ -6515,6 +6663,7 @@ async function validateApiKey(key) {
6515
6663
  apiKeyName: data.name || "Unknown",
6516
6664
  userId: data.created_by,
6517
6665
  allowedServerIds,
6666
+ allowedTools: normalizeAllowedTools(data.allowed_tools),
6518
6667
  permissions,
6519
6668
  roleName
6520
6669
  };
@@ -6709,7 +6858,7 @@ async function validateSshKey(pubkeyPathInput, expectedApiKeyId) {
6709
6858
  return null;
6710
6859
  }
6711
6860
  const apiRows = await db.execute(sql`
6712
- SELECT id, name, created_by, allowed_server_ids, is_active, expires_at
6861
+ SELECT id, name, created_by, allowed_server_ids, allowed_tools, is_active, expires_at
6713
6862
  FROM dashboard_mcp_api_key
6714
6863
  WHERE id = ${keyRow.api_key_id} AND is_active = true
6715
6864
  LIMIT 1
@@ -6763,6 +6912,7 @@ async function validateSshKey(pubkeyPathInput, expectedApiKeyId) {
6763
6912
  apiKeyName: `${apiRow.name || "Unknown"} (ssh: ${keyRow.name})`,
6764
6913
  userId: apiRow.created_by,
6765
6914
  allowedServerIds,
6915
+ allowedTools: normalizeAllowedTools(apiRow.allowed_tools),
6766
6916
  permissions,
6767
6917
  roleName
6768
6918
  };
@@ -8991,11 +9141,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
8991
9141
  applied_by TEXT
8992
9142
  );
8993
9143
  `.trim();
8994
- function normaliseMigrationSql(sql23) {
8995
- return sql23.replace(/\r\n/g, "\n").trim() + "\n";
9144
+ function normaliseMigrationSql(sql25) {
9145
+ return sql25.replace(/\r\n/g, "\n").trim() + "\n";
8996
9146
  }
8997
- function migrationSha256(sql23) {
8998
- return createHash("sha256").update(normaliseMigrationSql(sql23), "utf8").digest("hex");
9147
+ function migrationSha256(sql25) {
9148
+ return createHash("sha256").update(normaliseMigrationSql(sql25), "utf8").digest("hex");
8999
9149
  }
9000
9150
  function dollarQuoteTag(value) {
9001
9151
  let tag = "_mcp";
@@ -10200,13 +10350,64 @@ var TOOLS = [
10200
10350
  }
10201
10351
  }
10202
10352
  },
10353
+ // ----- Cursor Remote (Agent Control) -----
10354
+ {
10355
+ name: "cursor-remote-list",
10356
+ description: "List the paired developer machines (agent hosts) and their workspaces that a headless Cursor agent run can be started on, across the whole team. Each workspace shows its owner, git repository + branch, online status and default git mode. Call this FIRST to discover valid `who` (owner) and `project` (repo/workspace) values before calling cursor-remote-run. Only online hosts can run.",
10357
+ inputSchema: {
10358
+ type: "object",
10359
+ properties: {
10360
+ onlineOnly: {
10361
+ type: "boolean",
10362
+ description: "Only return workspaces whose host is currently online (default false \u2014 shows all active pairings with an online flag)."
10363
+ }
10364
+ }
10365
+ }
10366
+ },
10367
+ {
10368
+ name: "cursor-remote-run",
10369
+ description: "Start a headless Cursor agent run on a teammate's machine to make a code change. Resolve `who` (workspace owner name/email) + `project` (repo 'org/repo', URL, or workspace name) to a single online workspace, then queue the run via the agent-host daemon (@cursor/sdk). Use this when an AI (e.g. a ticket assistant) should hand a self-contained, well-scoped fix prompt to a developer's PC. Default git mode is 'worktree': the change runs in an isolated worktree and ends in a DIFF APPROVAL the owner reviews on the dashboard/mobile \u2014 nothing lands on their branch automatically. Returns the conversationId + runId and a dashboard link to follow progress. Call cursor-remote-list first if unsure about who/project.",
10370
+ inputSchema: {
10371
+ type: "object",
10372
+ properties: {
10373
+ who: {
10374
+ type: "string",
10375
+ description: "Workspace owner to target, matched case-insensitively against full name or email (e.g. 'Sidney'). Optional, but recommended when several people have the same repo paired."
10376
+ },
10377
+ project: {
10378
+ type: "string",
10379
+ description: "The repository ('MGSoftwareBV/mg-dashboard', a GitHub URL), or the workspace name/path. Matched against the paired workspaces."
10380
+ },
10381
+ prompt: {
10382
+ type: "string",
10383
+ description: "The full, self-contained instruction for the agent. Write it as an optimized, scoped task: what to change, relevant files/areas, and acceptance criteria. The agent has no other context."
10384
+ },
10385
+ model: {
10386
+ type: "string",
10387
+ description: "Optional Cursor model slug to run with (e.g. a Sonnet-tier model). Omit to use the host default."
10388
+ },
10389
+ gitMode: {
10390
+ type: "string",
10391
+ enum: ["worktree", "in_place"],
10392
+ description: "'worktree' (default, recommended): isolated worktree + diff approval. 'in_place': applies directly in the working tree (no isolation) \u2014 use only when explicitly desired."
10393
+ },
10394
+ branch: {
10395
+ type: "string",
10396
+ description: "Optional: prefer a workspace already checked out on this branch when several match."
10397
+ }
10398
+ },
10399
+ required: ["project", "prompt"]
10400
+ }
10401
+ },
10203
10402
  // ----- Trigger.dev -----
10204
10403
  ...TRIGGER_TOOLS
10205
10404
  ];
10206
10405
  var MCP_VERSION = "7.0.5";
10207
10406
  async function handleListTools() {
10208
10407
  if (!authContext) return { tools: TOOLS };
10408
+ const allowedTools = authContext.allowedTools;
10209
10409
  const accessible = TOOLS.filter((tool) => {
10410
+ if (allowedTools && !allowedTools.includes(tool.name)) return false;
10210
10411
  const requiredModule = TOOL_MODULE_MAP[tool.name];
10211
10412
  if (!requiredModule) return true;
10212
10413
  return authContext.permissions.modules[requiredModule] === true;
@@ -10219,6 +10420,16 @@ async function handleCallTool(request) {
10219
10420
  }
10220
10421
  const { name, arguments: toolArgs } = request.params;
10221
10422
  const a = toolArgs || {};
10423
+ if (authContext.allowedTools && !authContext.allowedTools.includes(name)) {
10424
+ return {
10425
+ content: [
10426
+ {
10427
+ type: "text",
10428
+ text: `Access denied: this API key is restricted to a specific tool allowlist and may not call "${name}". Allowed: ${authContext.allowedTools.join(", ")}`
10429
+ }
10430
+ ]
10431
+ };
10432
+ }
10222
10433
  const requiredModule = TOOL_MODULE_MAP[name];
10223
10434
  if (requiredModule && authContext.permissions.modules[requiredModule] !== true) {
10224
10435
  return {
@@ -10424,6 +10635,131 @@ ${facets.join(", ")}` : ""),
10424
10635
  ]
10425
10636
  };
10426
10637
  }
10638
+ // ----- Cursor Remote (Agent Control) -----
10639
+ case "cursor-remote-list": {
10640
+ const onlineOnly = a.onlineOnly === true;
10641
+ const all = await listRemoteWorkspaces();
10642
+ const workspaces = onlineOnly ? all.filter((w) => w.hostOnline) : all;
10643
+ if (workspaces.length === 0) {
10644
+ return {
10645
+ content: [
10646
+ {
10647
+ type: "text",
10648
+ text: onlineOnly ? "No online agent-host workspaces found. Ask the developer to start their MG Agent Host." : "No paired agent-host workspaces found."
10649
+ }
10650
+ ]
10651
+ };
10652
+ }
10653
+ const payload = workspaces.map((w) => ({
10654
+ owner: w.ownerName ?? w.ownerEmail ?? w.ownerUserId,
10655
+ online: w.hostOnline,
10656
+ host: w.hostName,
10657
+ project: w.name,
10658
+ repo: w.gitRepository,
10659
+ branch: w.gitBranch,
10660
+ defaultGitMode: w.defaultGitMode,
10661
+ path: w.path,
10662
+ lastSyncedAt: w.lastSyncedAt
10663
+ }));
10664
+ return {
10665
+ content: [
10666
+ {
10667
+ type: "text",
10668
+ text: JSON.stringify(
10669
+ {
10670
+ count: payload.length,
10671
+ onlineCount: workspaces.filter((w) => w.hostOnline).length,
10672
+ workspaces: payload,
10673
+ hint: "Use `who` (owner) + `project` (repo or project name) with cursor-remote-run. Only online workspaces can run."
10674
+ },
10675
+ null,
10676
+ 2
10677
+ )
10678
+ }
10679
+ ]
10680
+ };
10681
+ }
10682
+ case "cursor-remote-run": {
10683
+ const project = typeof a.project === "string" ? a.project.trim() : "";
10684
+ const prompt = typeof a.prompt === "string" ? a.prompt.trim() : "";
10685
+ if (!project) {
10686
+ return { content: [{ type: "text", text: "Error: project is required" }] };
10687
+ }
10688
+ if (!prompt) {
10689
+ return { content: [{ type: "text", text: "Error: prompt is required" }] };
10690
+ }
10691
+ const who = typeof a.who === "string" ? a.who.trim() : null;
10692
+ const branch = typeof a.branch === "string" ? a.branch.trim() : null;
10693
+ const model = typeof a.model === "string" ? a.model.trim() : null;
10694
+ const gitMode = a.gitMode === "in_place" ? "in_place" : "worktree";
10695
+ const resolution = await resolveRemoteTarget({ who, project, branch });
10696
+ if (!resolution.ok) {
10697
+ const describe = (c) => ` - ${c.ownerName ?? c.ownerEmail ?? c.ownerUserId} \xB7 ${c.name}${c.gitRepository ? ` (${c.gitRepository}${c.gitBranch ? `@${c.gitBranch}` : ""})` : ""}`;
10698
+ const candidates = resolution.candidates.map(describe).join("\n");
10699
+ const intro = resolution.reason === "ambiguous" ? `Multiple online workspaces match \u2014 narrow it down with \`who\` (and/or \`branch\`):` : `No online workspace matched who=${who ?? "(any)"} project="${project}". Online candidates:`;
10700
+ return {
10701
+ content: [
10702
+ {
10703
+ type: "text",
10704
+ text: `${intro}
10705
+ ${candidates || " (none online \u2014 start the MG Agent Host)"}`
10706
+ }
10707
+ ]
10708
+ };
10709
+ }
10710
+ const target = resolution.target;
10711
+ const title = prompt.split("\n")[0]?.slice(0, 80) || "Remote agent run";
10712
+ const queued = await queueAgentRun({
10713
+ workspace: {
10714
+ id: target.workspaceId,
10715
+ host_id: target.hostId,
10716
+ path: target.path,
10717
+ git_branch: target.gitBranch,
10718
+ git_repository: target.gitRepository
10719
+ },
10720
+ userId: target.ownerUserId,
10721
+ prompt,
10722
+ title,
10723
+ source: "mcp_remote",
10724
+ model,
10725
+ gitMode,
10726
+ metadata: {
10727
+ requestedBy: ctx.userId,
10728
+ who: who ?? null,
10729
+ project
10730
+ }
10731
+ });
10732
+ if (!queued) {
10733
+ return {
10734
+ content: [{ type: "text", text: "Error: failed to queue the agent run" }]
10735
+ };
10736
+ }
10737
+ const conversationUrl = `${dashboardBaseUrl}/cursor-remote?conversation=${queued.conversationId}`;
10738
+ return {
10739
+ content: [
10740
+ {
10741
+ type: "text",
10742
+ text: JSON.stringify(
10743
+ {
10744
+ status: "queued",
10745
+ owner: target.ownerName ?? target.ownerEmail ?? target.ownerUserId,
10746
+ host: target.hostName,
10747
+ project: target.name,
10748
+ repo: target.gitRepository,
10749
+ branch: target.gitBranch,
10750
+ gitMode,
10751
+ conversationId: queued.conversationId,
10752
+ runId: queued.runId,
10753
+ conversationUrl,
10754
+ note: gitMode === "worktree" ? "Runs in an isolated worktree and ends in a diff approval the owner reviews \u2014 nothing lands on their branch automatically." : "Runs in_place in the working tree (no isolation)."
10755
+ },
10756
+ null,
10757
+ 2
10758
+ )
10759
+ }
10760
+ ]
10761
+ };
10762
+ }
10427
10763
  // ----- Servers -----
10428
10764
  case "list-servers": {
10429
10765
  const data = ctx.allowedServerIds !== null ? await db.execute(sql`