| 88 | 88 | | /** The most of a prompt's intent that titles a session. */ |
| 89 | 89 | | export const TITLE_LIMIT = 80; |
| 90 | 90 | | |
| 91 | + | /** A turn the harness starts itself, as a tag: `<command-name>`, `<task-notification>`, one with attributes. */ |
| 92 | + | const HARNESS_TAG = /^\s*<[a-z][\w-]*(?:\s[^>]*)?>/i; |
| 93 | + | |
| 91 | 94 | | /** |
| 92 | 95 | | * What a prompt is for, as a session's title: its first line or sentence, |
| 93 | 96 | | * without code blocks or markdown, whitespace collapsed, clipped at |
| ⋯ |
| 97 | 100 | | */ |
| 98 | 101 | | export function intentTitle(prompt: string | null | undefined): string | null { |
| 99 | 102 | | let text = String(prompt ?? "").replace(/^\uFEFF/, ""); |
| 100 | | − | if (/^\s*\//.test(text) || /^\s*<[a-z][\w-]*>/i.test(text)) return null; |
| 103 | + | // What the harness pasted in or attached is not what the prompt is for: |
| 104 | + | // pasted text (Claude Code's <pasted_content id="…">…</pasted_content>) |
| 105 | + | // and image placeholders ([Image #1], [Image: source: …]) go first. |
| 106 | + | text = text |
| 107 | + | .replace(/<pasted_content\b[^>]*>[\s\S]*?(?:<\/pasted_content[^>]*>|$)/gi, "\n") |
| 108 | + | .replace(/\[Image(?: #\d+)?(?::[^\]\n]*)?\]/g, " "); |
| 109 | + | if (/^\s*\//.test(text) || HARNESS_TAG.test(text)) return null; |
| 101 | 110 | | text = text |
| 102 | 111 | | .replace(/```[\s\S]*?(```|$)/g, "\n") |
| 103 | 112 | | .replace(/~~~[\s\S]*?(~~~|$)/g, "\n") |
| ⋯ |
| 130 | 139 | | session: Pick<WorkSession, "title" | "branch" | "firstPrompt">, |
| 131 | 140 | | firstPrompt: string | null | undefined = session.firstPrompt, |
| 132 | 141 | | ): string { |
| 142 | + | // A title that is only a harness tag (an early plugin's from a pasted prompt) reads as none. |
| 133 | 143 | | const title = session.title?.trim(); |
| 134 | | − | if (title) return title; |
| 144 | + | if (title && !HARNESS_TAG.test(title)) return title; |
| 135 | 145 | | const intent = intentTitle(firstPrompt); |
| 136 | 146 | | if (intent) return intent; |
| 137 | 147 | | return session.branch ? `Session on ${session.branch}` : "Untitled session"; |