( transcript: T[], )
| 1745 | * Used to determine if a session has valid user interaction. |
| 1746 | */ |
| 1747 | export function getFirstMeaningfulUserMessageTextContent<T extends Message>( |
| 1748 | transcript: T[], |
| 1749 | ): string | undefined { |
| 1750 | for (const msg of transcript) { |
| 1751 | if (msg.type !== 'user' || msg.isMeta) continue |
| 1752 | // Skip compact summary messages - they should not be treated as the first prompt |
| 1753 | if ('isCompactSummary' in msg && msg.isCompactSummary) continue |
| 1754 | |
| 1755 | const content = msg.message?.content |
| 1756 | if (!content) continue |
| 1757 | |
| 1758 | // Collect all text values. For array content (common in VS Code where |
| 1759 | // IDE metadata tags come before the user's actual prompt), iterate all |
| 1760 | // text blocks so we don't miss the real prompt hidden behind |
| 1761 | // <ide_selection>/<ide_opened_file> blocks. |
| 1762 | const texts: string[] = [] |
| 1763 | if (typeof content === 'string') { |
| 1764 | texts.push(content) |
| 1765 | } else if (Array.isArray(content)) { |
| 1766 | for (const block of content) { |
| 1767 | if (block.type === 'text' && block.text) { |
| 1768 | texts.push(block.text) |
| 1769 | } |
| 1770 | } |
| 1771 | } |
| 1772 | |
| 1773 | for (const textContent of texts) { |
| 1774 | if (!textContent) continue |
| 1775 | |
| 1776 | const commandNameTag = extractTag(textContent, COMMAND_NAME_TAG) |
| 1777 | if (commandNameTag) { |
| 1778 | const commandName = commandNameTag.replace(/^\//, '') |
| 1779 | |
| 1780 | // If it's a built-in command, then it's unlikely to provide |
| 1781 | // meaningful context (e.g. `/model sonnet`) |
| 1782 | if (builtInCommandNames().has(commandName)) { |
| 1783 | continue |
| 1784 | } else { |
| 1785 | // Otherwise, for custom commands, then keep it only if it has |
| 1786 | // arguments (e.g. `/review reticulate splines`) |
| 1787 | const commandArgs = extractTag(textContent, 'command-args')?.trim() |
| 1788 | if (!commandArgs) { |
| 1789 | continue |
| 1790 | } |
| 1791 | // Return clean formatted command instead of raw XML |
| 1792 | return `${commandNameTag} ${commandArgs}` |
| 1793 | } |
| 1794 | } |
| 1795 | |
| 1796 | // Format bash input with ! prefix (as user typed it). Checked before |
| 1797 | // the generic XML skip so bash-mode sessions get a meaningful title. |
| 1798 | const bashInput = extractTag(textContent, 'bash-input') |
| 1799 | if (bashInput) { |
| 1800 | return `! ${bashInput}` |
| 1801 | } |
| 1802 | |
| 1803 | // Skip non-meaningful messages (local command output, hook output, |
| 1804 | // autonomous tick prompts, task notifications, pure IDE metadata tags) |
no test coverage detected