kanzo
Self-hosted, sharable kanban for humans and agents. One 4.5 MB binary, 4.5 MB of RAM, 10 to 60 tokens per agent reply, starts in 2 ms, zero setup.
翻譯準備中
What was promised, by whom, and what you walk into next — a local-first macOS menu-bar app that turns recorded meetings into commitments, briefs you before the next call, and also types what you say into any app.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A promise with a date turns up when the date passes. One without a date never turns up at all.
Griasa records your meetings on your own Mac, works out who promised what, and puts the second kind in front of you before your next call with the person you owe. A menu-bar app, not a service.
Download for macOS · The project page · What changed
Before the call. Minutes before a calendar event, a panel appears with who is on it and what you last discussed. Open promises are grouped by why you are being shown them — overdue, promised in an earlier call with exactly these people, from a one-to-one with somebody in the room, or with these people from somewhere else. A promise involving nobody in the room is not shown at all. One button opens the Zoom or Meet link and starts recording.
During. Your microphone and everything the Mac plays are captured as two tracks, which is what lets the transcript say who was speaking. Make a reminder from anything on screen (⌃⌥⌘R) and it remembers the app, window and browser tab it came from. Hold a key and talk, and the words land in whatever app has focus.
After. Promises are pulled out of the transcript — who took what on and by when — split into what you owe and what you are owed. A later conversation can propose closing one, and has to quote the sentence that says it was finished; nothing closes itself. Anything old and undated gets asked about, because that is the kind that never surfaces on its own. Every colleague gets a page built from the meeting roster, with no setup step.
And your own assistant can read it. Claude Code, Codex, Cursor — anything speaking MCP — over a local, token-protected, read-only endpoint, and Claude Desktop installs it in one click.
Everything runs at once: you can dictate while a conversation is being recorded. Most of the pieces are small, and Apple could ship several of them tomorrow. What is hard to copy is that they are one thing — the reminder made during a call, the promises extracted from that same call, the page for the person who made them and the brief before the next meeting with them all point at the same people and the same history. Separate apps would need separate integrations and would still lose the joins, because a join is not a feature any one of them owns.
The project page has the rest: more screenshots, what leaves your Mac and what doesn't, the engineering that turned out harder than it looked, and the questions people actually ask.
The short version is below; the full policy is at stari-am.github.io/griasa/privacy.html.
whisper.cpp. There is no Griasa server and no account.Download the latest release — signed, notarized, universal, or ./build.sh to compile it yourself.
With Homebrew, one command installs the app and both engines it uses (whisper.cpp for speech, llama.cpp for the on-device AI):
brew trust --cask stari-am/tap/griasa
brew install --cask stari-am/tap/griasa
Homebrew 7 loads casks from taps outside its own only after you trust them; the first line does that for this one cask.
Free, and nothing is held back: there is no license check, and under GPL-3.0 there could not be a meaningful one. If it saves you time, $29 on Ko-fi would make my day — any amount does, and none is fine too.
Think of it the way you think of WinRAR: the trial never ends and it keeps working whether or not you pay. WinRAR apparently makes real money like that, which leaves the mystery nobody has solved in thirty years — somebody pays. Feel free to be one of the somebodies.
Everything below is for running, building and changing it.
| Trigger | Hold Right Option / Right Command / Fn (configurable) — works in any app's text field |
| Insertion | Live typing as you speak, then a single corrected replacement on release |
| Auto edits | Filler words removed, punctuation restored, lists formatted, self-corrections resolved |
| Tone | The frontmost app's name is passed to the AI prompt, so Slack reads casual and Mail reads formal |
| Speech recognition | On-device (Apple recognizer, or whisper.cpp locally) — audio never leaves the Mac |
| Cloud usage | Only the short cleanup step may call an LLM, and only text, and only if you enable it |
| Languages | Any locale the system offers; two or three at a time run as parallel legs and the winner is locked in |
| Snippets | Typed abbreviations expand in place, including ;ai … ;; for an inline question |
./build.sh # produces "Griasa Dev.app"
open "Griasa Dev.app"
./test.sh # the invariant checks; release.sh runs these first
A local build announces itself: amber DEV icon, its own file name, "Griasa
Dev" in Privacy settings and Activity Monitor, and a squared-off menu-bar glyph.
It also uses its own bundle identifier, am.stari.griasa.dev. That last part is
not cosmetic — macOS stores a privacy grant against the identifier and the
signature it saw, so two differently-signed builds sharing one identifier
overwrite each other's grants and both end up asking every launch. Separate
identifiers cost one round of permission prompts, once. release.sh does none of
this; the signed build carries the real identifier, icon and name.
test.sh checks the rules live typing must never break — emitted text only ever
grows, a hypothesis that re-words the start of an utterance may not extend its end,
the language leg locks once. The first two were regressions that actually shipped;
they now fail a command instead of a text field. There is no test framework involved: swiftc compiles
PartialStabilizer.swift with the checks and the binary exits non-zero, so this
works on a machine with only the Command Line Tools. Each failure message states
the rule and why it matters, because "expected true, got false" tells whoever reads
it nothing.
Benchmark. How well Griasa finds promises is measured on six invented
meetings in benchmark/ — Russian, English, Ukrainian and
mixed, from a 12,000-character sync to a 90,000-character roadmap session — each
with the promises a person marked by hand. "Griasa Dev.app/Contents/MacOS/Griasa" --benchmark benchmark/meetings runs today's single pass, the long-meeting path
and (with --cloud <provider>) a cloud reference on them and prints recall,
precision and mine/theirs accuracy. Nobody in them is real, so anyone can rerun
it and compare.
./release.sh # signed + notarized dist/Griasa-<version>.dmg — the one on Releases
./dist.sh # ad-hoc signed dmg, no Apple account needed
release.sh is the path used for the published build: Developer ID signature, Hardened Runtime, Apple's notarization ticket stapled to both the app and the disk image, then verified the way a stranger's Mac will — spctl, stapler, and lipo -archs on the finished file rather than on the build settings. It needs a Developer ID Application certificate and a stored notarytool credential; run it without them and its preflight explains exactly what to create, including the two traps that cost the most time (importing the certificate with security import because a double-click reports -25294, and installing the G2 intermediate separately or codesign dies with "unable to build chain to self-signed root").
dist.sh is for handing a build to a colleague with no Apple Developer account: a universal dmg with an app icon, an /Applications shortcut, and INSTALL.txt. Being ad-hoc signed, recipients must right-click → Open once. Either way the rest is self-provisioning: on first launch the app installs whisper-cpp via Homebrew, downloads the model, and asks for the four permissions.
Builds on macOS 14+ with either full Xcode or the standalone Command Line Tools; runs on macOS 14+. build.sh compiles with swiftc directly, which needs no working SwiftPM — some standalone Command Line Tools installs ship a broken manifest library. Package.swift is equivalent and swift build works under full Xcode; use whichever you prefer.
Intel builds have a deadline. Xcode 26 is the last release that runs on Intel Macs and the last that gives you x86_64 for free. Under Xcode 27, a deployment target of 27.0 or later drops x86_64 from ARCHS_STANDARD silently — no error, just an arm64-only binary — and Intel software support ends altogether in macOS 28. This project targets macOS 14.0, so release.sh still produces a genuine universal binary; it verifies with lipo -archs rather than trusting the build settings, and you should keep doing that if you ever raise the target.
Patches welcome, and so is a fork. CONTRIBUTING.md covers the build, the invariant checks, and the handful of conventions a patch is most likely to trip over — where prompts live, why new UI is a hub tab rather than a window, and why screenshots must never be taken against real data.
Everything that identifies whose build this is lives in one file: Sources/Griasa/BuildConfig.swift. Edit it before building:
| Constant | What it drives | If left empty |
|---|---|---|
supportEmail | "Send Feedback" mail drafts | feedback buttons hidden |
donateURL | the ♥ donate button | donate buttons hidden |
updateRepo | daily update check against github.com/<owner>/<name> Releases | update checking off |
appName | mail subjects and version lines | — |
Code signing is parameterized via environment variables — no script edits needed:
CODESIGN_IDENTITY="My Own Cert" ./build.sh # dev build (default: "Griasa Dev Signing")
DIST_SIGN_IDENTITY="Developer ID Application: You (TEAM)" ./dist.sh # distributable (default: ad-hoc)
Bundle identifier and version live in Support/Info.plist — change CFBundleIdentifier if you distribute a fork, so your permission grants and update feed never collide with upstream builds.
Every system prompt Griasa sends to a model is in one file: Sources/Griasa/Prompts.swift — dictation cleanup, meeting notes, commitment extraction, colleague dossiers, project filing, document drafting, reminder parsing, chat replies, inline answers. Reading that file is enough to know exactly what the app asks a model to do; edit it and rebuild to change the app's behavior.
Runtime values are written {{placeholder}} and filled by the call site — {{today}}, {{participants}}, {{skeleton}} and so on. A placeholder the caller doesn't supply is left visible in the prompt rather than silently blanked, so a mistake shows up in the output instead of hiding.
You don't have to rebuild to experiment: Settings → AI & Actions → System prompts → Export for editing writes the current wording to ~/Documents/Griasa Prompts.json. Any key present there overrides the built-in version; delete a key (or the file) to go back. Reload edits picks up changes without a restart.
The prompt-preset library (Settings → Prompt presets) is deliberately not in that file — those are your own documents, edited in the UI and stored per user, not app behavior.
Always run the bundled app (
Griasa Dev.applocally,Griasa.apponce installed), not the bare binary — macOS permission prompts (mic, speech) need the Info.plist in the bundle.
On first launch Griasa asks for everything it needs. If a prompt doesn't appear, grant manually in System Settings → Privacy & Security:
| Permission | Used for |
|---|---|
| Microphone | dictation + recording |
| Speech Recognition | on-device transcription |
| Accessibility | global hotkey + inserting text (⌘V synthesis) |
| Screen Recording | system-audio capture (macOS gates app audio behind this) |
After granting Accessibility or Screen Recording, quit and relaunch Griasa.
Signing & permission stability: macOS ties privacy grants to the app's code signature. build.sh signs with a local self-signed certificate ("Griasa Dev Signing", created once in the login keychain), so the identity — and your permission grants — survive rebuilds. If that certificate is missing, the script falls back to ad-hoc signing and warns you (with ad-hoc, every rebuild looks like a new app and permissions reset). To recreate the certificate: generate a self-signed cert with the codeSigning EKU, import the .p12 (legacy format) into the login keychain, and security add-trusted-cert -r trustRoot -p codeSign it. If permissions ever get into a confused state, clear them with tccutil reset All am.stari.griasa and re-grant once.
Live typing can be turned off in Settings → Dictation (then everything is pasted once at the end) — useful in apps where synthetic keystrokes misbehave (e.g. fields with aggressive autocomplete).
Griasa keeps a lifetime time-saved counter (words dictated × the 40 wpm-typing vs 150 wpm-speaking gap) in the menu-bar panel and Settings → System.
On first launch a Welcome guide opens in the hub: quick start plus a live checklist of the three system permissions, each with a one-click jump to the right System Settings pane and a note on what still works if you decline. Reopen it anytime from Settings → System.
AI cleanup uses the configured AI provider (see below). Without one — or if the call fails — a local rule-based cleanup runs instead, so dictation always works offline.
Every AI feature routes through one configurable provider (Settings → AI & Actions). A new install is on the local model — nothing is sent to a cloud vendor until somebody chooses one.
127.0.0.1:8177. Short English cleanup jobs go to Apple's on-device model where the Mac has it. A server address or model names typed into Settings (customBaseURL, customFastModel, customSmartModel) are never replaced. Meetings that end before the model is ready are summarized afterwards. If no local model is running, the first AI action says so and how to fix it. With the built-in local Whisper, Griasa then runs fully offline — no text ever leaves the Mac. (Organizations: MDM guide.)ANTHROPIC_API_KEY. Keys entered in Settings are kept in the macOS login keychain, never in the preferences file.OPENAI_API_KEY (GPT-5.6 family by default).GEMINI_API_KEY (via Google's OpenAI-compatible endpoint; Gemini 3.x by default).claude or codex CLI is installed and logged in, Griasa answers through it, billed to the subscription you already pay for. Auto-detected (with an optional path override); expect ~5–10 s per request — great for meeting notes, dossiers, and documents, slower than an API key for typing-path features like ;tldr. (CLIRunner.swift)Each provider has a fast model (dictation cleanup, reminder parsing, classification, reply drafts) and a smart model (presets, summaries, Ask Project, meeting notes); defaults are sensible (Haiku 4.5/Opus 4.8, GPT-5.6 Luna/Terra, Gemini 3.1 Flash-Lite/3.5 Flash; Qwen3 8B/30B for a local server set up by hand — the model Griasa sets up itself serves both) and editable — for local endpoints a Load model list button offers what's actually installed, and Test pings the provider and reports latency. Meeting transcripts are trimmed to a context budget on local models (38k characters for the model Griasa sets up, which leaves room for the reply; 24k by default, configurable, for a server set up by hand; cloud models get 400k). An install from before local became the default, which already had a cloud key and no saved choice, keeps that provider — pasting the key was the choice. If the selected provider fails — the local model not running is the usual case — and a cloud provider is also available, explicit actions ask before re-sending that one request through the cloud ("ask each time", with an until restart option); dictation cleanup and background classification never ask — they degrade silently. (LLMProvider.swift, AIFormatter.swift)
Griasa prefers Whisper (whisper.cpp, large-v3-turbo) — dramatically more accurate than Apple's recognizer, fully local, with genuine automatic language detection built into the model.
Setup is automatic on first launch: the app installs whisper-cpp via Homebrew (if missing) and downloads the 1.6 GB model to ~/Library/Application Support/Griasa/, showing progress in the menu-bar menu and in Settings → Speech engine (with a Retry button if anything fails). Only Homebrew itself is assumed; without it the app tells you and keeps working on the Apple engine.
Every model is checked before it is used. Downloads come from a pinned repository commit, not a branch, and are verified against a SHA-256 and size written into the app (ModelManifest.swift) before they are moved into place — a file that does not match is never installed. Models already on disk are checked once after an update and remembered; one that fails is renamed *.unverified and replaced with a verified download. Manual equivalent:
brew install whisper-cpp
curl -L -o ~/Library/Application\ Support/Griasa/ggml-large-v3-turbo.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-large-v3-turbo.bin
shasum -a 256 ~/Library/Application\ Support/Griasa/ggml-large-v3-turbo.bin
# expect 1fc70f774d38eb169993ac391eea357ef47c88757ef72ee5943879b7e8e2bc69
Griasa keeps a local whisper-server running with the model loaded, so dictation transcribes near-instantly on hotkey release and meeting regions don't pay a per-call model load. When whisper-cli or the model is missing, everything falls back to the Apple recognizer.
Correct timeline on meetings: whisper.cpp's built-in --vad concatenates speech chunks and re-times them continuously, so text after long pauses drifts earlier (upstream issue #3634). Griasa avoids this by running Silero VAD itself (whisper-vad-speech-segments), slicing each speech region out of the audio, transcribing regions individually against the warm server, and stamping each with its true start time — segments stay in real chronological order even across long silences. A headless mode re-runs the pipeline on any past recording: "./Griasa Dev.app"/Contents/MacOS/Griasa --transcribe "<recording folder>".
-l auto) — nothing to configure.en-US, ru-RU) and keeps the transcription with the highest confidence score — per phrase for dictation, per 30-second chunk for meetings.Mixed-in English tech/crypto terms are handled two ways:
Select text anywhere — browser, Slack, PDF — and apply a prompt preset from the menu or its hotkey. Ships with Summarize (⌃⌥⌘S), Fix grammar (⌃⌥⌘G), Translate to English, Make it professional, To bullet points, and Rewrite as commit message. Editable presets that rewrite the text (grammar, translate…) show a Replace Selection button; summaries don't.
Add your own in Settings → AI & Actions: name, emoji, the instruction to the model, whether it replaces the selection, and an optional hotkey. Defaults use the triple-modifier ⌃⌥⌘ because global hotkeys on macOS can't be swallowed — the frontmost app also receives the keystroke, so the combo must be one no app binds. Griasa waits for you to release the modifiers before reading the selection (synthesizing ⌘C while modifiers are held can reach some apps as plain text and wipe the selection); your clipboard is saved and restored.
Every dictation, meeting transcript, and preset result is saved to a searchable History window (menu → History…). Search full text, then Copy, Insert, Reveal File, or Export. Stored locally in ~/Library/Application Support/Griasa/history.json (last 500 entries).
Export (from History and from any result popup) needs no external service or auth:
.md), PDF, HTML, RTF, or plain text.Markdown imports cleanly into Notion/Obsidian/GitHub; HTML and RTF paste with formatting into Docs/Confluence; PDF is print-ready. (Exporter.swift — a small dependency-free Markdown→HTML converter feeds macOS's rich-text and print systems.)
Four system-wide actions, each with a menu item and a configurable hotkey (Settings → Capture):
Documents/Griasa/Reminders/ and link it from the reminder (EventKit can't attach files directly), so you keep the pixels, not just the OCR text. (ReminderSource.swift; browser URLs need a one-time Automation permission per browser.);meet, ;sig) in any app and it expands in place, or insert from the menu. Templates support dynamic placeholders resolved at insert time: {date} {date+3d} {time} {clipboard} {meetlink} (your permanent Zoom/Meet room from Settings), {slot} / {slots:3} (the nearest free slots from your calendar — read-only EventKit, work hours configurable), {commitments} / {commitments:mine} / {commitments:theirs} (open promises from the Commitments tab as bullet points with the source meeting, date, and due date — ;todos pastes yours into any status update), {last-meeting} (the newest meeting's summary and who does what), {last-meeting:promises}, (what Name promised in the newest meeting with them), (everything Name still owes), and . The default produces e.g. . Settings → Snippets.Screen/window capture reuses the Screen Recording permission; Remind asks once for Reminders access. (CaptureController.swift, OCR.swift, RegionCapture.swift, WindowCapture.swift, Reminders.swift)
;todos snippet pastes your open promises anywhere you're typing. (Commitments.swift)Every history entry — dictation, meeting, capture result — is filed into a project automatically: the fast model matches the entry against each project's name and description (Settings → Projects), falling back to Inbox when unsure or offline. Re-assign any entry from the History window (project picker in the detail pane, filter in the sidebar), and run Categorize existing history once to file everything recorded before the feature existed.
Each project is mirrored on disk as plain Markdown: ~/Documents/Griasa/Projects/<Project>/YYYY-MM-DD-HHmm-<kind>-<id>.md with YAML frontmatter — browsable, git/Obsidian-friendly, and never deleted (removing a project moves its files to Inbox).
🗂 Ask project (⌃⌥⌘P or menu) opens a window where the smart model answers questions using the project's entries plus attached source folders as context — attach a repo, a docs folder, anything; text files are read newest-entries-first up to ~150k characters. Answers are saved back into the project's history. (Projects.swift, ProjectFiles.swift, ProjectAI.swift, AskProjectWindow.swift)
The Conversations tab imports a Telegram Desktop export (result.json; only the chats you tick and the period you pick) and reads it a day at a time with the model you already use: each day gets a short summary, and promises made in the chat — yours, and what others owe you — join your list with the sentence they came from. Search covers every message; Delete chat removes a chat's messages and summaries from the database. Messages stay on the Mac in conversations.sqlite (readable only by your account); an organization can cap how long they are kept (conversationRetentionDays, see Support/mdm), and summaries and promises outlive that limit. Source: ConversationStore.swift, ConversationProcessor.swift, ConversationRetention.swift.
Anything else can offer chats once you switch on Settings → System → Let assistants add to Griasa (off by default): the MCP tools below, or POST http://127.0.0.1:<port>/conversations with the endpoint's own bearer token (Copy token / Rotate token sit beside the switch; 2 MB, 30 requests a minute, local origins only). Offers land in a review list in the tab, held unread in the same database file; nothing is turned into messages, summarized or turned into promises until you press Accept, and unanswered ones expire after 14 days. If an organization has turned off AI assistants (MCP) with mcpEnabled, this door is closed too.
Switch on Settings → System → AI assistants (MCP) and Griasa serves a read-only endpoint on this Mac (127.0.0.1, port 8179 or the next free one, bearer token). It answers twelve questions, starting with get_context — everything about one person, project or topic in a single call within a size budget: open promises (yours, what you are waiting on, or only what your team owes you), decisions with their reasons and the words they were taken in, one colleague, meetings by search, one meeting's summary, a transcript (only when asked for), the next meeting's brief, the week's meetings with what you and each attendee owe each other, a follow-up email draft for a meeting (Griasa sends nothing), people, projects. It also offers four ready-made prompts that clients show as commands: prepare me for my next call, weekly review, write the follow-up, what do I owe this person. Nothing can be changed through it, unless you also switch on Let assistants add to Griasa: then two more tools appear. add_conversation offers a chat for your review, and propose_close_commitment suggests that a promise is done, with the sentence that says so, under Looks done. Neither can close, edit or delete anything — you decide.
Meetings are found by their words in any order and any grammatical form. Settings → System → Search by meaning adds an optional local model (bge-m3, 634 MB, run by llama-server on 127.0.0.1:8176 only) that also finds them by what was meant, across Russian, Ukrainian and English: each meeting is cut into pieces of about 800 characters, the pieces' vectors are kept in search.sqlite beside the history (readable only by your account), and a search ranks them together with word search. On the search benchmark the answer is among the first five results for 81% of queries, against 60% for words alone. Source: SemanticSearch.swift, WordSearch.swift.
Claude Desktop — one click. Press Add to Claude Desktop. Griasa writes Griasa.mcpb into its store folder and opens it in Claude Desktop, which asks you to confirm the install. The extension is a small Node script (Node ships inside Claude Desktop) that forwards Claude's requests to the app; it reads the port and token from mcp.json each time, so a new token or a different port needs no reinstall. Griasa has to be running — if it is not, Claude is told to open it from the menu bar.
Every assistant: the Griasa plugin. plugins/griasa holds a skill (SKILL.md in the open Agent Skills format — when to use which tool, transcripts last) and the same bridge the Claude Desktop extension uses, so no token goes into any configuration file. It needs Node.js.
| Assistant | Install |
|---|---|
| Claude Code (and Cowork on this Mac) | /plugin install griasa --marketplace Stari-am/griasa |
| Claude Desktop chat | Add to Claude Desktop above; for the skill, upload plugins/griasa as a plugin in Customize → Plugins |
| ChatGPT desktop app, Codex | Settings → MCP servers → Add server, or ~/.codex/config.toml: [mcp_servers.griasa] with command = "node" and args = ["<repo>/plugins/griasa/server/index.js"]; copy plugins/griasa/skills/griasa into Codex's skills folder |
| Gemini CLI | gemini extensions install https://github.com/Stari-am/griasa |
| Cursor, VS Code, others | the same node …/plugins/griasa/server/index.js command as a stdio server, or Copy client configuration in Griasa for the HTTP form {"mcpServers": {"griasa": {"type": "http", "url": "http://127.0.0.1:8179/mcp", "headers": {"Authorization": "Bearer …"}}}} |
| ChatGPT on the web, Grok | not supported: they only reach servers on the internet, and Griasa stays on your Mac |
Protocol. Streamable HTTP, MCP 2026-07-28 (stateless; server/discover; per-request version and headers) and, on the same endpoint, 2025-06-18 and 2025-11-25 clients with their initialize handshake. Tools carry titles, annotations and output schemas; the ten read tools are marked read-only, the two opt-in tools are marked as writes that do not destroy. Source: , , .
What an assistant reads goes wherever that assistant sends its context; transcripts are a separate tool so a question about promises cannot pull one in by accident.
Enable Show live notes while recording (Settings → Meetings). While a recording runs, the hub's Recording tab transcribes both tracks in ~18-second chunks against the warm Whisper server and shows a running summary (headline, key points, open questions, action items). The summary refreshes every minute when the Auto toggle is on, or only when you press ⟳ Summarize now — your choice. Below the transcript there's a note field: anything you type is pinned into the transcript at the current timecode (📝 rows), fed to the live summary as high-signal input, and woven into the final meeting notes — the model reflects your notes in the Summary/Key points/Action items and keeps them in the transcript as > 📝 **Note [mm:ss]:** blockquotes.
A recording nobody is speaking into used to run until somebody noticed — one left going overnight filled the disk. When neither the microphone nor the Mac's own audio has carried speech for a while, a small window asks whether to carry on. Keep recording restarts the clock, so the same wait asks again rather than never asking twice; with no answer the recording stops by itself, is transcribed exactly as a manual stop would be, and the transcript's last line says it stopped automatically and after how long. Both intervals are in Settings → Meetings → Silence (five minutes and two, by default). If speech resumes while the question is up, the question goes away and the recording continues. (SilenceWatch.swift, SilenceLevel.swift)
Everything lives in a single floating hub window with tabs: action results, the live Recording tab, the Custom-reminder date picker, the "who was on the call?" question, Ask Project, and History. A busy moment — recording a call while setting a reminder off a preset result — is one window with three tabs, not three windows fighting for focus. Closing a tab resolves its flow properly (a dismissed reminder deletes its orphan clip; closing the participants tab counts as "Skip" so the transcription pipeline never stalls). The pin beside the tabs keeps the window open when you switch apps — a panel hides itself on deactivation by default, which suits a popup and not an hour spent reading history. Only Settings (the standard macOS settings scene) and system dialogs (file save/open, the region-drag capture overlay) stay outside. (HubWindow.swift)
Set Your name and enable Ask who was on the call (Settings → Meetings). When a recording stops, Griasa asks which known people took part (roster is remembered) plus any new names, and the model attributes the transcript to real names — using conversational cues like self-introductions and people addressing each other by name — instead of generic "You"/"Them". This is content-based attribution (the two tracks are mic vs. everything-else, not per-speaker audio), so it's most accurate with the roster provided.
You: / Them: dialogue (transcript-raw.txt).meeting-transcript.md with a summary, key points, action items, and a cleaned speaker-labeled transcript — then opens it (toggleable).meeting-transcript.md.~/Documents/Griasa Recordings/<timestamp>/ contains:
microphone.caf — your voicesystem-audio.caf — everything the Mac played (call participants, videos…)transcript-raw.txt — timestamped merged dialoguemeeting-transcript.md — AI-formatted meeting notes⚠️ Consent: recording calls may require consent from all participants depending on your jurisdiction (many places are two-party-consent). Tell people you're recording.
Sources/Griasa/
├── GriasaApp.swift # MenuBarExtra app entry
├── AppState.swift # Central state + settings (UserDefaults)
├── HotkeyMonitor.swift # Global flagsChanged monitor (hold-to-talk)
├── MicCapture.swift # Single shared AVAudioEngine mic tap, multi-consumer
├── DictationEngine.swift # Parallel per-language recognizers, best-confidence pick
├── Vocabulary.swift # Built-in tech/crypto contextual vocabulary
├── LLMProvider.swift # Provider config (Anthropic/OpenAI/Gemini/local) + fallback consent
├── AIFormatter.swift # Provider router, dictation cleanup + local fallback
├── TextInserter.swift # Pasteboard swap + synthetic ⌘V
├── SystemAudioCapture.swift # ScreenCaptureKit audio stream → .caf
├── ConversationRecorder.swift # Session folders, mic+system tracks
├── WhisperTranscriber.swift # whisper.cpp integration (preferred engine)
├── WhisperInstaller.swift # First-launch auto-install: brew + model download
├── FileTranscriber.swift # Apple-recognizer fallback (chunked, timestamped)
├── MeetingTranscriber.swift # Merge tracks + AI meeting-notes formatting
├── Permissions.swift # TCC requests/status
├── SelectionActions.swift # Global preset hotkeys, selection grab
├── PromptPresets.swift # Prompt-preset model + store (built-in + custom)
├── HubWindow.swift # The shared tabbed hub window all activities open in
├── PopupController.swift # Result tab content (preset/action output)
├── HistoryStore.swift # Persistent searchable history
├── HistoryWindow.swift # History browser (hub tab)
├── Participants.swift # Speaker roster + "who's who" prompt
├── LiveNotes.swift # Realtime transcript, typed notes + running summary
├── LiveTyper.swift # Synthetic keystrokes for live dictation typing
├── CaptureController.swift # Capture actions: remind / OCR / draft reply / clipboard
├── CaptureAI.swift # AI calls for reminder parsing + reply drafting
├── OCR.swift # On-device text recognition (Apple Vision)
├── RegionCapture.swift # Interactive screen-region screenshot
├── WindowCapture.swift # Frontmost-window screenshot
├── Reminders.swift # EventKit reminder creation
├── ReminderSource.swift # source app / window / tab URL for reminders
├── Projects.swift # Project model + store (auto-categorized history)
├── ProjectFiles.swift # Markdown mirror: one .md per entry per project
├── ProjectAI.swift # AI classification + Ask Project context/Q&A
├── AskProjectWindow.swift # Ask Project window (question → answer over context)
├── Exporter.swift # Markdown → MD/PDF/HTML/RTF/plain export + rich copy
├── Prompts.swift # EVERY system prompt, in one place (JSON-overridable)
├── Snippets.swift # Snippet model + store (defaults incl. ;meet)
├── SnippetEngine.swift # Placeholder rendering: date/clipboard/meetlink/slot/ai
├── SnippetExpander.swift # Type-anywhere abbreviation expansion + `;ai … ;;` inline ask
├── FreeSlotFinder.swift # EventKit free-slot search for {slot}
├── Commitments.swift # Promise model/store + AI extraction from meetings
├── CommitmentsView.swift # ✅ Commitments hub tab (mine / theirs / done)
├── People.swift # Person pages store, avatars, AI dossier
├── PeopleView.swift # 📇 People hub tab (notes, meetings, promises)
├── MeetingPrep.swift # Calendar watcher + pre-meeting brief assembly
├── PrepView.swift # 📋 Prep hub tab (countdown, Join & Record)
├── DocTemplates.swift # Editable PRD/RFC/1-pager/postmortem templates + generator
├── NewDocumentView.swift # 📄 New Document hub tab (brief → filled draft)
├── OnboardingView.swift # First-launch welcome guide + live permission checklist
├── UsageStats.swift # Lifetime dictation counters ("time saved vs typing")
├── UpdateChecker.swift # Daily GitHub-Releases update check (pre-Sparkle)
├── BuildConfig.swift # ← fork here: support email, donate URL, update repo
├── Support.swift # Donate / feedback actions driven by BuildConfig
├── HubStyle.swift # Shared hub visual language: cards, empty states
├── MenuView.swift # Menu-bar popover UI
└── SettingsView.swift # Settings window
Design notes:
MicCapture installs a single AVAudioEngine tap and fans buffers out, so dictation and recording never fight over the input device.requiresOnDeviceRecognition). Only the short cleanup step sends text (never audio) to the configured provider, and only if you enable it.Prompts.swift (key dictationCleanup) is the place to inject a custom dictionary.ffmpeg -filter_complex amix post-step could produce a single file.ggml-large-v3.bin into ~/Library/Application Support/Griasa/ (3 GB) and update the filename in WhisperTranscriber.swift.GPL-3.0. Build it, change it, share it — derivatives stay open under the same license. If Griasa saves you time, the ♥ button in the menu is appreciated.
{last-meeting:Name}{promises:Name}{ai: instruction};meet;ai … ;; — ask without leaving the sentence. Type ;ai in any app, write the question, close it with ;;, and the whole run is replaced in place by the answer: ;ai как по-английски "предварительный расчёт";;. No snippet to set up — it's built in, and it reads everything between the markers, so the question can be as long as you like. Escape or moving the caret cancels; if the model fails, your typed text comes back untouched. (SnippetExpander.swift){ai:} — an AI you can type into any text field. The {ai: instruction} placeholder inserts whatever the model writes, right where your cursor is — and placeholders nest, so the prompt can see live context: ;tldr expands {ai: Summarize in 3 bullets: {clipboard}} into a summary of whatever you copied, ;en translates the clipboard into English, and a one-line custom snippet turns any repeated writing task (polite decline, standup opener, commit-message style) into three keystrokes. Works in Slack, Jira, code reviews — anywhere text goes. (Draws on the model's knowledge and your local context; it doesn't browse the web.)griasa://open/<tab>. Right-click the desktop → Edit Widgets… → search "Griasa". (GriasaWidget.swift, WidgetSnapshot.swift)