diff --git a/packages/server/src/server/agent/providers/claude/agent.history.test.ts b/packages/server/src/server/agent/providers/claude/agent.history.test.ts
index 89df8aeb1..2425dcb12 100644
--- a/packages/server/src/server/agent/providers/claude/agent.history.test.ts
+++ b/packages/server/src/server/agent/providers/claude/agent.history.test.ts
@@ -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 =
+ "diagnose\n/diagnose\nrecently the PR data does not update";
+
+ 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 =
+ "caveman:caveman\n/caveman:caveman";
+
+ expect(extractUserMessageText(content)).toBe("/caveman:caveman");
+ });
+
test("returns null when no textual content is present", () => {
const content = [
{ type: "image", source: "foo.png" },
diff --git a/packages/server/src/server/agent/providers/claude/agent.test.ts b/packages/server/src/server/agent/providers/claude/agent.test.ts
index 262999109..6005a1d7c 100644
--- a/packages/server/src/server/agent/providers/claude/agent.test.ts
+++ b/packages/server/src/server/agent/providers/claude/agent.test.ts
@@ -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:
+ "caveman:caveman\n/caveman:caveman",
+ },
+ cwd,
+ sessionId: commandSessionId,
+ })}\n`,
+ "utf-8",
+ );
+ await fs.writeFile(
+ argsSessionFile,
+ `${JSON.stringify({
+ parentUuid: null,
+ isSidechain: false,
+ type: "user",
+ message: {
+ role: "user",
+ content:
+ "diagnose\n/diagnose\nrecently the PR data does not update",
+ },
+ 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();
diff --git a/packages/server/src/server/agent/providers/claude/agent.ts b/packages/server/src/server/agent/providers/claude/agent.ts
index 307f33094..8a0d3d319 100644
--- a/packages/server/src/server/agent/providers/claude/agent.ts
+++ b/packages/server/src/server/agent/providers/claude/agent.ts
@@ -604,6 +604,9 @@ function isClaudeNoResponsePlaceholderText(value: unknown): boolean {
const LOCAL_COMMAND_STDOUT_PATTERN =
/^\s*[\s\S]*<\/local-command-stdout>\s*$/;
+const CLAUDE_COMMAND_MESSAGE_PATTERN = /([\s\S]*?)<\/command-message>/;
+const CLAUDE_COMMAND_ARGS_PATTERN = /([\s\S]*?)<\/command-args>/;
+const CLAUDE_COMMAND_NAME_PATTERN = /([\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);
}
}
}