@mgsoftwarebv/mg-dashboard-mcp 7.0.7 → 7.0.9

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
@@ -619,6 +619,12 @@ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
619
619
  "INTERRUPTED",
620
620
  "EXPIRED"
621
621
  ]);
622
+ var EXECUTE_PIPELINE_TASK = "execute-pipeline";
623
+ function validateExecutePipelineTestPayload(payload) {
624
+ const stepIds = payload.stepIds;
625
+ if (Array.isArray(stepIds) && stepIds.length > 0) return null;
626
+ return 'execute-pipeline requires real stepIds from a release row. Use a lightweight task (e.g. "hello-world") for connectivity checks, or pass a payload with releaseId (UUID) and non-empty stepIds.';
627
+ }
622
628
  var TRIGGER_SERVER_ID = "03659d55-e194-400d-b82a-bf6457371ded";
623
629
  var COMPOSE_PROJECT = "mg-dashboard-supabase-trigger";
624
630
  var PG_CONTAINER = `${COMPOSE_PROJECT}-postgres-1`;
@@ -678,10 +684,10 @@ var TRIGGER_TOOL_MODULE_MAP = {
678
684
  "trigger-run": "ci_cd"
679
685
  };
680
686
  async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
681
- const sql28 = `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
+ const sql29 = `SELECT re.\\"apiKey\\" FROM \\"RuntimeEnvironment\\" re JOIN \\"Project\\" p ON re.\\"projectId\\" = p.id WHERE p.slug='${projectSlug}' AND re.slug='prod' LIMIT 1`;
682
688
  const cmd = [
683
689
  `PORT=$(docker port "${WA_CONTAINER}" 3000/tcp 2>/dev/null | head -1 | sed 's/.*://')`,
684
- `KEY=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql28}" 2>/dev/null | tr -d '[:space:]')`,
690
+ `KEY=$(docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql29}" 2>/dev/null | tr -d '[:space:]')`,
685
691
  'echo "$PORT|$KEY"'
686
692
  ].join(" && ");
687
693
  const result = await sshExec2(conn, cmd, proxy);
@@ -702,8 +708,8 @@ async function discoverInstance(projectSlug, conn, proxy, sshExec2) {
702
708
  return { port, apiKey: apiKey2 };
703
709
  }
704
710
  async function fetchRunLogs(runId, conn, proxy, sshExec2) {
705
- const sql28 = `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`;
706
- const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql28}" 2>/dev/null`;
711
+ const sql29 = `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`;
712
+ const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql29}" 2>/dev/null`;
707
713
  const result = await sshExec2(conn, cmd, proxy);
708
714
  const output = result.stdout.trim();
709
715
  if (!output) return "";
@@ -785,8 +791,8 @@ async function handleTriggerTool(name, args2, deps) {
785
791
  switch (name) {
786
792
  // -----------------------------------------------------------------
787
793
  case "trigger-list": {
788
- const sql28 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
789
- const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql28}" 2>/dev/null`;
794
+ const sql29 = 'SELECT slug, name FROM \\"Project\\" ORDER BY name';
795
+ const cmd = `docker exec "${PG_CONTAINER}" psql -U postgres -d main -t -A -c "${sql29}" 2>/dev/null`;
790
796
  const result = await sshExec2(conn, cmd, proxy);
791
797
  const output = result.stdout.trim();
792
798
  if (!output) {
@@ -867,8 +873,15 @@ ${rawJson.substring(0, 500)}` }] };
867
873
  throw new Error(`Invalid JSON payload: ${String(args2.payload).substring(0, 200)}`);
868
874
  }
869
875
  }
876
+ const parsedPayload = JSON.parse(payload);
877
+ if (taskId === EXECUTE_PIPELINE_TASK) {
878
+ const validationError = validateExecutePipelineTestPayload(parsedPayload);
879
+ if (validationError) {
880
+ return { content: [{ type: "text", text: `Error: ${validationError}` }] };
881
+ }
882
+ }
870
883
  const triggerBody = JSON.stringify({
871
- payload: JSON.parse(payload),
884
+ payload: parsedPayload,
872
885
  options: { tags: ["mcp-test"], test: true }
873
886
  });
874
887
  const triggerJson = await triggerApi(
@@ -944,9 +957,9 @@ async function fetchAndFormatRun(conn, proxy, sshExec2, instance, runId) {
944
957
  return { content: [{ type: "text", text: `Invalid API response:
945
958
  ${rawJson.substring(0, 500)}` }] };
946
959
  }
947
- let text6 = formatRunDetail(run);
948
- if (logs) text6 += "\n\n--- Logs ---\n" + logs;
949
- return { content: [{ type: "text", text: text6 }] };
960
+ let text7 = formatRunDetail(run);
961
+ if (logs) text7 += "\n\n--- Logs ---\n" + logs;
962
+ return { content: [{ type: "text", text: text7 }] };
950
963
  }
951
964
  async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSeconds) {
952
965
  const pollInterval = 3e3;
@@ -968,10 +981,10 @@ async function waitForCompletion(conn, proxy, sshExec2, instance, runId, waitSec
968
981
  continue;
969
982
  }
970
983
  if (TERMINAL_STATUSES.has(run.status)) {
971
- let text6 = formatRunDetail(run);
984
+ let text7 = formatRunDetail(run);
972
985
  const logs = await fetchRunLogs(runId, conn, proxy, sshExec2);
973
- if (logs) text6 += "\n\n--- Logs ---\n" + logs;
974
- return { content: [{ type: "text", text: text6 }] };
986
+ if (logs) text7 += "\n\n--- Logs ---\n" + logs;
987
+ return { content: [{ type: "text", text: text7 }] };
975
988
  }
976
989
  }
977
990
  return {
@@ -3658,10 +3671,10 @@ var ZodObject = class _ZodObject extends ZodType {
3658
3671
  // }) as any;
3659
3672
  // return merged;
3660
3673
  // }
3661
- catchall(index4) {
3674
+ catchall(index5) {
3662
3675
  return new _ZodObject({
3663
3676
  ...this._def,
3664
- catchall: index4
3677
+ catchall: index5
3665
3678
  });
3666
3679
  }
3667
3680
  pick(mask) {
@@ -3979,9 +3992,9 @@ function mergeValues(a, b) {
3979
3992
  return { valid: false };
3980
3993
  }
3981
3994
  const newArray = [];
3982
- for (let index4 = 0; index4 < a.length; index4++) {
3983
- const itemA = a[index4];
3984
- const itemB = b[index4];
3995
+ for (let index5 = 0; index5 < a.length; index5++) {
3996
+ const itemA = a[index5];
3997
+ const itemB = b[index5];
3985
3998
  const sharedValue = mergeValues(itemA, itemB);
3986
3999
  if (!sharedValue.valid) {
3987
4000
  return { valid: false };
@@ -4187,10 +4200,10 @@ var ZodMap = class extends ZodType {
4187
4200
  }
4188
4201
  const keyType = this._def.keyType;
4189
4202
  const valueType = this._def.valueType;
4190
- const pairs = [...ctx.data.entries()].map(([key, value], index4) => {
4203
+ const pairs = [...ctx.data.entries()].map(([key, value], index5) => {
4191
4204
  return {
4192
- key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index4, "key"])),
4193
- value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index4, "value"]))
4205
+ key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index5, "key"])),
4206
+ value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index5, "value"]))
4194
4207
  };
4195
4208
  });
4196
4209
  if (ctx.common.async) {
@@ -5716,7 +5729,30 @@ pgTable(
5716
5729
  index("github_token_owner_idx").on(table.owner)
5717
5730
  ]
5718
5731
  );
5732
+ pgTable(
5733
+ "github_webhook_secret",
5734
+ {
5735
+ id: uuid("id").primaryKey().defaultRandom(),
5736
+ label: text("label").notNull(),
5737
+ owner: text("owner"),
5738
+ notes: text("notes"),
5739
+ secretEncrypted: text("secret_encrypted").notNull(),
5740
+ enabled: boolean("enabled").notNull().default(true),
5741
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
5742
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
5743
+ },
5744
+ (table) => [
5745
+ index("github_webhook_secret_label_idx").on(table.label),
5746
+ index("github_webhook_secret_owner_idx").on(table.owner)
5747
+ ]
5748
+ );
5749
+ function assertPipelinePayload(payload) {
5750
+ if (!payload.stepIds?.length) {
5751
+ throw new Error("Cannot trigger execute-pipeline: stepIds is empty");
5752
+ }
5753
+ }
5719
5754
  async function triggerPipeline(payload) {
5755
+ assertPipelinePayload(payload);
5720
5756
  return tasks.trigger(
5721
5757
  "execute-pipeline",
5722
5758
  payload
@@ -6168,6 +6204,24 @@ async function createReleaseForStage(userId, params) {
6168
6204
  if (!triggerAuthorName && userId) {
6169
6205
  triggerAuthorName = await resolveUserDisplayName(userId);
6170
6206
  }
6207
+ const enabledSteps = buildStepList(
6208
+ stage,
6209
+ profile,
6210
+ await resolveBuildStepListOptions({
6211
+ profile,
6212
+ triggerSha,
6213
+ githubToken: token,
6214
+ isManual: triggerType === "manual" || triggerType === "mcp",
6215
+ stageId: params.stageId,
6216
+ deployRetryScripts: stage.deploy_retry_scripts
6217
+ })
6218
+ );
6219
+ if (enabledSteps.length === 0) {
6220
+ throw new CreateReleaseError(
6221
+ "No pipeline steps configured for this stage (enable a PM2/Docker stage_app or deployment_project_server)",
6222
+ "PRECONDITION_FAILED"
6223
+ );
6224
+ }
6171
6225
  const initialStatus = "running";
6172
6226
  let release;
6173
6227
  try {
@@ -6201,18 +6255,6 @@ async function createReleaseForStage(userId, params) {
6201
6255
  "INTERNAL_SERVER_ERROR"
6202
6256
  );
6203
6257
  }
6204
- const enabledSteps = buildStepList(
6205
- stage,
6206
- profile,
6207
- await resolveBuildStepListOptions({
6208
- profile,
6209
- triggerSha,
6210
- githubToken: token,
6211
- isManual: triggerType === "manual" || triggerType === "mcp",
6212
- stageId: params.stageId,
6213
- deployRetryScripts: stage.deploy_retry_scripts
6214
- })
6215
- );
6216
6258
  const stepIds = [];
6217
6259
  try {
6218
6260
  stepIds.push(...await insertReleaseSteps(release.id, enabledSteps));
@@ -6717,6 +6759,7 @@ var apiKey = getArg2("api-key") || process.env.MG_DASHBOARD_API_KEY;
6717
6759
  var sshKeyPath = getArg2("ssh-key") || process.env.MG_DASHBOARD_SSH_KEY;
6718
6760
  var databaseUrl = getArg2("database-url") || process.env.DATABASE_PRIMARY_POOLER_URL || process.env.DATABASE_PRIMARY_URL;
6719
6761
  var encryptionKey = getArg2("encryption-key") || process.env.ENCRYPTION_KEY;
6762
+ var dashboardBaseUrl = (getArg2("dashboard-url") || process.env.MG_DASHBOARD_BASE_URL || "https://dashboard.mgsoftware.nl").replace(/\/$/, "");
6720
6763
  var mijnhostApiKey = getArg2("mijnhost-api-key") || process.env.MIJNHOST_API_KEY;
6721
6764
  function isMijnhostViaSshProxyEnabled() {
6722
6765
  const arg = getArg2("mijnhost-via-ssh-proxy");
@@ -7435,7 +7478,7 @@ function getEncryptionKey() {
7435
7478
  throw new Error("ENCRYPTION_KEY must be a 64-character hex string");
7436
7479
  return buf;
7437
7480
  }
7438
- function encrypt(text6) {
7481
+ function encrypt(text7) {
7439
7482
  const key = getEncryptionKey();
7440
7483
  const iv = randomBytes(ENC_IV_LENGTH);
7441
7484
  const cipher = createCipheriv(
@@ -7443,7 +7486,7 @@ function encrypt(text6) {
7443
7486
  new Uint8Array(key),
7444
7487
  new Uint8Array(iv)
7445
7488
  );
7446
- let encrypted = cipher.update(text6, "utf8", "hex");
7489
+ let encrypted = cipher.update(text7, "utf8", "hex");
7447
7490
  encrypted += cipher.final("hex");
7448
7491
  const authTag = cipher.getAuthTag();
7449
7492
  return Buffer.concat([
@@ -8163,10 +8206,10 @@ async function r2GetObjectRange(bucket, key, range) {
8163
8206
  const body = result.Body;
8164
8207
  if (!body?.transformToString)
8165
8208
  throw new Error("R2 returned no readable body");
8166
- const text6 = await body.transformToString();
8209
+ const text7 = await body.transformToString();
8167
8210
  const header = `# range: bytes ${range.offset}-${end} of ${size} (${effectiveLen} bytes)`;
8168
8211
  return `${header}
8169
- ${text6}`;
8212
+ ${text7}`;
8170
8213
  } catch (e) {
8171
8214
  throw r2WrapError(bucket, key, e);
8172
8215
  }
@@ -8529,15 +8572,15 @@ async function sftpRead(opts, filePath, proxy, options) {
8529
8572
  clearTimeout(timer);
8530
8573
  cleanup?.();
8531
8574
  cleanup = void 0;
8532
- const text6 = Buffer.concat(
8575
+ const text7 = Buffer.concat(
8533
8576
  chunks.map((ch) => new Uint8Array(ch))
8534
8577
  ).toString("utf-8");
8535
8578
  if (!isWholeFileRequest) {
8536
8579
  const header = `# range: bytes ${offset}-${offset + effectiveLen - 1} of ${total} (${effectiveLen} bytes)`;
8537
8580
  resolve(`${header}
8538
- ${text6}`);
8581
+ ${text7}`);
8539
8582
  } else {
8540
- resolve(text6);
8583
+ resolve(text7);
8541
8584
  }
8542
8585
  });
8543
8586
  rs.on("error", (e) => {
@@ -8667,11 +8710,11 @@ function getKnownContainers(serverId, maxAgeMs = 5 * 6e4) {
8667
8710
  if (Date.now() - e.capturedAt > maxAgeMs) return void 0;
8668
8711
  return e.names;
8669
8712
  }
8670
- function truncateForLLM(text6, maxBytes) {
8671
- const totalBytes = Buffer.byteLength(text6, "utf8");
8713
+ function truncateForLLM(text7, maxBytes) {
8714
+ const totalBytes = Buffer.byteLength(text7, "utf8");
8672
8715
  if (totalBytes <= maxBytes)
8673
- return { text: text6, truncated: false, totalBytes, shownBytes: totalBytes };
8674
- const buf = Buffer.from(text6, "utf8");
8716
+ return { text: text7, truncated: false, totalBytes, shownBytes: totalBytes };
8717
+ const buf = Buffer.from(text7, "utf8");
8675
8718
  let cut = maxBytes;
8676
8719
  while (cut > 0 && (buf[cut] & 192) === 128) cut--;
8677
8720
  const head = buf.subarray(0, cut).toString("utf8");
@@ -8701,10 +8744,10 @@ function isTransientSshError(stderr, exitCode) {
8701
8744
  function postprocessResult(result, meta) {
8702
8745
  if (!result.content?.length) return result;
8703
8746
  const block = result.content[0];
8704
- let text6 = String(block.text ?? "");
8705
- const trunc = truncateForLLM(text6, RESPONSE_MAX_BYTES);
8747
+ let text7 = String(block.text ?? "");
8748
+ const trunc = truncateForLLM(text7, RESPONSE_MAX_BYTES);
8706
8749
  if (trunc.truncated) {
8707
- text6 = trunc.text + "\n\n... " + buildTruncationHint(
8750
+ text7 = trunc.text + "\n\n... " + buildTruncationHint(
8708
8751
  meta.toolName,
8709
8752
  meta.args,
8710
8753
  trunc.totalBytes,
@@ -8718,11 +8761,11 @@ function postprocessResult(result, meta) {
8718
8761
  const parts = [`took ${tookStr}`, sizeStr];
8719
8762
  if (meta.serverIdLabel) parts.push(`server: ${meta.serverIdLabel}`);
8720
8763
  if (meta.cached) parts.push("cached");
8721
- text6 = `${text6}
8764
+ text7 = `${text7}
8722
8765
 
8723
8766
  [${parts.join(", ")}]`;
8724
8767
  }
8725
- return { ...result, content: [{ ...block, text: text6 }] };
8768
+ return { ...result, content: [{ ...block, text: text7 }] };
8726
8769
  }
8727
8770
  function buildPipelineScript(commands, shell, marker, stopOnError) {
8728
8771
  if (shell === "powershell") {
@@ -9508,11 +9551,11 @@ CREATE TABLE IF NOT EXISTS _mcp_migrations (
9508
9551
  applied_by TEXT
9509
9552
  );
9510
9553
  `.trim();
9511
- function normaliseMigrationSql(sql28) {
9512
- return sql28.replace(/\r\n/g, "\n").trim() + "\n";
9554
+ function normaliseMigrationSql(sql29) {
9555
+ return sql29.replace(/\r\n/g, "\n").trim() + "\n";
9513
9556
  }
9514
- function migrationSha256(sql28) {
9515
- return createHash("sha256").update(normaliseMigrationSql(sql28), "utf8").digest("hex");
9557
+ function migrationSha256(sql29) {
9558
+ return createHash("sha256").update(normaliseMigrationSql(sql29), "utf8").digest("hex");
9516
9559
  }
9517
9560
  function dollarQuoteTag(value) {
9518
9561
  let tag = "_mcp";
@@ -10748,6 +10791,34 @@ var TOOLS = [
10748
10791
  required: ["action", "domain", "type", "name"]
10749
10792
  }
10750
10793
  },
10794
+ // ----- Team memory -----
10795
+ {
10796
+ name: "search-team-memory",
10797
+ description: "Search the team's ENTIRE past Cursor history (every developer, every project) for how something was handled before. Use this FIRST, before investigating from scratch, whenever you: hit a non-trivial bug or error, are about to build something that may have been done before, need a project-specific convention/gotcha, or the user asks 'have we done X', 'how did we fix Y', or 'did we solve this already'. Hybrid semantic + keyword search over mirrored conversations. Returns ranked past chats with repo, date, a solution snippet and a similarity score. Phrase the query as the problem in natural language (e.g. 'release pipeline PM2 deploy fails with module not found').",
10798
+ inputSchema: {
10799
+ type: "object",
10800
+ properties: {
10801
+ query: {
10802
+ type: "string",
10803
+ description: "The problem/topic in natural language. Include error text, tool/module names, or symptoms for best recall."
10804
+ },
10805
+ repo: {
10806
+ type: "string",
10807
+ description: "Optional repo filter, e.g. 'github.com/MGSoftwareBV/mg-dashboard'. Omit to search across all projects."
10808
+ },
10809
+ scope: {
10810
+ type: "string",
10811
+ enum: ["team", "mine"],
10812
+ description: "'team' (default) searches everyone's history; 'mine' restricts to the calling user's own conversations."
10813
+ },
10814
+ limit: {
10815
+ type: "number",
10816
+ description: "Max results to return (1-25, default 8)."
10817
+ }
10818
+ },
10819
+ required: ["query"]
10820
+ }
10821
+ },
10751
10822
  // ----- Trigger.dev -----
10752
10823
  ...TRIGGER_TOOLS
10753
10824
  ];
@@ -10819,6 +10890,65 @@ async function executeToolCall(name, a, _serverId) {
10819
10890
  const ctx = authContext;
10820
10891
  try {
10821
10892
  switch (name) {
10893
+ // ----- Team memory -----
10894
+ case "search-team-memory": {
10895
+ const query = typeof a.query === "string" ? a.query.trim() : "";
10896
+ if (!query) {
10897
+ return { content: [{ type: "text", text: "Error: query is required" }] };
10898
+ }
10899
+ const res = await fetch(`${dashboardBaseUrl}/api/team-memory/search`, {
10900
+ method: "POST",
10901
+ headers: {
10902
+ "content-type": "application/json",
10903
+ authorization: `Bearer ${apiKey}`
10904
+ },
10905
+ body: JSON.stringify({
10906
+ query,
10907
+ repo: typeof a.repo === "string" ? a.repo : void 0,
10908
+ scope: a.scope === "mine" ? "mine" : "team",
10909
+ limit: typeof a.limit === "number" ? a.limit : void 0
10910
+ })
10911
+ });
10912
+ if (!res.ok) {
10913
+ const detail = await res.text().catch(() => "");
10914
+ return {
10915
+ content: [
10916
+ {
10917
+ type: "text",
10918
+ text: `Error: team-memory search failed (${res.status}). ${detail.slice(0, 300)}`
10919
+ }
10920
+ ]
10921
+ };
10922
+ }
10923
+ const data = await res.json();
10924
+ if (data.count === 0) {
10925
+ return {
10926
+ content: [
10927
+ {
10928
+ type: "text",
10929
+ text: `No prior team conversations matched "${query}". Proceed from scratch.`
10930
+ }
10931
+ ]
10932
+ };
10933
+ }
10934
+ const lines = data.hits.map((hit, index5) => {
10935
+ const when = hit.lastMessageAt ? new Date(hit.lastMessageAt).toISOString().slice(0, 10) : "unknown date";
10936
+ const repo = hit.repo ?? "unknown repo";
10937
+ const sim = hit.similarity !== null ? ` \xB7 ${(hit.similarity * 100).toFixed(0)}% match` : "";
10938
+ return `${index5 + 1}. [${repo} \xB7 ${when}${sim}] ${hit.title}
10939
+ ${(hit.summary ?? hit.snippet).replace(/\s+/g, " ").slice(0, 500)}`;
10940
+ });
10941
+ return {
10942
+ content: [
10943
+ {
10944
+ type: "text",
10945
+ text: `Found ${data.count} prior conversation(s) (${data.mode} search) \u2014 reuse this before solving from scratch:
10946
+
10947
+ ` + lines.join("\n\n")
10948
+ }
10949
+ ]
10950
+ };
10951
+ }
10822
10952
  // ----- Servers -----
10823
10953
  case "list-servers": {
10824
10954
  const data = ctx.allowedServerIds !== null ? await db.execute(sql`
@@ -11777,8 +11907,8 @@ ${sample.join("\n")}${files.length > 5 ? `
11777
11907
  };
11778
11908
  const filtered = sortRows(applyFilter(only.rows));
11779
11909
  if (format === "json") {
11780
- const text7 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
11781
- return { content: [{ type: "text", text: text7 }] };
11910
+ const text8 = filtered.map((c) => JSON.stringify(c)).join("\n") || "(no containers)";
11911
+ return { content: [{ type: "text", text: text8 }] };
11782
11912
  }
11783
11913
  if (groupByProject) {
11784
11914
  const groups = /* @__PURE__ */ new Map();
@@ -11805,8 +11935,8 @@ ${sample.join("\n")}${files.length > 5 ? `
11805
11935
  }
11806
11936
  const header = `${"NAMES".padEnd(36)} ${"IMAGE".padEnd(40)} ${"STATUS".padEnd(24)} ${"HEALTH".padEnd(10)} ${"PORTS".padEnd(40)} PROJECT`;
11807
11937
  const body = filtered.map((r) => `${fmtRow(r)} ${r.Project || ""}`);
11808
- const text6 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
11809
- return { content: [{ type: "text", text: text6 }] };
11938
+ const text7 = filtered.length === 0 ? "(no containers match)" : [header, ...body].join("\n");
11939
+ return { content: [{ type: "text", text: text7 }] };
11810
11940
  }
11811
11941
  if (format === "json") {
11812
11942
  const lines = [];