Build "Mini-scribe" — an AI meeting notetaker on Claude using Minimi:
Before you paste this: you need (1) the Minimi memory connector installed and your laptop online, and (2) an environment that can create live artifacts (e.g. Claude Cowork). A strong model helps — this is a one-pass build. Paste everything below this line.
Build me a live artifact called Mini-scribe — a Granola-style AI meeting notetaker that runs entirely on my Minimi memory. It's a self-contained, light-mode HTML page (inline all CSS/JS, :root{color-scheme:light}) that pulls fresh from Minimi each time it opens. Brand it "Mini-scribe · built with Minimi". Design: off-white background, dark text, green accent; sidebar main panel. Don't build a page-reload button (the artifact header has one).
Use my Minimi tools via window.cowork.callMcpTool(...): meeting_memory (actions list, get_context), list_active_threads, and search_memory. Use window.cowork.askClaude(prompt, data[]) for all AI generation.
Probe first. Before coding, call each tool once and build parsers around the ACTUAL responses — Minimi returns markdown, not JSON. Expect these shapes (verify them yourself):
Every response may start with an italic line _User local time: ... (timezone)._ — skip it.
meeting_memory list → ### N. Title blocks with - **App:**, - **Thread ID:** (UUID), and — only for real calls — - **Started:** (ISO UTC) - **Duration:** ("1 min", "1 hr 5 min"). Plain chat threads appear in the same list WITHOUT Started/Duration. time_range accepts {day:"YYYY-MM-DD"}; limit caps at 20.
meeting_memory get_context → ## Title, the same fields, then ### Transcript with the transcript inside a ``` code fence. Lines are tagged [You] / [Others]. The ASR is noisy: mixed English Hindi, garbled words, and the same sentence often ECHOED on both speakers' lines.
list_active_threads → ### N. App / ThreadName blocks with - **Last updated:** - **Preview:**. Thread names can be URLs — split app/name on the FIRST " / " only. Takes time_range:{from,to} in epoch ms.
search_memory → ### Result N blocks with - **Source:** App / Name, - **Captured:**, optional - **Memory:** gist, and a - **Context:** code fence. time_range supports {day, between:{from:"HH:MM", to:"HH:MM"}} — between cannot cross midnight, so split windows that do.
Read results defensively: r.structuredContent ?? JSON.parse/r.content[0].text style, tolerate missing fields.
Requirements
Calls only. Keep a meeting only if it has BOTH a start time AND a non-zero duration. Exclude plain chat threads; drop 0-minute recordings.
Beat the 20-item cap. Recent chats bury older calls in list. Fetch one day at a time ({day:"YYYY-MM-DD"}, limit 20) across the window, with a small concurrency pool; dedupe by thread ID; sort newest-first.
Load-more paging. Start with the last 10 days. A "Load 10 more days" button fetches the next 10-day window and appends, showing days loaded. A whole window with no new calls → "No more calls", stop — but NEVER treat an offline response as "ended" (see 9).
Group-by switcher in the sidebar (top of the drawer, full-width tabs): Date (default, Today/Yesterday separators), People, App, Theme. Theme = 1–2 word category from one AI pass over titles. Resolve People/Theme lazily on first select with a progress state; cache in localStorage.
Per-call notes fused with screen context. On open: fetch the transcript AND screen context via search_memory (query = call title, window ≈ 5 min before start → 90 min after end, midnight-split). Pass BOTH to askClaude; demand STRICT JSON: {summary, keyPoints[], actionItems[{who,task}], decisions[], followUps[], resources[{label,url}], related[{name,note}]}. Instruct it to: use screen context to fix garbled transcript terms (names, companies, numbers → the on-screen spelling); weave genuinely related on-screen activity into the summary; capture follow-ups that already happened right after the call; drop unrelated tabs. Render Granola-style; Follow-ups/Resources(clickable)/Related sit behind a persistent show/hide "screen context" toggle. Cache notes per thread in localStorage; add "Regenerate". Parse the AI reply leniently (strip fences, first-{-to-last-}, retry once).
Transcript tab with color-coded [You] vs [Others] bubbles, plus:
⧉ Copy — copies the visible version with a title/date header and real speaker names. Make it sandbox-proof: try a synchronous hidden-textarea execCommand("copy") INSIDE the click gesture first, then navigator.clipboard, and if both are blocked open a modal with the text pre-selected for ⌘C.
✨ Enhanced view (Raw | Enhanced toggle) — an AI cleanup pass fed the transcript screen context the notes summary: correct mis-heard words to on-screen spellings, merge/drop echoed duplicate lines (attribute each sentence to the actual speaker), fix punctuation/casing, keep Hindi as Hindi, never invent or summarize. Output plain [You]/[Others] lines. Chunk long transcripts on line boundaries (~6k chars) with progress; validate output (tags present, ≥25% of input length; one retry) and never overwrite a good transcript with garbage. Cache per thread; offer Redo.
✎ Fix words — user-editable corrections for mis-transcribed words (names, companies, products — e.g. "Shrum → Shram"), scoped per-call or all-calls, persisted, removable. Case-insensitive with Unicode word boundaries (Devanagari-safe, must not corrupt longer words). Apply display-time to titles, every notes section, both transcript views, copies, and chat answers (instantly, even cached ones), and to everything sent to the AI — but never to the user's own typed questions or URLs.
Ask tab — chat that answers questions from ONLY the selected call's transcript via askClaude, with brief Q&A history.
Participant identification ("With: ___"). Blend three signals: (a) greeting/vocative in the opening lines, English or Hindi ("Hey Sara", "हां, आर्यन") — strong; (b) a contact's personal DM thread active around the call via list_active_threads (~10 min before → 75 min after) — ignore group chats, channels, and web/app entries; (c) the title. Greeting matching a thread name = high confidence; voice notes with no counterpart = "—". Show an editable "With: name · source" line; manual edits persist in localStorage and override the AI. The People grouping must use this resolved participant and read transcripts (batch: fetch transcripts with a pool, then ONE askClaude call for all unresolved calls).
Offline handling. Minimi is local-only: when the laptop is asleep/offline its tools return text like "device is not currently connected / must be running and online". Detect that (regex on response text), show a "laptop offline" banner with Try-again wired to retry the failed operation, and never mark the list ended or show a false empty state because of it.
In-app ↻ refresh — re-queries the last ~3 days, merges without losing loaded history, spin state " N new" / "Up to date" flash.
Hide calls. Hover ✕ on a sidebar item hides it (persisted); footer shows "N hidden" which toggles revealing them dimmed with an ↩ unhide button; hidden calls are excluded from groups, counts, and AI passes; handle the "everything hidden" state.
Sidebar chips: per-app colored dot app name time duration ( participant when known).
Engineering bar. One <script>, no external libraries, event delegation (artifact CSP-friendly), escape all injected text, all caches namespaced in localStorage with try/catch, transcripts cached in memory only. Before publishing: extract the JS, syntax-check it (e.g. node --check), and unit-test the markdown parsers against the real probed outputs.
Finally: publish it as an artifact, listing only the three Minimi tools you actually called.Progress10 of 10Probe Minimi tools to capture actual response shapesBuild Scribe HTML artifact with all featuresVerify JS parses and publish artifactAdd transcript copy AI-enhanced transcript viewAdd hide/unhide for callsAdd name-fix (find/replace) for transcriptsGeneralize to word fixing across title, notes, chatMake transcript copy bulletproof in the sandboxMove group-by switcher into the sidebarWrite shareable build-prompt for Mini-scribeWorking foldermini-scribe-prompt.mdscribe.htmlContextConnectorsminimi