import { escapeHtml } from './format-telegram.js' import { readDailyNote, parseFrontmatter, ensureDailyNote, todayIso, vaultRelFrom, } from './vault.js' import { listVaultNotes } from './memory-graph.js' import { calendarBetween } from './calendar.js' import { gmailSince, gmailTopByImportance } from './gmail.js' import { googleAuthConfigured, googleTokenSaved, } from './google-auth.js' import { taskRows, shortTaskId } from './tasks.js' export type BriefSummary = { html: string markdown: string } function fmtTime(ms: number | null): string { if (!ms) return '—' return new Date(ms).toLocaleTimeString('en-IN', { hour: '2-digit', minute: '2-digit', hour12: false }) } export async function composeMorningBrief(): Promise { const date = todayIso() await ensureDailyNote(date) const raw = (await readDailyNote(date)) ?? '' const { frontmatter } = parseFrontmatter(raw) const required: string[] = [] if (frontmatter.gym_required === 'true') required.push('gym') if (frontmatter.thesis_required === 'true') required.push('thesis') required.push('kit', 'meditation') const now = Date.now() const lastDay = now - 24 * 3600_000 const nextDay = now + 24 * 3600_000 // Calendar let calendarLines: string[] if (googleAuthConfigured() && googleTokenSaved()) { const events = calendarBetween(now - 3600_000, nextDay) calendarLines = events.length === 0 ? ['no events in next 24h'] : events.slice(0, 10).map(e => { const time = fmtTime(e.starts_at) const sum = escapeHtml(e.summary ?? '(no title)') const link = e.meet_link ? ` · meet` : '' return `• ${time} ${sum}${link}` }) } else { calendarLines = ['not configured — run `npx tsx scripts/setup-google.ts`'] } // Gmail — ranked by LLM-scored importance, not user labels. let gmailLines: string[] if (googleAuthConfigured() && googleTokenSaved()) { const ranked = gmailTopByImportance(lastDay, 8) const items = ranked.length > 0 ? ranked : gmailSince(lastDay, 8) gmailLines = items.length === 0 ? ['no inbox activity in last 24h'] : items.map(m => { const sender = escapeHtml((m.sender ?? '').split('<')[0]?.trim() || 'unknown') const subj = escapeHtml(m.subject ?? '(no subject)') const score = m.importance !== null && m.importance !== undefined ? ` · ${m.importance}` : '' const why = m.importance_reason ? ` ${escapeHtml(m.importance_reason)}` : '' return `• ${sender} — ${subj}${score}${why}` }) } else { gmailLines = ['not configured — run `npm run setup:google`'] } const tasks = taskRows(8).filter(t => t.status !== 'completed') const taskLines = tasks.length === 0 ? ['no open tasks tracked'] : tasks.map(t => { const due = t.due_ts ? ` · ${escapeHtml(new Date(t.due_ts).toLocaleDateString('en-IN'))}` : '' return `• ${escapeHtml(shortTaskId(t.id))} ${escapeHtml(t.title)}${due}` }) // Vault const inboxFiles = listVaultNotes('04_Notes/inbox') const recentInbox = inboxFiles .sort((a, b) => b.mtime - a.mtime) .slice(0, 5) .map(n => vaultRelFrom(n.absPath)) let openIdeas: string[] = [] try { openIdeas = listVaultNotes('06_Projects/ideas') .filter(n => n.relPath.endsWith('/index.md')) .sort((a, b) => b.mtime - a.mtime) .slice(0, 3) .map(n => vaultRelFrom(n.absPath)) } catch { /* ignore */ } const htmlLines: string[] = [ `Morning brief · ${date} · ${escapeHtml(frontmatter.weekday ?? '?')}`, '', `Required today: ${required.map(escapeHtml).join(', ')}`, '', 'Calendar · next 24h', ...calendarLines, '', 'Inbox · priority · 24h', ...gmailLines, '', 'Google Tasks · open', ...taskLines, '', `Vault inbox (recent) · ${recentInbox.length}`, ...recentInbox.map(p => `• ${escapeHtml(p)}`), ] if (openIdeas.length > 0) { htmlLines.push('', `Open ideas · ${openIdeas.length}`) htmlLines.push(...openIdeas.map(p => `• ${escapeHtml(p)}`)) } htmlLines.push('', `Daily note${escapeHtml(`03_Daily/${date}.md`)}`) const html = htmlLines.join('\n') const markdownLines = [ `## Brief — ${date}`, '', `- Required: ${required.join(', ')}`, `- Calendar: ${calendarLines.length === 1 && calendarLines[0]?.includes('not configured') ? 'n/a' : `${calendarLines.length} items`}`, `- Gmail priority: ${gmailLines.length === 1 && gmailLines[0]?.includes('not configured') ? 'n/a' : `${gmailLines.length} items`}`, `- Google Tasks: ${tasks.length} open`, `- Vault inbox (${recentInbox.length}):`, ...recentInbox.map(p => ` - [[${p}]]`), ] if (openIdeas.length > 0) { markdownLines.push(`- Open ideas (${openIdeas.length}):`) markdownLines.push(...openIdeas.map(p => ` - [[${p}]]`)) } const markdown = markdownLines.join('\n') return { html, markdown } }