Skip to content

Commit ecfd36a

Browse files
authored
Support /compact (#51)
It would be nice to also support sending status via hooks when the compact action is happening (and maybe show something in the UI to show where compacting happened in the thread) but for now this at least allows it. --------- Signed-off-by: Ben Brandt <benjamin.j.brandt@gmail.com>
1 parent 7bd1638 commit ecfd36a

2 files changed

Lines changed: 65 additions & 4 deletions

File tree

src/acp-agent.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,24 @@ export class ClaudeAcpAgent implements Agent {
284284
continue;
285285
}
286286

287+
// Slash commands like /compact can generate invalid output... doesn't match
288+
// their own docs: https://docs.anthropic.com/en/docs/claude-code/sdk/sdk-slash-commands#%2Fcompact-compact-conversation-history
289+
if (
290+
typeof message.message.content === "string" &&
291+
message.message.content.includes("<local-command-stdout>")
292+
) {
293+
console.log(message.message.content);
294+
break;
295+
}
296+
297+
if (
298+
typeof message.message.content === "string" &&
299+
message.message.content.includes("<local-command-stderr>")
300+
) {
301+
console.error(message.message.content);
302+
break;
303+
}
304+
287305
if (
288306
message.message.model === "<synthetic>" &&
289307
message.message.content.length === 1 &&
@@ -361,7 +379,6 @@ async function getAvailableSlashCommands(query: Query): Promise<AvailableCommand
361379
"bashes", // Modal
362380
"bug", // Modal
363381
"clear", // Escape Codes
364-
"compact", // Not supported via SDK?
365382
"config", // Modal
366383
"context", // Escape Codes
367384
"cost", // Escape Codes
@@ -390,14 +407,14 @@ async function getAvailableSlashCommands(query: Query): Promise<AvailableCommand
390407
"todos", // Escape Codes
391408
"vim", // Not needed
392409
];
393-
//todo: Do not use `as any` once `supportedCommands` is exposed via the typescript interface
394-
const commands = await (query as any).supportedCommands();
410+
const commands = await query.supportedCommands();
395411

396412
return commands
397-
.map((command: { name: string; description: string; argumentHint: string }) => {
413+
.map((command) => {
398414
const input = command.argumentHint ? { hint: command.argumentHint } : null;
399415
return {
400416
name: command.name,
417+
// @ts-expect-error type in the ts interface
401418
description: command.description || "",
402419
input,
403420
};

src/tests/acp-agent.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,50 @@ describe.skipIf(!process.env.RUN_INTEGRATION_TESTS)("ACP subprocess integration"
191191

192192
expect(client.takeReceivedText()).toContain("Hello GPT-5");
193193
}, 30000);
194+
195+
it("/compact works", async () => {
196+
const { client, connection, newSessionResponse } = await setupTestSession(__dirname);
197+
198+
const commands = await client.availableCommandsPromise;
199+
200+
expect(commands).toContainEqual({
201+
description:
202+
"Clear conversation history but keep a summary in context. Optional: /compact [instructions for summarization]",
203+
input: {
204+
hint: "<optional custom summarization instructions>",
205+
},
206+
name: "compact",
207+
});
208+
209+
// Error case (no previous message)
210+
await connection.prompt({
211+
prompt: [{ type: "text", text: "/compact" }],
212+
sessionId: newSessionResponse.sessionId,
213+
});
214+
215+
expect(client.takeReceivedText()).toBe("");
216+
217+
// Send something
218+
await connection.prompt({
219+
prompt: [{ type: "text", text: "Hi" }],
220+
sessionId: newSessionResponse.sessionId,
221+
});
222+
// Clear response
223+
client.takeReceivedText();
224+
225+
// Test with instruction
226+
await connection.prompt({
227+
prompt: [
228+
{
229+
type: "text",
230+
text: "/compact greeting",
231+
},
232+
],
233+
sessionId: newSessionResponse.sessionId,
234+
});
235+
236+
expect(client.takeReceivedText()).toContain("");
237+
}, 30000);
194238
});
195239

196240
describe("tool conversions", () => {

0 commit comments

Comments
 (0)