From 20b79f1be7dc7cb2352dfa875ea20c0cb7f7f93a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 06:21:56 +0000
Subject: [PATCH 01/10] Compact MCP CLI help to fit first 20 lines

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs      | 77 +++++++++++++++++-------
 actions/setup/js/mcp_cli_bridge.test.cjs | 38 +++++++++++-
 2 files changed, 93 insertions(+), 22 deletions(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index 0ec0fc6826a..56c2393ec29 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -54,6 +54,9 @@ const KEEPALIVE_PING_INTERVAL_MS = 10000;
 /** Starting JSON-RPC ID for keepalive ping requests */
 const KEEPALIVE_PING_ID_START = 1000;
 
+/** Preferred max lines for generated CLI help output */
+const HELP_MAX_LINES = 20;
+
 // ---------------------------------------------------------------------------
 // Audit logging
 // ---------------------------------------------------------------------------
@@ -763,20 +766,27 @@ function loadTools(toolsFile) {
  * @param {Array<{name: string, description?: string}>} tools - Tool list
  */
 function showHelp(serverName, tools) {
-  const lines = [`Usage: ${serverName} <command> [options]`, ""];
-  lines.push("Available commands:");
+  const lines = [
+    `Usage: ${serverName} <command> [--param value ...]`,
+    `Tip: ${serverName} <command> --help`,
+    "",
+    `Commands (${tools.length}):`,
+  ];
   if (tools.length > 0) {
-    // Calculate column width for aligned output
-    const maxNameLen = Math.max(...tools.map(t => t.name.length));
-    for (const tool of tools) {
-      const padded = tool.name.padEnd(maxNameLen + 2);
-      lines.push(`  ${padded}${tool.description || "No description"}`);
+    const reservedLines = 3; // blank + footer + potential overflow marker
+    const maxCommandLines = Math.max(1, HELP_MAX_LINES - lines.length - reservedLines);
+    const shownTools = tools.slice(0, maxCommandLines);
+    for (const tool of shownTools) {
+      lines.push(`  ${tool.name} — ${summarizeHelpText(tool.description || "No description", 68)}`);
+    }
+    if (tools.length > shownTools.length) {
+      lines.push(`  ... +${tools.length - shownTools.length} more command(s)`);
     }
   } else {
     lines.push("  (tool list unavailable)");
   }
   lines.push("");
-  lines.push(`Run '${serverName} <command> --help' for more information on a command.`);
+  lines.push(`Run '${serverName} <command> --help' for option details.`);
   process.stdout.write(lines.join("\n") + "\n");
 }
 
@@ -796,26 +806,32 @@ function showToolHelp(serverName, toolName, tools) {
     return;
   }
 
-  const lines = [`Command: ${toolName}`, `Description: ${tool.description || "No description"}`];
+  const lines = [
+    `Command: ${toolName}`,
+    `Description: ${summarizeHelpText(tool.description || "No description", 90)}`,
+    `Usage: ${serverName} ${toolName} [--param value ...]`,
+    `JSON mode: printf '{"param":"value",...}' | ${serverName} ${toolName} .`,
+  ];
 
   const props = tool.inputSchema?.properties;
   const required = new Set(tool.inputSchema?.required || []);
   if (props && Object.keys(props).length > 0) {
     lines.push("");
-    lines.push("Options:");
-    const maxKeyLen = Math.max(...Object.keys(props).map(k => k.length));
-    for (const [key, val] of Object.entries(props)) {
-      const flagPad = `--${key}`.padEnd(maxKeyLen + 4);
-      const parts = [getTypeStr(val.type)];
-      if (required.has(key)) parts.push("(required)");
-      if (val.description) parts.push(val.description);
-      lines.push(`  ${flagPad}${parts.join(" ")}`);
+    lines.push(`Options (${Object.keys(props).length}):`);
+    const optionEntries = Object.entries(props);
+    const reservedLines = 2; // footer + potential overflow marker
+    const maxOptionLines = Math.max(1, HELP_MAX_LINES - lines.length - reservedLines);
+    const shownOptions = optionEntries.slice(0, maxOptionLines);
+    for (const [key, val] of shownOptions) {
+      const requiredMark = required.has(key) ? "*" : "";
+      const description = val.description ? ` - ${summarizeHelpText(val.description, 62)}` : "";
+      lines.push(`  --${key} ${getTypeStr(val.type)}${requiredMark}${description}`);
+    }
+    if (optionEntries.length > shownOptions.length) {
+      lines.push(`  ... +${optionEntries.length - shownOptions.length} more option(s)`);
     }
-
-    lines.push("");
-    lines.push(`Usage: ${serverName} ${toolName} [--param value ...]`);
-    lines.push(`  or:  printf '{"param":"value",...}' | ${serverName} ${toolName} .`);
   }
+  lines.push("Required options are marked with *.");
 
   process.stdout.write(lines.join("\n") + "\n");
 }
@@ -832,6 +848,23 @@ function getTypeStr(type) {
   return `(${types.length > 0 ? types.join("|") : "null"})`;
 }
 
+/**
+ * Collapse whitespace and trim long help text for compact output.
+ *
+ * @param {string} value
+ * @param {number} maxLen
+ * @returns {string}
+ */
+function summarizeHelpText(value, maxLen) {
+  const normalized = String(value || "")
+    .replace(/\s+/g, " ")
+    .trim();
+  if (normalized.length <= maxLen) {
+    return normalized;
+  }
+  return `${normalized.slice(0, Math.max(0, maxLen - 1))}…`;
+}
+
 // ---------------------------------------------------------------------------
 // Response formatting
 // ---------------------------------------------------------------------------
@@ -1130,6 +1163,8 @@ module.exports = {
   extractJSONRPCMessages,
   renderProgressMessages,
   formatResponse,
+  showHelp,
+  showToolHelp,
   hasStdinJsonPayload,
   readStdinSync,
   main,
diff --git a/actions/setup/js/mcp_cli_bridge.test.cjs b/actions/setup/js/mcp_cli_bridge.test.cjs
index 89476c12846..73808e0e59d 100644
--- a/actions/setup/js/mcp_cli_bridge.test.cjs
+++ b/actions/setup/js/mcp_cli_bridge.test.cjs
@@ -1,6 +1,6 @@
 import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
 
-import { formatResponse, hasStdinJsonPayload, parseToolArgs, readStdinSync } from "./mcp_cli_bridge.cjs";
+import { formatResponse, hasStdinJsonPayload, parseToolArgs, readStdinSync, showHelp, showToolHelp } from "./mcp_cli_bridge.cjs";
 
 describe("mcp_cli_bridge.cjs", () => {
   let originalCore;
@@ -212,6 +212,42 @@ describe("mcp_cli_bridge.cjs", () => {
     expect(process.exitCode).toBe(0);
   });
 
+  it("keeps top-level help compact for many commands", () => {
+    const tools = Array.from({ length: 25 }, (_, i) => ({
+      name: `tool_${i + 1}`,
+      description: `Description for command ${i + 1} that is intentionally verbose for truncation checks.`,
+    }));
+
+    showHelp("safeoutputs", tools);
+
+    const outputLines = stdoutChunks.join("").trimEnd().split("\n");
+    expect(outputLines.length).toBeLessThanOrEqual(20);
+    expect(outputLines.join("\n")).toContain("... +");
+  });
+
+  it("keeps command help compact for many options", () => {
+    const properties = {};
+    for (let i = 1; i <= 24; i++) {
+      properties[`field_${i}`] = { type: "string", description: `Field ${i} description with additional details for truncation.` };
+    }
+
+    showToolHelp("safeoutputs", "create_issue", [
+      {
+        name: "create_issue",
+        description: "Create an issue with many available fields and optional metadata.",
+        inputSchema: {
+          properties,
+          required: ["field_1", "field_2"],
+        },
+      },
+    ]);
+
+    const outputLines = stdoutChunks.join("").trimEnd().split("\n");
+    expect(outputLines.length).toBeLessThanOrEqual(20);
+    expect(outputLines.join("\n")).toContain("... +");
+    expect(outputLines.join("\n")).toContain("Required options are marked with *.");
+  });
+
   describe("stdin placeholder removed — '-' is always a literal value", () => {
     it("passes '--key -' as literal '-' (space-separated form)", () => {
       const schemaProperties = { body: { type: "string" } };

From a634c231c63b625f7a2188c9e070ee4758228e46 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 06:22:48 +0000
Subject: [PATCH 02/10] Show required-marker note only when options exist

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index 56c2393ec29..fcb67c0ac90 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -830,8 +830,8 @@ function showToolHelp(serverName, toolName, tools) {
     if (optionEntries.length > shownOptions.length) {
       lines.push(`  ... +${optionEntries.length - shownOptions.length} more option(s)`);
     }
+    lines.push("Required options are marked with *.");
   }
-  lines.push("Required options are marked with *.");
 
   process.stdout.write(lines.join("\n") + "\n");
 }

From 064789dd401b47ca8b7925c02c1bbdd6dca6d24a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 06:23:37 +0000
Subject: [PATCH 03/10] Harden help truncation and overflow assertions

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs      | 8 +++++++-
 actions/setup/js/mcp_cli_bridge.test.cjs | 4 ++--
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index fcb67c0ac90..ccda70274ea 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -856,13 +856,19 @@ function getTypeStr(type) {
  * @returns {string}
  */
 function summarizeHelpText(value, maxLen) {
+  if (!Number.isFinite(maxLen) || maxLen <= 0) {
+    return "";
+  }
   const normalized = String(value || "")
     .replace(/\s+/g, " ")
     .trim();
   if (normalized.length <= maxLen) {
     return normalized;
   }
-  return `${normalized.slice(0, Math.max(0, maxLen - 1))}…`;
+  if (maxLen === 1) {
+    return "…";
+  }
+  return `${normalized.slice(0, maxLen - 1)}…`;
 }
 
 // ---------------------------------------------------------------------------
diff --git a/actions/setup/js/mcp_cli_bridge.test.cjs b/actions/setup/js/mcp_cli_bridge.test.cjs
index 73808e0e59d..7319d3a2c93 100644
--- a/actions/setup/js/mcp_cli_bridge.test.cjs
+++ b/actions/setup/js/mcp_cli_bridge.test.cjs
@@ -222,7 +222,7 @@ describe("mcp_cli_bridge.cjs", () => {
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
     expect(outputLines.length).toBeLessThanOrEqual(20);
-    expect(outputLines.join("\n")).toContain("... +");
+    expect(outputLines.join("\n")).toMatch(/\.\.\. \+\d+ more command\(s\)/);
   });
 
   it("keeps command help compact for many options", () => {
@@ -244,7 +244,7 @@ describe("mcp_cli_bridge.cjs", () => {
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
     expect(outputLines.length).toBeLessThanOrEqual(20);
-    expect(outputLines.join("\n")).toContain("... +");
+    expect(outputLines.join("\n")).toMatch(/\.\.\. \+\d+ more option\(s\)/);
     expect(outputLines.join("\n")).toContain("Required options are marked with *.");
   });
 

From a1dd5048d561f50c99c6080da99bb6f0e767f420 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 06:24:19 +0000
Subject: [PATCH 04/10] Extract help text length constants

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index ccda70274ea..693635a2b39 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -56,6 +56,9 @@ const KEEPALIVE_PING_ID_START = 1000;
 
 /** Preferred max lines for generated CLI help output */
 const HELP_MAX_LINES = 20;
+const COMMAND_DESC_MAX_LEN = 68;
+const TOOL_DESC_MAX_LEN = 90;
+const OPTION_DESC_MAX_LEN = 62;
 
 // ---------------------------------------------------------------------------
 // Audit logging
@@ -777,7 +780,7 @@ function showHelp(serverName, tools) {
     const maxCommandLines = Math.max(1, HELP_MAX_LINES - lines.length - reservedLines);
     const shownTools = tools.slice(0, maxCommandLines);
     for (const tool of shownTools) {
-      lines.push(`  ${tool.name} — ${summarizeHelpText(tool.description || "No description", 68)}`);
+      lines.push(`  ${tool.name} — ${summarizeHelpText(tool.description || "No description", COMMAND_DESC_MAX_LEN)}`);
     }
     if (tools.length > shownTools.length) {
       lines.push(`  ... +${tools.length - shownTools.length} more command(s)`);
@@ -808,7 +811,7 @@ function showToolHelp(serverName, toolName, tools) {
 
   const lines = [
     `Command: ${toolName}`,
-    `Description: ${summarizeHelpText(tool.description || "No description", 90)}`,
+    `Description: ${summarizeHelpText(tool.description || "No description", TOOL_DESC_MAX_LEN)}`,
     `Usage: ${serverName} ${toolName} [--param value ...]`,
     `JSON mode: printf '{"param":"value",...}' | ${serverName} ${toolName} .`,
   ];
@@ -824,7 +827,7 @@ function showToolHelp(serverName, toolName, tools) {
     const shownOptions = optionEntries.slice(0, maxOptionLines);
     for (const [key, val] of shownOptions) {
       const requiredMark = required.has(key) ? "*" : "";
-      const description = val.description ? ` - ${summarizeHelpText(val.description, 62)}` : "";
+      const description = val.description ? ` - ${summarizeHelpText(val.description, OPTION_DESC_MAX_LEN)}` : "";
       lines.push(`  --${key} ${getTypeStr(val.type)}${requiredMark}${description}`);
     }
     if (optionEntries.length > shownOptions.length) {
@@ -856,17 +859,14 @@ function getTypeStr(type) {
  * @returns {string}
  */
 function summarizeHelpText(value, maxLen) {
-  if (!Number.isFinite(maxLen) || maxLen <= 0) {
-    return "";
-  }
   const normalized = String(value || "")
     .replace(/\s+/g, " ")
     .trim();
-  if (normalized.length <= maxLen) {
+  if (!Number.isFinite(maxLen) || maxLen <= 0) {
     return normalized;
   }
-  if (maxLen === 1) {
-    return "…";
+  if (normalized.length <= maxLen) {
+    return normalized;
   }
   return `${normalized.slice(0, maxLen - 1)}…`;
 }

From c538c02044e685cfc03ec629df3992dfe8f49253 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 06:24:55 +0000
Subject: [PATCH 05/10] Extract help reserved-line constants

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index 693635a2b39..50d779613e7 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -59,6 +59,8 @@ const HELP_MAX_LINES = 20;
 const COMMAND_DESC_MAX_LEN = 68;
 const TOOL_DESC_MAX_LEN = 90;
 const OPTION_DESC_MAX_LEN = 62;
+const TOP_HELP_RESERVED_LINES = 3;
+const TOOL_HELP_RESERVED_LINES = 2;
 
 // ---------------------------------------------------------------------------
 // Audit logging
@@ -776,8 +778,7 @@ function showHelp(serverName, tools) {
     `Commands (${tools.length}):`,
   ];
   if (tools.length > 0) {
-    const reservedLines = 3; // blank + footer + potential overflow marker
-    const maxCommandLines = Math.max(1, HELP_MAX_LINES - lines.length - reservedLines);
+    const maxCommandLines = Math.max(1, HELP_MAX_LINES - lines.length - TOP_HELP_RESERVED_LINES);
     const shownTools = tools.slice(0, maxCommandLines);
     for (const tool of shownTools) {
       lines.push(`  ${tool.name} — ${summarizeHelpText(tool.description || "No description", COMMAND_DESC_MAX_LEN)}`);
@@ -822,8 +823,7 @@ function showToolHelp(serverName, toolName, tools) {
     lines.push("");
     lines.push(`Options (${Object.keys(props).length}):`);
     const optionEntries = Object.entries(props);
-    const reservedLines = 2; // footer + potential overflow marker
-    const maxOptionLines = Math.max(1, HELP_MAX_LINES - lines.length - reservedLines);
+    const maxOptionLines = Math.max(1, HELP_MAX_LINES - lines.length - TOOL_HELP_RESERVED_LINES);
     const shownOptions = optionEntries.slice(0, maxOptionLines);
     for (const [key, val] of shownOptions) {
       const requiredMark = required.has(key) ? "*" : "";

From d099bf796f859802a1cd25cf77dd887633a4d6e3 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 06:25:31 +0000
Subject: [PATCH 06/10] Show required-option marker only when needed

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index 50d779613e7..2a57b92e459 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -833,7 +833,9 @@ function showToolHelp(serverName, toolName, tools) {
     if (optionEntries.length > shownOptions.length) {
       lines.push(`  ... +${optionEntries.length - shownOptions.length} more option(s)`);
     }
-    lines.push("Required options are marked with *.");
+    if (required.size > 0) {
+      lines.push("Required options are marked with *.");
+    }
   }
 
   process.stdout.write(lines.join("\n") + "\n");

From f2734ea546e21ac86c5d0a76562bb59f427b8234 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 11:53:21 +0000
Subject: [PATCH 07/10] Fix help-line budget edge cases and add exact-fit tests

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs      | 32 +++++++------
 actions/setup/js/mcp_cli_bridge.test.cjs | 60 ++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 14 deletions(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index 2a57b92e459..436dc942cc5 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -59,8 +59,7 @@ const HELP_MAX_LINES = 20;
 const COMMAND_DESC_MAX_LEN = 68;
 const TOOL_DESC_MAX_LEN = 90;
 const OPTION_DESC_MAX_LEN = 62;
-const TOP_HELP_RESERVED_LINES = 3;
-const TOOL_HELP_RESERVED_LINES = 2;
+const TOP_HELP_FOOTER_LINES = 2;
 
 // ---------------------------------------------------------------------------
 // Audit logging
@@ -771,15 +770,13 @@ function loadTools(toolsFile) {
  * @param {Array<{name: string, description?: string}>} tools - Tool list
  */
 function showHelp(serverName, tools) {
-  const lines = [
-    `Usage: ${serverName} <command> [--param value ...]`,
-    `Tip: ${serverName} <command> --help`,
-    "",
-    `Commands (${tools.length}):`,
-  ];
+  const lines = [`Usage: ${serverName} <command> [--param value ...]`, `Tip: ${serverName} <command> --help`, "", `Commands (${tools.length}):`];
   if (tools.length > 0) {
-    const maxCommandLines = Math.max(1, HELP_MAX_LINES - lines.length - TOP_HELP_RESERVED_LINES);
-    const shownTools = tools.slice(0, maxCommandLines);
+    const maxCommandLines = Math.max(1, HELP_MAX_LINES - lines.length - TOP_HELP_FOOTER_LINES);
+    let shownTools = tools.slice(0, maxCommandLines);
+    if (tools.length > shownTools.length && lines.length + shownTools.length + TOP_HELP_FOOTER_LINES + 1 > HELP_MAX_LINES) {
+      shownTools = shownTools.slice(0, -1);
+    }
     for (const tool of shownTools) {
       lines.push(`  ${tool.name} — ${summarizeHelpText(tool.description || "No description", COMMAND_DESC_MAX_LEN)}`);
     }
@@ -823,17 +820,24 @@ function showToolHelp(serverName, toolName, tools) {
     lines.push("");
     lines.push(`Options (${Object.keys(props).length}):`);
     const optionEntries = Object.entries(props);
-    const maxOptionLines = Math.max(1, HELP_MAX_LINES - lines.length - TOOL_HELP_RESERVED_LINES);
-    const shownOptions = optionEntries.slice(0, maxOptionLines);
+    const maxOptionLines = Math.max(1, HELP_MAX_LINES - lines.length);
+    let shownOptions = optionEntries.slice(0, maxOptionLines);
+    let hasMoreOptions = optionEntries.length > shownOptions.length;
+    let hasVisibleRequired = shownOptions.some(([key]) => required.has(key));
+    while (shownOptions.length > 0 && lines.length + shownOptions.length + (hasMoreOptions ? 1 : 0) + (hasVisibleRequired ? 1 : 0) > HELP_MAX_LINES) {
+      shownOptions = shownOptions.slice(0, -1);
+      hasMoreOptions = optionEntries.length > shownOptions.length;
+      hasVisibleRequired = shownOptions.some(([key]) => required.has(key));
+    }
     for (const [key, val] of shownOptions) {
       const requiredMark = required.has(key) ? "*" : "";
       const description = val.description ? ` - ${summarizeHelpText(val.description, OPTION_DESC_MAX_LEN)}` : "";
       lines.push(`  --${key} ${getTypeStr(val.type)}${requiredMark}${description}`);
     }
-    if (optionEntries.length > shownOptions.length) {
+    if (hasMoreOptions) {
       lines.push(`  ... +${optionEntries.length - shownOptions.length} more option(s)`);
     }
-    if (required.size > 0) {
+    if (hasVisibleRequired) {
       lines.push("Required options are marked with *.");
     }
   }
diff --git a/actions/setup/js/mcp_cli_bridge.test.cjs b/actions/setup/js/mcp_cli_bridge.test.cjs
index 7319d3a2c93..22e2c4077ab 100644
--- a/actions/setup/js/mcp_cli_bridge.test.cjs
+++ b/actions/setup/js/mcp_cli_bridge.test.cjs
@@ -225,6 +225,19 @@ describe("mcp_cli_bridge.cjs", () => {
     expect(outputLines.join("\n")).toMatch(/\.\.\. \+\d+ more command\(s\)/);
   });
 
+  it("does not truncate top-level help when commands exactly fit the line budget", () => {
+    const tools = Array.from({ length: 14 }, (_, i) => ({
+      name: `tool_${i + 1}`,
+      description: `Description for command ${i + 1}.`,
+    }));
+
+    showHelp("safeoutputs", tools);
+
+    const outputLines = stdoutChunks.join("").trimEnd().split("\n");
+    expect(outputLines.length).toBe(20);
+    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more command\(s\)/);
+  });
+
   it("keeps command help compact for many options", () => {
     const properties = {};
     for (let i = 1; i <= 24; i++) {
@@ -246,6 +259,53 @@ describe("mcp_cli_bridge.cjs", () => {
     expect(outputLines.length).toBeLessThanOrEqual(20);
     expect(outputLines.join("\n")).toMatch(/\.\.\. \+\d+ more option\(s\)/);
     expect(outputLines.join("\n")).toContain("Required options are marked with *.");
+    expect(outputLines.join("\n")).toMatch(/--field_1 \(string\)\*/);
+    expect(outputLines.join("\n")).toMatch(/--field_2 \(string\)\*/);
+  });
+
+  it("does not truncate command help when options exactly fit the line budget", () => {
+    const properties = {};
+    for (let i = 1; i <= 13; i++) {
+      properties[`field_${i}`] = { type: "string", description: `Field ${i}.` };
+    }
+
+    showToolHelp("safeoutputs", "create_issue", [
+      {
+        name: "create_issue",
+        description: "Create an issue.",
+        inputSchema: {
+          properties,
+          required: ["field_1"],
+        },
+      },
+    ]);
+
+    const outputLines = stdoutChunks.join("").trimEnd().split("\n");
+    expect(outputLines.length).toBe(20);
+    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
+    expect(outputLines.join("\n")).toContain("Required options are marked with *.");
+  });
+
+  it("omits required-note when required options are truncated", () => {
+    const properties = {};
+    for (let i = 1; i <= 24; i++) {
+      properties[`field_${i}`] = { type: "string", description: `Field ${i}.` };
+    }
+
+    showToolHelp("safeoutputs", "create_issue", [
+      {
+        name: "create_issue",
+        description: "Create an issue.",
+        inputSchema: {
+          properties,
+          required: ["field_23", "field_24"],
+        },
+      },
+    ]);
+
+    const outputLines = stdoutChunks.join("").trimEnd().split("\n");
+    expect(outputLines.join("\n")).toMatch(/\.\.\. \+\d+ more option\(s\)/);
+    expect(outputLines.join("\n")).not.toContain("Required options are marked with *.");
   });
 
   describe("stdin placeholder removed — '-' is always a literal value", () => {

From fa8aaea56845401bf5f840c456a489da70ed2536 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 12:40:52 +0000
Subject: [PATCH 08/10] Compact help to show full command and option names

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs      | 106 +++++++++++++----------
 actions/setup/js/mcp_cli_bridge.test.cjs |  34 +++++---
 2 files changed, 83 insertions(+), 57 deletions(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index 436dc942cc5..d4761f56445 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -55,11 +55,10 @@ const KEEPALIVE_PING_INTERVAL_MS = 10000;
 const KEEPALIVE_PING_ID_START = 1000;
 
 /** Preferred max lines for generated CLI help output */
-const HELP_MAX_LINES = 20;
-const COMMAND_DESC_MAX_LEN = 68;
+const TOP_HELP_MAX_LINES = 20;
+const TOOL_HELP_MAX_LINES = 30;
 const TOOL_DESC_MAX_LEN = 90;
-const OPTION_DESC_MAX_LEN = 62;
-const TOP_HELP_FOOTER_LINES = 2;
+const COMPACT_NAME_LINE_TARGET_WIDTH = 110;
 
 // ---------------------------------------------------------------------------
 // Audit logging
@@ -772,22 +771,16 @@ function loadTools(toolsFile) {
 function showHelp(serverName, tools) {
   const lines = [`Usage: ${serverName} <command> [--param value ...]`, `Tip: ${serverName} <command> --help`, "", `Commands (${tools.length}):`];
   if (tools.length > 0) {
-    const maxCommandLines = Math.max(1, HELP_MAX_LINES - lines.length - TOP_HELP_FOOTER_LINES);
-    let shownTools = tools.slice(0, maxCommandLines);
-    if (tools.length > shownTools.length && lines.length + shownTools.length + TOP_HELP_FOOTER_LINES + 1 > HELP_MAX_LINES) {
-      shownTools = shownTools.slice(0, -1);
-    }
-    for (const tool of shownTools) {
-      lines.push(`  ${tool.name} — ${summarizeHelpText(tool.description || "No description", COMMAND_DESC_MAX_LEN)}`);
-    }
-    if (tools.length > shownTools.length) {
-      lines.push(`  ... +${tools.length - shownTools.length} more command(s)`);
-    }
+    const maxCommandLines = Math.max(1, TOP_HELP_MAX_LINES - lines.length);
+    lines.push(
+      ...formatCompactNameLines(
+        tools.map(tool => tool.name),
+        maxCommandLines
+      )
+    );
   } else {
     lines.push("  (tool list unavailable)");
   }
-  lines.push("");
-  lines.push(`Run '${serverName} <command> --help' for option details.`);
   process.stdout.write(lines.join("\n") + "\n");
 }
 
@@ -820,24 +813,15 @@ function showToolHelp(serverName, toolName, tools) {
     lines.push("");
     lines.push(`Options (${Object.keys(props).length}):`);
     const optionEntries = Object.entries(props);
-    const maxOptionLines = Math.max(1, HELP_MAX_LINES - lines.length);
-    let shownOptions = optionEntries.slice(0, maxOptionLines);
-    let hasMoreOptions = optionEntries.length > shownOptions.length;
-    let hasVisibleRequired = shownOptions.some(([key]) => required.has(key));
-    while (shownOptions.length > 0 && lines.length + shownOptions.length + (hasMoreOptions ? 1 : 0) + (hasVisibleRequired ? 1 : 0) > HELP_MAX_LINES) {
-      shownOptions = shownOptions.slice(0, -1);
-      hasMoreOptions = optionEntries.length > shownOptions.length;
-      hasVisibleRequired = shownOptions.some(([key]) => required.has(key));
-    }
-    for (const [key, val] of shownOptions) {
-      const requiredMark = required.has(key) ? "*" : "";
-      const description = val.description ? ` - ${summarizeHelpText(val.description, OPTION_DESC_MAX_LEN)}` : "";
-      lines.push(`  --${key} ${getTypeStr(val.type)}${requiredMark}${description}`);
-    }
-    if (hasMoreOptions) {
-      lines.push(`  ... +${optionEntries.length - shownOptions.length} more option(s)`);
-    }
-    if (hasVisibleRequired) {
+    const hasRequiredOptions = required.size > 0;
+    const maxOptionLines = Math.max(1, TOOL_HELP_MAX_LINES - lines.length - (hasRequiredOptions ? 1 : 0));
+    lines.push(
+      ...formatCompactNameLines(
+        optionEntries.map(([key]) => `--${key}${required.has(key) ? "*" : ""}`),
+        maxOptionLines
+      )
+    );
+    if (hasRequiredOptions) {
       lines.push("Required options are marked with *.");
     }
   }
@@ -845,18 +829,6 @@ function showToolHelp(serverName, toolName, tools) {
   process.stdout.write(lines.join("\n") + "\n");
 }
 
-/**
- * Format a JSON schema type value as a short bracketed string.
- *
- * @param {string|string[]|undefined} type
- * @returns {string}
- */
-function getTypeStr(type) {
-  if (!type) return "(string)";
-  const types = Array.isArray(type) ? type.filter(t => t !== "null") : [type];
-  return `(${types.length > 0 ? types.join("|") : "null"})`;
-}
-
 /**
  * Collapse whitespace and trim long help text for compact output.
  *
@@ -877,6 +849,46 @@ function summarizeHelpText(value, maxLen) {
   return `${normalized.slice(0, maxLen - 1)}…`;
 }
 
+/**
+ * Render names as comma-separated compact lines and keep all names visible.
+ *
+ * @param {string[]} names
+ * @param {number} maxLines - Preferred line budget; non-positive/invalid values return one compact line
+ * @returns {string[]}
+ */
+function formatCompactNameLines(names, maxLines) {
+  if (!Array.isArray(names) || names.length === 0) {
+    return [];
+  }
+  if (!Number.isFinite(maxLines) || maxLines <= 0) {
+    return [`  ${names.join(", ")}`];
+  }
+  const lines = [];
+  let current = "  ";
+  for (const name of names) {
+    const token = current.trim() ? `, ${name}` : name;
+    const shouldStartNewLine = current.length + token.length > COMPACT_NAME_LINE_TARGET_WIDTH;
+    if (shouldStartNewLine) {
+      lines.push(current);
+      current = `  ${name}`;
+      continue;
+    }
+    current += token;
+  }
+  if (current.trim()) {
+    lines.push(current);
+  }
+  if (lines.length > maxLines) {
+    // Keep all names visible by collapsing overflow into the last allowed line.
+    const compactTail = lines
+      .slice(maxLines - 1)
+      .map(line => line.trim())
+      .join(", ");
+    return [...lines.slice(0, maxLines - 1), `  ${compactTail}`];
+  }
+  return lines;
+}
+
 // ---------------------------------------------------------------------------
 // Response formatting
 // ---------------------------------------------------------------------------
diff --git a/actions/setup/js/mcp_cli_bridge.test.cjs b/actions/setup/js/mcp_cli_bridge.test.cjs
index 22e2c4077ab..dd83f950364 100644
--- a/actions/setup/js/mcp_cli_bridge.test.cjs
+++ b/actions/setup/js/mcp_cli_bridge.test.cjs
@@ -222,7 +222,10 @@ describe("mcp_cli_bridge.cjs", () => {
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
     expect(outputLines.length).toBeLessThanOrEqual(20);
-    expect(outputLines.join("\n")).toMatch(/\.\.\. \+\d+ more command\(s\)/);
+    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more command\(s\)/);
+    for (const tool of tools) {
+      expect(outputLines.join("\n")).toContain(tool.name);
+    }
   });
 
   it("does not truncate top-level help when commands exactly fit the line budget", () => {
@@ -234,8 +237,11 @@ describe("mcp_cli_bridge.cjs", () => {
     showHelp("safeoutputs", tools);
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
-    expect(outputLines.length).toBe(20);
+    expect(outputLines.length).toBeLessThanOrEqual(20);
     expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more command\(s\)/);
+    for (const tool of tools) {
+      expect(outputLines.join("\n")).toContain(tool.name);
+    }
   });
 
   it("keeps command help compact for many options", () => {
@@ -256,11 +262,14 @@ describe("mcp_cli_bridge.cjs", () => {
     ]);
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
-    expect(outputLines.length).toBeLessThanOrEqual(20);
-    expect(outputLines.join("\n")).toMatch(/\.\.\. \+\d+ more option\(s\)/);
+    expect(outputLines.length).toBeLessThanOrEqual(30);
+    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
     expect(outputLines.join("\n")).toContain("Required options are marked with *.");
-    expect(outputLines.join("\n")).toMatch(/--field_1 \(string\)\*/);
-    expect(outputLines.join("\n")).toMatch(/--field_2 \(string\)\*/);
+    for (let i = 1; i <= 24; i++) {
+      expect(outputLines.join("\n")).toContain(`--field_${i}`);
+    }
+    expect(outputLines.join("\n")).toContain("--field_1*");
+    expect(outputLines.join("\n")).toContain("--field_2*");
   });
 
   it("does not truncate command help when options exactly fit the line budget", () => {
@@ -281,12 +290,15 @@ describe("mcp_cli_bridge.cjs", () => {
     ]);
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
-    expect(outputLines.length).toBe(20);
+    expect(outputLines.length).toBeLessThanOrEqual(30);
     expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
     expect(outputLines.join("\n")).toContain("Required options are marked with *.");
+    for (let i = 1; i <= 13; i++) {
+      expect(outputLines.join("\n")).toContain(`--field_${i}`);
+    }
   });
 
-  it("omits required-note when required options are truncated", () => {
+  it("keeps required-note when required options are in the compact list", () => {
     const properties = {};
     for (let i = 1; i <= 24; i++) {
       properties[`field_${i}`] = { type: "string", description: `Field ${i}.` };
@@ -304,8 +316,10 @@ describe("mcp_cli_bridge.cjs", () => {
     ]);
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
-    expect(outputLines.join("\n")).toMatch(/\.\.\. \+\d+ more option\(s\)/);
-    expect(outputLines.join("\n")).not.toContain("Required options are marked with *.");
+    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
+    expect(outputLines.join("\n")).toContain("Required options are marked with *.");
+    expect(outputLines.join("\n")).toContain("--field_23*");
+    expect(outputLines.join("\n")).toContain("--field_24*");
   });
 
   describe("stdin placeholder removed — '-' is always a literal value", () => {

From 6936fd9821173bbd2710d6fa20e0a18f73eebec1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 12:42:02 +0000
Subject: [PATCH 09/10] Document compact help soft-width behavior

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index d4761f56445..a54530fc2a6 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -851,9 +851,10 @@ function summarizeHelpText(value, maxLen) {
 
 /**
  * Render names as comma-separated compact lines and keep all names visible.
+ * Width is a soft target; the final line may exceed it to avoid dropping names.
  *
  * @param {string[]} names
- * @param {number} maxLines - Preferred line budget; non-positive/invalid values return one compact line
+ * @param {number} maxLines - Preferred line budget; non-positive/invalid values force one compact line
  * @returns {string[]}
  */
 function formatCompactNameLines(names, maxLines) {

From f4a7fefb1fad4f79358533a29d6dad99a451eab8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 12:44:31 +0000
Subject: [PATCH 10/10] Polish compact help formatting and test assertions

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 actions/setup/js/mcp_cli_bridge.cjs      |  5 ++-
 actions/setup/js/mcp_cli_bridge.test.cjs | 39 +++++++++++++-----------
 2 files changed, 26 insertions(+), 18 deletions(-)

diff --git a/actions/setup/js/mcp_cli_bridge.cjs b/actions/setup/js/mcp_cli_bridge.cjs
index a54530fc2a6..b68015a49eb 100644
--- a/actions/setup/js/mcp_cli_bridge.cjs
+++ b/actions/setup/js/mcp_cli_bridge.cjs
@@ -859,6 +859,7 @@ function summarizeHelpText(value, maxLen) {
  */
 function formatCompactNameLines(names, maxLines) {
   if (!Array.isArray(names) || names.length === 0) {
+    // Callers spread the result into help lines, so empty input should contribute no lines.
     return [];
   }
   if (!Number.isFinite(maxLines) || maxLines <= 0) {
@@ -868,6 +869,7 @@ function formatCompactNameLines(names, maxLines) {
   let current = "  ";
   for (const name of names) {
     const token = current.trim() ? `, ${name}` : name;
+    // A single very long name may still exceed the width target; we keep it intact.
     const shouldStartNewLine = current.length + token.length > COMPACT_NAME_LINE_TARGET_WIDTH;
     if (shouldStartNewLine) {
       lines.push(current);
@@ -880,9 +882,10 @@ function formatCompactNameLines(names, maxLines) {
     lines.push(current);
   }
   if (lines.length > maxLines) {
-    // Keep all names visible by collapsing overflow into the last allowed line.
+    // Keep maxLines - 1 full lines and collapse the remaining names into the final allowed line.
     const compactTail = lines
       .slice(maxLines - 1)
+      // Trim per-line indentation before rebuilding a single indented tail line.
       .map(line => line.trim())
       .join(", ");
     return [...lines.slice(0, maxLines - 1), `  ${compactTail}`];
diff --git a/actions/setup/js/mcp_cli_bridge.test.cjs b/actions/setup/js/mcp_cli_bridge.test.cjs
index dd83f950364..587bb018930 100644
--- a/actions/setup/js/mcp_cli_bridge.test.cjs
+++ b/actions/setup/js/mcp_cli_bridge.test.cjs
@@ -221,10 +221,11 @@ describe("mcp_cli_bridge.cjs", () => {
     showHelp("safeoutputs", tools);
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
+    const output = outputLines.join("\n");
     expect(outputLines.length).toBeLessThanOrEqual(20);
-    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more command\(s\)/);
+    expect(output).not.toMatch(/\.\.\. \+\d+ more command\(s\)/);
     for (const tool of tools) {
-      expect(outputLines.join("\n")).toContain(tool.name);
+      expect(output).toContain(tool.name);
     }
   });
 
@@ -237,10 +238,11 @@ describe("mcp_cli_bridge.cjs", () => {
     showHelp("safeoutputs", tools);
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
+    const output = outputLines.join("\n");
     expect(outputLines.length).toBeLessThanOrEqual(20);
-    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more command\(s\)/);
+    expect(output).not.toMatch(/\.\.\. \+\d+ more command\(s\)/);
     for (const tool of tools) {
-      expect(outputLines.join("\n")).toContain(tool.name);
+      expect(output).toContain(tool.name);
     }
   });
 
@@ -262,14 +264,15 @@ describe("mcp_cli_bridge.cjs", () => {
     ]);
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
+    const output = outputLines.join("\n");
     expect(outputLines.length).toBeLessThanOrEqual(30);
-    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
-    expect(outputLines.join("\n")).toContain("Required options are marked with *.");
+    expect(output).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
+    expect(output).toContain("Required options are marked with *.");
     for (let i = 1; i <= 24; i++) {
-      expect(outputLines.join("\n")).toContain(`--field_${i}`);
+      expect(output).toContain(`--field_${i}`);
     }
-    expect(outputLines.join("\n")).toContain("--field_1*");
-    expect(outputLines.join("\n")).toContain("--field_2*");
+    expect(output).toContain("--field_1*");
+    expect(output).toContain("--field_2*");
   });
 
   it("does not truncate command help when options exactly fit the line budget", () => {
@@ -290,15 +293,16 @@ describe("mcp_cli_bridge.cjs", () => {
     ]);
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
+    const output = outputLines.join("\n");
     expect(outputLines.length).toBeLessThanOrEqual(30);
-    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
-    expect(outputLines.join("\n")).toContain("Required options are marked with *.");
+    expect(output).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
+    expect(output).toContain("Required options are marked with *.");
     for (let i = 1; i <= 13; i++) {
-      expect(outputLines.join("\n")).toContain(`--field_${i}`);
+      expect(output).toContain(`--field_${i}`);
     }
   });
 
-  it("keeps required-note when required options are in the compact list", () => {
+  it("keeps required note when required options are in the compact list", () => {
     const properties = {};
     for (let i = 1; i <= 24; i++) {
       properties[`field_${i}`] = { type: "string", description: `Field ${i}.` };
@@ -316,10 +320,11 @@ describe("mcp_cli_bridge.cjs", () => {
     ]);
 
     const outputLines = stdoutChunks.join("").trimEnd().split("\n");
-    expect(outputLines.join("\n")).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
-    expect(outputLines.join("\n")).toContain("Required options are marked with *.");
-    expect(outputLines.join("\n")).toContain("--field_23*");
-    expect(outputLines.join("\n")).toContain("--field_24*");
+    const output = outputLines.join("\n");
+    expect(output).not.toMatch(/\.\.\. \+\d+ more option\(s\)/);
+    expect(output).toContain("Required options are marked with *.");
+    expect(output).toContain("--field_23*");
+    expect(output).toContain("--field_24*");
   });
 
   describe("stdin placeholder removed — '-' is always a literal value", () => {
