This is a submission for the Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady
What I Built
Quack is a rubber duck that lives in your terminal.
You tell it what's broken. It finds the handful of lines in your repo that matter, asks you one question about your own code, explains the likely cause with a file:line, and writes a TASKS.md for you to work through. Then it stops.
It cannot edit your source files. That isn't a promise in the README, it's a check in the code: every write goes through one function that allows TASKS.md, QUACK_HISTORY.md and .quack/, and nothing else. Afterwards, quack verify proves nothing changed.
quack ask "why does session_valid reject valid sessions?"
pytest -x 2>&1 | quack ask "why does this fail?" --stdin # files in the traceback are read first
quack verify # PASS: no source files were modified.
Most coding assistants optimise for the patch. I wanted the opposite for one specific moment: when I want to understand the bug, not outsource it. The part of debugging I'm happy to skip is the hunt (grep, open five files, forget which one had the clue). Quack does the hunt and leaves the thinking to me.
It fits the theme literally. A sloth conserves energy; Quack conserves tokens. Indexing costs zero, only small line windows are ever sent, and a repeated question is answered from a local cache for free.
Demo
- Landing page: [https://quack.wasmer.app]
The part of Quack that costs nothing, run for real on the source of pydantic 2.14.1 (107 files), with no model involved:
$ quack index
files: 107 symbols: 2518 mentions: 33979 todos: 63 time: 569 ms tokens: 0
$ quack index --changed # nothing changed since
files: 107 symbols: 2518 mentions: 33979 todos: 63 time: 7 ms tokens: 0
$ quack ask "how does model validation handle extra fields" --dry-run
Dry run. The duck would read:
pydantic/config.py (score 12.8768)
pydantic/fields.py (score 12.4624)
pydantic/root_model.py (score 12.2613)
context: 9893 chars (~2473 tokens), depth 2, model ollama:gemma3:4b
would write: TASKS.md, QUACK_HISTORY.md, .quack/ (index, cache, history)
source files: never.
$ quack verify
PASS: no source files were modified.
And the model half, on a repo I know: [[FILL: paste your real quack ask output here, with the footer line showing tokens and model]]
Try it in three minutes:
git clone [https://gitlab.com/quack-tutor/quack-hopper] && cd quack-tutor
pip install -e .
ollama pull gemma3:4b
quack -C tests/fixtures/mini_repo ask "why does session_valid reject valid sessions?"
Code
[https://gitlab.com/quack-tutor/quack-hopper]
MIT licensed. quack/ is the CLI, web/ is the Astro landing page, and tests/ holds 69 tests that run with no network and no model.
How I Built It
The open pieces. The default model is Gemma 3 (4B) running locally through Ollama, using Ollama's structured output with the Pydantic schema so the answer arrives as validated JSON. Everything else is the standard library: SQLite, regex, and a hand-written BM25.
One step in six uses the model.
- Index: walk the repo, extract symbols, term counts and TODOs into SQLite. 0 tokens.
- Retrieve: BM25, plus a bonus for matching symbol and file names. Traceback paths get pinned.
-
Window: only line windows around matching symbols, capped at 30 / 80 / 150 lines per file by
--depth. Never a whole file. -
Redact: API keys, GitHub and Slack tokens, AWS keys and private keys are masked before indexing and before anything reaches a model.
.envfiles and key files are never indexed at all. - Ask: one model call returns a question, a cause, tasks, concepts and a confidence level.
- Ground: tasks that point at files that don't exist are dropped and impossible line numbers are clamped, so a small model can't send you chasing an invented path.
Context is trimmed to fit a 5,000 token budget, and Quack warns if a run goes over. The targets below come from my build spec. The last column is measured.
| Depth | Lines per file | Spec target | Measured prompt |
|---|---|---|---|
| 1 | 30 | ~1,000 to 1,500 | ~1,200 |
| 2 (default) | 80 | ~2,000 to 3,000 | ~2,800 |
| 3 | 150 | ~4,000 to 5,000 | ~4,300 (trimmed to fit) |
"Measured" is the system prompt plus retrieved context for the pydantic question above, estimated at 4 characters per token, before the model's answer. At depth 3 the raw context was about 5,100 tokens, so Quack trimmed it to fit the budget. Real usage varies by model, so quack ask --json prints what your provider reports.
Beyond one model. Remote providers (OpenRouter, Anthropic, Groq, Gemini and a dozen others, plus any OpenAI-compatible endpoint) are supported but blocked until you opt in. quack compare "..." -w ollama -w openrouter:google/gemma-3-27b-it asks the same question of several models and lays the answers side by side.
How I worked. I wrote a build spec first (it's in the repo as BUILD_SPEC.json) and built the project with an AI coding assistant against it, then tested and reviewed the result. The tests use a fake model and a local HTTP server, so the whole suite runs offline in about three seconds.
What it doesn't do well. Symbols come from regex, not a parser, so unusual syntax can be missed. There are no embeddings; retrieval is keyword-based. A 4B model will sometimes be wrong, which is why the answer carries an honest confidence level, the grounding step exists, and the prompt tells it to say "show me another file" instead of guessing.
Why Does Open Innovation Matter?
Quack's whole job is reading code you haven't shipped yet: pre-launch features, customer fixtures, internal TODOs. That's exactly what people hesitate to paste into a hosted assistant.
With open weights on localhost, "nothing leaves your machine" is a property I can test, not a policy I have to trust. A test asserts that Quack refuses to contact any non-local host until you opt in, and quack verify --audit lists every place the program can write to disk. I could never write those tests against a closed API.
-
Private and free. After
ollama pull gemma3:4bit runs on a laptop with no internet and no per-token bill. - Swappable. Changing the model is a config line, so I can compare a local 4B against a larger open model on the same bug and see where each one goes wrong.
- Adjustable. The prompts are plain strings in one file. Changing the duck's voice is an edit, not a feature request.
The trade-off is honest: a local 4B model is less sharp than a frontier one. Open innovation is what lets me choose that trade per project (local by default, a bigger open model when I decide to), instead of having it chosen for me.
The Lazy Payoff
The payoff is the hunt that no longer happens. For a bug in code I don't know well, the usual loop is: search, open files, read, guess, search again. Quack collapses that into one command and a short checklist, and the question it asks first often points at the right place before I've read the explanation.
What I can measure is the cost side. On a codebase I didn't write (pydantic 2.14.1, 107 files), Quack indexed everything in about half a second for zero tokens, and re-indexing after a change took 7 ms. Choosing the files worth reading and cutting them down to line windows is plain Python over a local SQLite file, so the only tokens spent are the ~2,800 in the final prompt at the default depth. What it saves you in debugging time is something you'll feel on your own repo, so I won't invent a stopwatch number for it.
Prize Categories
- Best Use of Gemma: Gemma 3 is the default model, run locally through Ollama, with Gemma 3 27B available through OpenRouter for comparison.

Top comments (0)