Skip to content

Commit bec8e2f

Browse files
Ark0Nclaude
andcommitted
fix: improve history prompt extraction — filter expanded commands, add tail scan fallback
Skip /init expansions, slash commands, orchestrator prompts, ANSI codes, secrets, and short/vague messages. When head scan finds no usable prompt (e.g. /init sessions), read last 32KB of transcript to find a recent meaningful user message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 2203f3a commit bec8e2f

1 file changed

Lines changed: 41 additions & 3 deletions

File tree

src/web/routes/session-routes.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -984,14 +984,25 @@ export function registerSessionRoutes(
984984
if (textBlock) text = textBlock.text;
985985
}
986986
if (!text) continue;
987-
// Strip XML-like system/command tags that Claude Code injects into transcripts
987+
// Strip XML-like system/command tags and ANSI escapes from transcripts
988988
text = text
989989
.replace(/<[^>]+>/g, '')
990+
.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, '')
990991
.trim()
991992
.replace(/\s+/g, ' ');
992993
if (!text) continue;
993-
// Skip system-injected messages and slash command artifacts (not real user prompts)
994-
if (/^(Caveat:|init\b|clear\b|\/\w+ \w+$|You are a )/i.test(text)) continue;
994+
// Skip system-injected messages, slash command artifacts, and expanded skill prompts
995+
if (
996+
/^(Caveat:|init\b|clear\b|resume\b|\/[a-z][\w-]*\b|You are a |\[Request |Set model to )/i.test(text) ||
997+
/^(Please )?(analyze|review) this codebase/i.test(text) ||
998+
/^(Read|Implement the following) .+, then (search|list|check) /i.test(text) ||
999+
/^\d+ vulnerabilit/i.test(text) ||
1000+
/\btoolu_/.test(text) ||
1001+
/^[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]+/.test(text) ||
1002+
/\b(sk-ant-|ANTHROPIC_API_KEY|API_KEY=|SECRET|TOKEN=)/i.test(text) ||
1003+
text.length < 8
1004+
)
1005+
continue;
9951006
return text.length > MAX_PROMPT_LEN ? text.slice(0, MAX_PROMPT_LEN) + '…' : text;
9961007
} catch {
9971008
// Malformed line — skip
@@ -1012,6 +1023,25 @@ export function registerSessionRoutes(
10121023
}
10131024
}
10141025

1026+
/** Read the last `buf.length` bytes of a file (for tail-scanning user prompts). */
1027+
async function readFileTail(path: string, buf: Buffer, fileSize: number): Promise<string | null> {
1028+
try {
1029+
const fd = await fs.open(path, 'r');
1030+
const offset = Math.max(0, fileSize - buf.length);
1031+
const { bytesRead } = await fd.read(buf, 0, buf.length, offset);
1032+
await fd.close();
1033+
const text = buf.toString('utf8', 0, bytesRead);
1034+
// Skip first partial line when we didn't read from the start
1035+
if (offset > 0) {
1036+
const nl = text.indexOf('\n');
1037+
return nl >= 0 ? text.slice(nl + 1) : null;
1038+
}
1039+
return text;
1040+
} catch {
1041+
return null;
1042+
}
1043+
}
1044+
10151045
app.get('/api/history/sessions', async () => {
10161046
const projectsDir = join(process.env.HOME || '/tmp', '.claude', 'projects');
10171047
const results: Array<{
@@ -1074,6 +1104,14 @@ export function registerSessionRoutes(
10741104
}
10751105
if (head) firstPrompt = extractFirstUserPrompt(head);
10761106

1107+
// If head scan found no usable prompt (e.g. session started with /init),
1108+
// try reading the tail for a recent user message.
1109+
if (!firstPrompt && fileStat.size > 65536) {
1110+
const tailBuf = Buffer.alloc(32768);
1111+
const tail = await readFileTail(filePath, tailBuf, fileStat.size);
1112+
if (tail) firstPrompt = extractFirstUserPrompt(tail);
1113+
}
1114+
10771115
results.push({
10781116
sessionId,
10791117
workingDir,

0 commit comments

Comments
 (0)