Fix slash commands in imported session previews (#1502)

This commit is contained in:
Mohamed Boudra
2026-06-13 10:16:49 +08:00
committed by GitHub
parent f4687668e1
commit a5c4ba4d89
3 changed files with 155 additions and 6 deletions
@@ -16,6 +16,20 @@ describe("extractUserMessageText", () => {
expect(extractUserMessageText(content)).toBe("First line\n\nSecond line");
});
test("returns Claude slash command prompts without transcript tags", () => {
const content =
"<command-message>diagnose</command-message>\n<command-name>/diagnose</command-name>\n<command-args>recently the PR data does not update</command-args>";
expect(extractUserMessageText(content)).toBe("/diagnose recently the PR data does not update");
});
test("returns the Claude slash command prompt when no args were recorded", () => {
const content =
"<command-message>caveman:caveman</command-message>\n<command-name>/caveman:caveman</command-name>";
expect(extractUserMessageText(content)).toBe("/caveman:caveman");
});
test("returns null when no textual content is present", () => {
const content = [
{ type: "image", source: "foo.png" },
@@ -860,6 +860,98 @@ describe("normalizeClaudeAskUserQuestionUpdatedInput", () => {
});
});
describe("ClaudeAgentClient.listImportableSessions", () => {
test("shows Claude slash command prompts without transcript tags", async () => {
const tmpConfigDir = await fs.mkdtemp(path.join(os.tmpdir(), "paseo-claude-import-"));
const previousConfigDir = process.env.CLAUDE_CONFIG_DIR;
process.env.CLAUDE_CONFIG_DIR = tmpConfigDir;
try {
const commandSessionId = "session-command-import";
const argsSessionId = "session-command-args-import";
const cwd = "/tmp/paseo-test-claude-import";
const sanitized = cwd.replace(/[\\/._:]/g, "-");
const projectDir = path.join(tmpConfigDir, "projects", sanitized);
await fs.mkdir(projectDir, { recursive: true });
const commandSessionFile = path.join(projectDir, `${commandSessionId}.jsonl`);
const argsSessionFile = path.join(projectDir, `${argsSessionId}.jsonl`);
await fs.writeFile(
commandSessionFile,
`${JSON.stringify({
parentUuid: null,
isSidechain: false,
type: "user",
message: {
role: "user",
content:
"<command-message>caveman:caveman</command-message>\n<command-name>/caveman:caveman</command-name>",
},
cwd,
sessionId: commandSessionId,
})}\n`,
"utf-8",
);
await fs.writeFile(
argsSessionFile,
`${JSON.stringify({
parentUuid: null,
isSidechain: false,
type: "user",
message: {
role: "user",
content:
"<command-message>diagnose</command-message>\n<command-name>/diagnose</command-name>\n<command-args>recently the PR data does not update</command-args>",
},
cwd,
sessionId: argsSessionId,
})}\n`,
"utf-8",
);
await fs.utimes(
commandSessionFile,
new Date("2026-06-12T10:00:00.000Z"),
new Date("2026-06-12T10:00:00.000Z"),
);
await fs.utimes(
argsSessionFile,
new Date("2026-06-12T11:00:00.000Z"),
new Date("2026-06-12T11:00:00.000Z"),
);
const client = new ClaudeAgentClient({
logger: createTestLogger(),
resolveBinary: async () => "/test/claude/bin",
});
await expect(client.listImportableSessions({ limit: 2 })).resolves.toEqual([
{
providerHandleId: argsSessionId,
cwd,
title: "/diagnose recently the PR data does not update",
firstPromptPreview: "/diagnose recently the PR data does not update",
lastPromptPreview: "/diagnose recently the PR data does not update",
lastActivityAt: new Date("2026-06-12T11:00:00.000Z"),
},
{
providerHandleId: commandSessionId,
cwd,
title: "/caveman:caveman",
firstPromptPreview: "/caveman:caveman",
lastPromptPreview: "/caveman:caveman",
lastActivityAt: new Date("2026-06-12T10:00:00.000Z"),
},
]);
} finally {
if (previousConfigDir === undefined) {
delete process.env.CLAUDE_CONFIG_DIR;
} else {
process.env.CLAUDE_CONFIG_DIR = previousConfigDir;
}
await fs.rm(tmpConfigDir, { recursive: true, force: true });
}
});
});
describe("ClaudeAgentSession context window usage", () => {
const logger = createTestLogger();
@@ -604,6 +604,9 @@ function isClaudeNoResponsePlaceholderText(value: unknown): boolean {
const LOCAL_COMMAND_STDOUT_PATTERN =
/^\s*<local-command-stdout>[\s\S]*<\/local-command-stdout>\s*$/;
const CLAUDE_COMMAND_MESSAGE_PATTERN = /<command-message>([\s\S]*?)<\/command-message>/;
const CLAUDE_COMMAND_ARGS_PATTERN = /<command-args>([\s\S]*?)<\/command-args>/;
const CLAUDE_COMMAND_NAME_PATTERN = /<command-name>([\s\S]*?)<\/command-name>/;
function isClaudeLocalCommandStdout(value: unknown): boolean {
const normalized = normalizeClaudeTranscriptText(value);
@@ -659,7 +662,7 @@ export function extractUserMessageText(content: unknown): string | null {
if (!normalized || isClaudeTranscriptNoiseText(normalized)) {
return null;
}
return normalized;
return normalizeClaudeUserPromptText(normalized);
}
if (!Array.isArray(content)) {
@@ -675,7 +678,10 @@ export function extractUserMessageText(content: unknown): string | null {
if (text && text.trim()) {
const trimmed = text.trim();
if (!isClaudeTranscriptNoiseText(trimmed)) {
parts.push(trimmed);
const normalized = normalizeClaudeUserPromptText(trimmed);
if (normalized) {
parts.push(normalized);
}
}
continue;
}
@@ -683,7 +689,10 @@ export function extractUserMessageText(content: unknown): string | null {
if (input && input.trim()) {
const trimmed = input.trim();
if (!isClaudeTranscriptNoiseText(trimmed)) {
parts.push(trimmed);
const normalized = normalizeClaudeUserPromptText(trimmed);
if (normalized) {
parts.push(normalized);
}
}
}
}
@@ -5082,6 +5091,38 @@ function normalizeImportablePromptPreview(text: string): string | null {
return normalized.length > 160 ? normalized.slice(0, 160) : normalized;
}
function normalizeClaudeUserPromptText(text: string): string | null {
const normalized = text.trim();
if (!CLAUDE_COMMAND_MESSAGE_PATTERN.test(normalized)) {
return normalized || null;
}
const command = readClaudeCommandPromptName(normalized);
if (!command) {
return null;
}
const commandArgs = normalized.match(CLAUDE_COMMAND_ARGS_PATTERN)?.[1]?.trim();
if (commandArgs) {
return `${command} ${commandArgs}`;
}
return command;
}
function readClaudeCommandPromptName(text: string): string | null {
const commandName = text.match(CLAUDE_COMMAND_NAME_PATTERN)?.[1]?.trim();
if (commandName) {
return commandName.startsWith("/") ? commandName : `/${commandName}`;
}
const commandMessage = text.match(CLAUDE_COMMAND_MESSAGE_PATTERN)?.[1]?.trim();
if (!commandMessage) {
return null;
}
return commandMessage.startsWith("/") ? commandMessage : `/${commandMessage}`;
}
function extractClaudeUserText(messageRaw: unknown): string | null {
const message = toObjectRecord(messageRaw);
if (!message) {
@@ -5089,11 +5130,13 @@ function extractClaudeUserText(messageRaw: unknown): string | null {
}
if (typeof message.content === "string") {
const normalized = message.content.trim();
return normalized && !isClaudeTranscriptNoiseText(normalized) ? normalized : null;
if (!normalized || isClaudeTranscriptNoiseText(normalized)) return null;
return normalizeClaudeUserPromptText(normalized);
}
if (typeof message.text === "string") {
const normalized = message.text.trim();
return normalized && !isClaudeTranscriptNoiseText(normalized) ? normalized : null;
if (!normalized || isClaudeTranscriptNoiseText(normalized)) return null;
return normalizeClaudeUserPromptText(normalized);
}
if (isUnknownArray(message.content)) {
for (const block of message.content) {
@@ -5101,7 +5144,7 @@ function extractClaudeUserText(messageRaw: unknown): string | null {
if (blockRecord && typeof blockRecord.text === "string") {
const normalized = blockRecord.text.trim();
if (normalized && !isClaudeTranscriptNoiseText(normalized)) {
return normalized;
return normalizeClaudeUserPromptText(normalized);
}
}
}