export type Attachment = { id: string; kind: 'file' | 'lines' | 'paste'; filename: string; language: string | null; content: string; range?: [number, number]; source: '@' | 'line-select' | 'drop' | 'paste'; }; export const LANG_MAP: Record = { ts: 'typescript', tsx: 'tsx', js: 'javascript', jsx: 'jsx', mjs: 'javascript', cjs: 'javascript', py: 'python', go: 'go', rs: 'rust', rb: 'ruby', java: 'java', c: 'c', h: 'c', cpp: 'cpp', cc: 'cpp', hpp: 'cpp', cs: 'csharp', php: 'php', sh: 'bash', bash: 'bash', zsh: 'bash', yml: 'yaml', yaml: 'yaml', json: 'json', toml: 'toml', md: 'markdown', markdown: 'markdown', sql: 'sql', dockerfile: 'dockerfile', html: 'html', htm: 'html', css: 'css', scss: 'scss', }; export function inferLanguage(filename: string): string | null { const base = filename.split('/').pop() ?? filename; if (base.toLowerCase() === 'dockerfile') return 'dockerfile'; const m = base.match(/\.([^.]+)$/); return m ? (LANG_MAP[m[1]!.toLowerCase()] ?? null) : null; } export function flattenToMessage(attachments: Attachment[], text: string): string { if (attachments.length === 0) return text; const blocks = attachments.map(a => { const fence = '```' + (a.language ?? ''); let header: string; if (a.kind === 'lines') { header = `// from: ${a.filename}:${a.range?.[0] ?? '?'}-${a.range?.[1] ?? '?'}`; } else if (a.kind === 'paste') { header = `// from: pasted text (${a.content.split('\n').length} lines)`; } else { header = `// from: ${a.filename}`; } return `${fence}\n${header}\n${a.content}\n\`\`\``; }); return [...blocks, text].filter(Boolean).join('\n\n'); }