This is a submission for the Hacktoberfest Open-Source AI Challenge: Week 2, Side Hustle.
Disclosure: this post was drafted with Claude Code, the agent that built the project with me, from the repository's own records (README, docs/BENCHMARKS.md, docs/DECISIONS.md) and our session, linked at the end. Every number comes from those files.
What I Built
PostCall is a desktop app that turns a recorded client discovery call into a draft proposal, without the recording ever leaving your computer.
Writing the proposal is the unpaid work after every discovery call, and the obvious fix, pasting the call into a cloud AI, is often not allowed: discovery calls contain the client's plans, numbers and sometimes an NDA. So everything runs locally:
- Upload an mp3, m4a, wav or mp4 of the call.
- Whisper transcribes it with timestamps and detects the language. The audio is deleted afterwards unless you keep it.
- Gemma 4 extracts structured notes (client, problem in their own words, goals, requirements, budget or deadline mentioned, risks, open questions).
- The proposal is drafted from those notes plus your own profile (services, rates, terms, tone), in the call's language or any of eleven.
- You edit it and export Markdown or PDF.
Who would pay: freelancers and small agencies who run discovery calls and then write scope-and-price proposals. The pricing idea is a one-time license with no subscription, possible only because inference costs nothing to run. Nothing is charged today, and I have not yet measured whether anyone would pay.
What keeps the draft honest. A proposal that invents a requirement or a price is worse than none, so these rules live in code, not just in the prompt:
- Every scope item and deliverable links to the transcript moment it came from; click it to read the exact quote. The quote is looked up by the app, never written by the model.
- Anything without a source is flagged as an assumption. Segment ids the model invents are dropped (
repair_draftinapi/app/drafting.py). - Prices come only from your profile. The model never sees your rates, a client's budget is shown as a note and never used as a price, and money amounts in model text are rejected.
- If the call did not cover something, the draft says so and adds it to the open questions.
It still makes mistakes (in the sample call it listed both an "automatic invoicing system" and "un menú de facturación"), which the source links make easy to spot. The README says to read the draft before sending it.
Demo
There is no hosted demo, on purpose: the point is that nothing is hosted. PostCall-1.0.0-arm64.dmg is 306 MB (Apple Silicon, macOS 14+) and unsigned, so the first launch needs the "Open Anyway" steps in the README. On first launch it downloads the models once (7.2 GB); after that it works offline.
Code
RZEROSTERN
/
hacktoberfest-week3
This app is intended for freelancers so they can make proposals after an interview.
PostCall
Turn a recorded client discovery call into a draft proposal in minutes. On your own Mac, with open-source AI, without sending the call to anyone's server.
PostCall is a macOS app for freelancers and small agencies. You upload the recording of a call, and it transcribes it, pulls out what the client actually said, and drafts a proposal from those notes and your own rates. Everything runs on your computer: the speech model, the language model and the data. After a one-time model download it works fully offline.
Built for the DEV Open-Source AI Challenge, Week 2: "Side Hustle". Source code: MIT with a Beerware thank-you (see License).
The problem
Writing the proposal is unpaid work that happens after every discovery call. It takes hours, and it is the first thing that slips when you are busy. The obvious fix, pasting the call into a cloud AI, is…
MIT, with a Beerware thank-you. api/ is FastAPI, web/ a Nuxt 4 app, desktop/ the Electron shell; docs/DECISIONS.md logs every technical decision and why.
How I Built It
| Job | Open piece | License |
|---|---|---|
| Speech to text | Whisper large-v3-turbo (q5_0) on whisper.cpp, Metal |
MIT |
| Notes and proposal | Gemma 4 E4B through Ollama v0.35.1, bundled; JSON output validated with Pydantic | Apache 2.0 / MIT |
| Service and screens | FastAPI + SQLite, Nuxt 4, Electron | MIT / BSD |
At run time there is no closed model and no cloud inference; the only traffic is the model download the user approves.
I picked the model by measuring. I set targets first (30 minutes of audio transcribed in under 10 minutes, notes plus proposal in under 3, at least 90% of the expected key facts, nothing invented) and ran five models on the same Spanish role-play call, on an M2 Pro with 16 GB:
| Model | Download | 5-min call | 32-min call | Key facts | Verdict |
|---|---|---|---|---|---|
gemma4:e4b |
6.6 GB | 64 s | 160 s | 12 / 13 | Meets the targets. The default. |
qwen3.5:9b |
6.6 GB | 126 s | 322 s | 12 / 13 | Too slow at 30 minutes; two small inventions. |
qwen3.5:4b |
3.3 GB | 42 s | 175 s | 9 / 13 | Missed facts, invented an out-of-scope item. |
mistral-nemo:12b |
7.1 GB | 82 s | not run | 8 / 13 | Thin notes. |
gemma4:12b |
8.0 GB | 239 s | not run | 4.5 / 13 | Too slow and too terse on 16 GB. |
whisper.cpp transcribed 32 minutes in 88 s; faster-whisper on the CPU needed 666 s. The bigger Gemma was the worst fit, and I only know because open weights let me swap and measure. The limits are in the README: one 5-minute call, one run each, 13 key facts, scores assigned by Claude and not yet confirmed by a person.
What went wrong, and the fixes:
- Quotes. My first prompt asked the model for a word-for-word quote per item. Prompt v2 asks only for text and segment ids, and code looks the quote up: 11.5/13 facts in 103 s became 12/13 in 64 s.
-
Long audio. On the 32-minute file whisper.cpp's defaults produced 1,487 tiny fragments instead of about 240 segments. The app re-runs with
-mc 0when the output looks like that. - Missing questions. The default model did not ask about budget or deadline, so code adds those questions whenever the notes hold neither.
- Arabic. Running the real model per language showed Arabic proposals with Arabic titles over a Spanish body. A prompt fix and a test per script solved it (Arabic 100% in the right script).
Shipping it. The Electron shell starts the bundled Ollama and the API on two free loopback ports, restarts a crashed service once and stops everything on quit; the first-launch flow downloads, verifies and compatibility-checks the models, with pause and resume. The interface and proposals work in eleven languages, including right-to-left Arabic (the translations are AI-drafted and not yet reviewed by native speakers).
Working with the agent. The brief had hard rules: GitFlow, one branch and pull request per step, no stacked PRs, check official docs instead of guessing, log every decision. That became 18 merged PRs and 382 unit tests (235 API, 95 web, 52 desktop). The release checks drive the packaged app over the DevTools protocol (22 lifecycle checks, 26 end-to-end); the final run passed 25 of 26, the miss being the speed target on a busy Mac (194 s against 180 s), and one unexplained flaky step is recorded in DECISIONS.md rather than hidden.
After 1.0.0, I started .deb and .rpm packages: a separate LGPL-only FFmpeg instead of GPL-bundling PyAV, built in Docker. It is not released yet and nothing has been installed on real Linux hardware, so I won't claim more. I also decided against a hosted server for Windows users: it would move clients' calls off their computer, which is what the product exists to avoid.
Why Does Open Innovation Matter?
- Privacy you can verify. The whole path from recording to proposal is open code and open models on the user's machine, so "your NDA call stays private" is checkable in the source, not a policy promise.
- Cost. Zero cost per proposal is what makes a one-time license possible; a cloud API would turn it into a subscription.
- Choice. The model is a setting: five tested, any Ollama model allowed, cloud models refused, and every model passes the same compatibility check.
- Shippable. Ollama, whisper.cpp and the models can be redistributed inside one installer with no accounts or servers.
The trade-off is real: a small local model is a weak link (every model missed the same key fact, and the default's open questions were thin), so the app does the checking, flagging and repairing. For this product I think that is the right trade.
My Agent Session
The whole build, from "confirm the CLAUDE file you have in the root" to the request for this post, saved with DevRelay as one session: Building PostCall: a local-first proposal writer with Whisper and Gemma 4 (288 messages). It is long, so I link it instead of embedding it. Private reasoning, screenshots and system prompts are omitted, long tool output is truncated, and paths, emails and tokens are scrubbed.
My First User
Prize Categories
Best Use of Gemma. Gemma 4 E4B is PostCall's default and recommended model: the only one of the five that met all four targets (with one wrong out-of-scope item in the 32-minute run), at 4.6 GB of memory.



Top comments (0)