Skip to content

Commit 0efb80c

Browse files
committed
fix(summary): truncate long bash tool commands
1 parent b0e96ee commit 0efb80c

6 files changed

Lines changed: 62 additions & 1 deletion

File tree

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ OPENCODE_MODEL_ID=big-pickle
5050
# Higher value = fewer Telegram edit requests, lower value = more real-time updates
5151
# RESPONSE_STREAM_THROTTLE_MS=500
5252

53+
# Maximum displayed length for bash tool commands in Telegram summaries (default: 128)
54+
# Longer commands are truncated with "..."
55+
# BASH_TOOL_DISPLAY_MAX_LENGTH=128
56+
5357
# Bot locale: supported locale code (default: en)
5458
# Supported locales: en, de, es, fr, ru, zh
5559
# BOT_LOCALE=en

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ When installed via npm, the configuration wizard handles the initial setup. The
163163
| `PROJECTS_LIST_LIMIT` | Projects per page in `/projects` | No | `10` |
164164
| `COMMANDS_LIST_LIMIT` | Commands per page in `/commands` | No | `10` |
165165
| `TASK_LIMIT` | Maximum number of scheduled tasks that can exist at once | No | `10` |
166+
| `BASH_TOOL_DISPLAY_MAX_LENGTH` | Maximum displayed length for `bash` tool commands in Telegram summaries; longer commands are truncated | No | `128` |
166167
| `SERVICE_MESSAGES_INTERVAL_SEC` | Service messages interval (thinking + tool calls); keep `>=2` to avoid Telegram rate limits, `0` = immediate | No | `5` |
167168
| `HIDE_THINKING_MESSAGES` | Hide `💭 Thinking...` service messages | No | `false` |
168169
| `HIDE_TOOL_CALL_MESSAGES` | Hide tool-call service messages (`💻 bash ...`, `📖 read ...`, etc.) | No | `false` |

src/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export const config = {
9999
commandsListLimit: getOptionalPositiveIntEnvVar("COMMANDS_LIST_LIMIT", 10),
100100
taskLimit: getOptionalPositiveIntEnvVar("TASK_LIMIT", 10),
101101
responseStreamThrottleMs: getOptionalPositiveIntEnvVar("RESPONSE_STREAM_THROTTLE_MS", 500),
102+
bashToolDisplayMaxLength: getOptionalPositiveIntEnvVar("BASH_TOOL_DISPLAY_MAX_LENGTH", 128),
102103
locale: getOptionalLocaleEnvVar("BOT_LOCALE", "en"),
103104
hideThinkingMessages: getOptionalBooleanEnvVar("HIDE_THINKING_MESSAGES", false),
104105
hideToolCallMessages: getOptionalBooleanEnvVar("HIDE_TOOL_CALL_MESSAGES", false),

src/summary/formatter.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ import { getCurrentProject } from "../settings/manager.js";
1010
const TELEGRAM_MESSAGE_LIMIT = 4096;
1111
const MARKDOWN_V2_RESERVED_CHARS = /([_\*\[\]\(\)~`>#+\-=|{}.!\\])/g;
1212

13+
function truncateWithEllipsis(text: string, maxLength: number): string {
14+
if (text.length <= maxLength) {
15+
return text;
16+
}
17+
18+
if (maxLength <= 3) {
19+
return ".".repeat(Math.max(0, maxLength));
20+
}
21+
22+
return `${text.slice(0, maxLength - 3).trimEnd()}...`;
23+
}
24+
1325
interface SplitTextOptions {
1426
avoidTrailingMarkdownEscape?: boolean;
1527
}
@@ -484,7 +496,7 @@ export function formatToolInfo(toolInfo: ToolInfo): string | null {
484496
}
485497

486498
if (tool === "bash" && input && typeof input.command === "string") {
487-
details = input.command;
499+
details = truncateWithEllipsis(input.command, config.bot.bashToolDisplayMaxLength);
488500
}
489501

490502
if (tool === "apply_patch") {

tests/config.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,30 @@ describe("config boolean env parsing", () => {
134134
expect(config.bot.responseStreamThrottleMs).toBe(500);
135135
});
136136

137+
it("uses default bash tool display length when env is missing", async () => {
138+
vi.stubEnv("BASH_TOOL_DISPLAY_MAX_LENGTH", "");
139+
140+
const config = await loadConfig();
141+
142+
expect(config.bot.bashToolDisplayMaxLength).toBe(128);
143+
});
144+
145+
it("parses BASH_TOOL_DISPLAY_MAX_LENGTH as a positive integer", async () => {
146+
vi.stubEnv("BASH_TOOL_DISPLAY_MAX_LENGTH", "256");
147+
148+
const config = await loadConfig();
149+
150+
expect(config.bot.bashToolDisplayMaxLength).toBe(256);
151+
});
152+
153+
it("falls back to default bash tool display length on invalid value", async () => {
154+
vi.stubEnv("BASH_TOOL_DISPLAY_MAX_LENGTH", "zero");
155+
156+
const config = await loadConfig();
157+
158+
expect(config.bot.bashToolDisplayMaxLength).toBe(128);
159+
});
160+
137161
it("parses TASK_LIMIT as a positive integer", async () => {
138162
vi.stubEnv("TASK_LIMIT", "25");
139163

tests/summary/formatter.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,25 @@ describe("summary/formatter", () => {
192192
expect(text).toBe("💻 Run tests\nbash npm test");
193193
});
194194

195+
it("truncates long bash commands with ellipsis", () => {
196+
const longCommand = `node -e "${"a".repeat(160)}"`;
197+
198+
const text = formatToolInfo({
199+
sessionId: "s1",
200+
messageId: "m4b",
201+
callId: "c4b",
202+
tool: "bash",
203+
state: { status: "completed" } as never,
204+
input: {
205+
command: longCommand,
206+
},
207+
});
208+
209+
expect(text).toMatch(/^💻 bash /);
210+
expect(text?.endsWith("...")).toBe(true);
211+
expect(text).toHaveLength("💻 bash ".length + 128);
212+
});
213+
195214
it("formats apply_patch tool details without dumping full patch", () => {
196215
const text = formatToolInfo({
197216
sessionId: "s1",

0 commit comments

Comments
 (0)