Toadie
Z Claude Code
Czyli jak stworzyłem swojego personalnego
multi-device bota, bez czytania i pisania kodu
Michal Franc
O mnie
- Staff/Principal Engineer
- 15+ lat w branży, 12+ lat dla rynku UK/US, firmy produktowe
- Obecnie Affirm (BNPL fintech), tech lead tech leadów, 30+ inżynierów
- Impact oriented, wysoki lvl individual contributor
- AWS, Kubernetes, Terraform, Go, Python, C#, Kotlin
- Reliability, observability, cost optimization
- M.Sc. Politechnika Wrocławska
- Kiedyś współzałożyciel dotnetconf.pl i grupa .NET Wrocław na PWr
- mfranc.com · @francmichal
Polyglot programmer, ale Toadie to mój pierwszy projekt gdzie nie napisałem ani linijki kodu
Psycho fan vima i i3wm · hobbystycznie pisze grę · maluje figurki Warhammera
Co zbudowałem
- Aplikacja na Galaxy Watch, Kotlin, Wear OS
- Aplikacja na telefon Android, z botem Tamagotchi
- Serwer Python, HTTP, WebSocket, STT, TTS
- Dashboard webowy, Vue.js, timeline w real-time
- System uprawnień, hooks, polling, zatwierdzanie z zegarka
- Integracja z Tailscale, weryfikacja peerów, telefon jako relay
Pokażę jak to wszystko działa...
...i dlaczego Kotlin w ogóle pojawił się na grupie .NET
SPOILER: bo to możliwe że już nie ma znaczenia
SPOILER2: pogadamy też o przyszłości naszej branży trochę
Demo, edycja notatek przez Toadie
Demo na żywo
"Hey Toadie, dodaj notatkę że prezentacja idzie naprawdę dobrze"
[ screen share: phone + obsidian ]
Problem
Chciałem Superwhisper ale na Linuxie
- Wciśnij klawisz → zacznij dyktować
- Tekst pojawia się w aktywnym oknie (streaming)
- Działa wszędzie, terminal, przeglądarka, Obsidian
- Bonus: przekieruj tekst do Claude jako komendy
Skrypt dyktowania
Emulacja doświadczenia dyktowania jak na Macu. Python orkiestrujący przechwytywanie audio, wywołanie API i xdotool.
- Przechwycenie audio z mikrofonu
- Wysyłka do Deepgram (STT streaming)
- Tekst trafia do aktywnego okna przez
xdotool
Testowałem też: Whisper (lokalny), Google Cloud STT
Dlaczego Deepgram?
Zabezpieczenia przed kosztami
- Auto-stop po ciszy, skrypt kończy po 10s bez mowy
- Crontab, co 5 minut zabija procesy dyktowania
- Polybar, pokazuje czy dyktowanie jest włączone
Dyktowanie do instancji Claude Code
Dyktowanie do aktywnego okna jest fajne, ale czasem chcę po prostu pogadać z Claude. Wciśnij przycisk, powiedz coś, tekst ląduje w Claude Code.
- Nowy skrypt, spawnuje instancję Claude Code w
i3wm
- Transkrypcja pipe'owana bezpośrednio do procesu
- Działa z mikrofonu słuchawek, z dala od biurka
- Szybka myśl lub komenda głosem
- Nie trzeba siadać do komputera
- Wciśnij przycisk → mów → Claude dostaje
Problem: za każdym razem nowy proces, nowy kontekst...
Dyktowanie do persistent sesji Claude
Poprzedni skrypt za każdym razem startował nowy proces Claude, nowy kontekst. A ja chcę mówić do istniejącej sesji.
- Claude CLI działa w
tmux z nazwaną sesją
- Transkrypcja buforowana i wysyłana przez
tmux send-keys -t session:pane
- Kontekst zostaje między komendami
- Długie sesje z pełnym kontekstem projektu
- Mów z mikrofonu słuchawek, z dala od biurka
- Głos trafia do tego samego procesu co klawiatura
Galaxy Watch
Pewnej nocy mózg zdecydował: zegarek ma mikrofon... czemu nie gadać z Claude z nadgarstka?
- Galaxy Watch, klient Kotlin Wear OS, nagrywa audio, wyświetla odpowiedzi, odtwarza głos
- Serwer Python, lokalnie na maszynie, odbiera audio, wysyła do Deepgram, rozmawia z Claude
- Deepgram, STT + TTS, audio in → tekst out, tekst in → audio out
- Claude Code CLI, wrapper obsługuje input/output i lifecycle procesu, JSON streaming zamiast scrapowania tmux pane
- Sesja tmux, wrapper loguje wszystko do sesji
claude-watch, daje widoczność w to jak Claude pracuje
Demo, Galaxy Watch
Głos z zegarka → odpowiedź z Claude
Server Dashboard
Z zegarkiem, serwerem, WebSocketami i permission hooks setup robi się skomplikowany. Potrzebowałem widoczności.
- Timeline z wszystkimi akcjami serwera i timestampami
- Settings, zmiana języka, modelu
- Chat, wizualizacja konwersacji w real-time
- Vue.js, single-page, WebSocket live updates
- Pomaga debugować problemy i rozumieć flow
- Dostępny przez Cloudflare Tunnel
Demo, Server Dashboard
Demo na żywo
1. Timeline, settings, chat w akcji
2. Wysyłamy komendę i obserwujemy flow
[ screen share: browser ]
Aplikacja na telefon
Mam zegarek... czemu nie telefon? Więcej ekranu, a może nawet trochę osobowości?
Tak narodził się Toadie.
- Tamagotchi, zielony troll z animowanymi stanami (idle, słuchanie, myślenie, mówienie, spanie)
- Kolejna apka Kotlin, chat, wiadomości głosowe i tekstowe
- Z punktu widzenia serwera, klient jest abstrakcją
- Telefon jako relay dla zegarka, Watch → Phone (DataLayer) → Server
- Wizualizacja mockup stworzona w Remotion + Claude
- Ten sam protokół WebSocket co zegarek
Demo, Aplikacja na telefon
Wiadomość tekstowa + głosowa → odpowiedź TTS
Architecture, pełny obraz
Telefon i dashboard to po prostu kolejni klienci. Dashboard też łączy się przez WebSocket.
Tailscale
Wszystkie urządzenia udają że są w jednej prywatnej sieci, nawet jeśli są w różnych miejscach.
- WireGuard, szyfrowany tunel
- Tailscale orkiestruje: tożsamość, zarządzanie kluczami, discovery
- Każde urządzenie dostaje IP
100.x.x.x
- It JUST works
- UDP hole punching, urządzenia za NAT (normalnie niedostępne publicznie) rozmawiają ze sobą
- Jeśli hole punching nie działa, DERP relay server przejmuje
- Świetny UX, zero konfiguracji
Prywatny dostęp, Tailscale
Tailscale mesh, each device gets a 100.x.x.x address
Problem: zegarek nie ma Tailscale
Galaxy Watch nie wspiera Tailscale. Rozwiązanie: telefon jako relay.
- Watch → Phone → Server (przez Tailscale)
- Telefon działa jako transparentny proxy
- Android Wearable DataLayer do komunikacji watch↔phone
- Protokół sygnałowy zapewnia stabilne połączenie
- Zegarek nie musi być w publicznej sieci
- Dużo bardziej niezawodne połączenie niż bezpośrednie
Wake Word
"Hey Toadie"
- PicoVoice Porcupine, działa lokalnie na telefonie
- Bez połączenia z serwerem (poza aktywacją klucza)
- 1 custom wake word za darmo
- Automatycznie uruchamia nagrywanie
- W 2-3 tygodnie zbudowałem naprawdę funkcjonalny software z dobrym UX
- Używam tego codziennie
- Ciekawe i trochę przerażające co jedna osoba może teraz zbudować
Moment "WOW"
Demo, Wake Word
Demo na żywo
Powiedz "Hey Toadie", wake word uruchamia nagrywanie
[ screen share: phone ]
Demo, Rich Content
Demo na żywo
1. Wyślij screenshot na telefon przez toadie-show
2. Wyślij animację D3.js, interaktywna treść na telefonie
[ screen share: phone + terminal ]
(jak Michał zrobi rich content w webie bo na komórce jest)
Co potrafi Toadie?
- Czyta cele, listę zadań, kalendarz, maile
- Dostęp do wszystkich notatek 2nd brain (Obsidian)
- Steruje monitorami (wejścia, jasność)
- Włącza TV i uruchamia apki na telewizorze (mam też apkę TV z botem)
- Dyskutuje pomysły, pisze posty na bloga, tworzy plany
- Stał się mega użytecznym narzędziem do pracy z notatkami
- 95% gotowe żeby Toadie zarządzał innymi botami
Następny krok: Toadie jako manager, "hey, popracuj z zespołem na tym repo, sprawdź GitHub issues i po prostu pracuj nad nimi"
Bezpieczeństwo, Defense in Depth
Tech Stack
Python
aiohttp
WebSocket
Deepgram
tmux
Claude Code CLI
Kotlin
Wear OS
Android
PicoVoice
Tailscale
Vue.js
github.com/michal-franc/toadie-personal-assistant
Ile czasu zajęło napisanie tego całego kodu?
Nie napisałem ani linijki kodu
Nie przeczytałem ani linijki kodu
100% wygenerowane przez Claude Code
Co tak naprawdę robiłem
- Opisywałem czego chcę, naturalnym językiem
- Testowałem wynik, działa czy nie?
- Iterowałem, "to nie działa", "zmień to"
- Decydowałem o architekturze, nie o implementacji
Python, Kotlin, Vue.js, WebSockets, języki których ledwo dotykam
Rozwój oparty na specyfikacji
Zarządzanie projektem przez GitHub
Wszystkie zmiany, plany i prace trzymamy w GitHub. Pomysły, dyskusje i plan pracy.
- Issues jako backlog pomysłów i bugów
- Pull Requesty z pełnym opisem zmian i planem testów
- Dyskusje w komentarzach, iteracje na review
- Bot tworzy PRy, ja reviewuję i mergeuję
- Pełna historia decyzji i zmian
- CI/CD sprawdza linty i testy automatycznie
- Każda funkcjonalność = branch + PR
- Transparentność, widać co bot zrobił i dlaczego
Przykłady: PR #74, beta plan testów · PR #49, duża zmiana
Wiele botów, wiele issues naraz
Każdy bot pracuje nad swoim issue w izolacji. Klucz: git worktrees.
- Każdy bot dostaje własny worktree, osobna kopia repo
- Brak konfliktów, boty nie nadpisują sobie zmian
- Każdy bot tworzy branch → PR → review
- Mogę odpalić 3-4 boty równolegle na różnych taskach
- Claude Code ma już wbudowane wsparcie dla worktrees
- Ale polecam samemu zarządzać worktrees z botem
- Większa kontrola nad tym co bot robi i gdzie
git worktree add ./worktrees/feature -b feature
Worktree = tani klon repo bez kopiowania historii. Każdy bot ma swój sandbox.
Workflow: boty → beta → test
Zarządzanie kontekstem to klucz
CLAUDE.md, instrukcje które agent czyta przy każdej sesji
- Decyzje architektoniczne, ścieżki plików, komendy
- Konwencje testów, czego NIE robić
- Info o urządzeniach, portach, zależnościach
- Auto-pamięć, agent uczy się między sesjami
context-doctor
github.com/michal-franc/context-doctor
Linter dla CLAUDE.md, uruchom, dostań ocenę
$ context-doctor CLAUDE.md
METRICS
Lines: 119 (MODERATE)
Instructions: ~43 (+50 Claude = ~93) (OK)
Progressive Disclosure: YES
Scope Activity: 11 commits since last update (10 days ago)
LENGTH ISSUES
⚠ [CD002] File has more than 100 lines
→ Consider being more concise. Ideal ~60 lines
REFERENCED DOCS
✓ KLAUDIUSH.md (9 days ago)
✓ docs/server-api.md (9 days ago)
✓ docs/features/security.md (23 days ago)
DIMENSION SCORES
Correctness ███████████████████░ 95/100
Style ████████████████████ 100/100
Freshness ██████████████████░░ 90/100
OVERALL SCORE
███████████████████░ 96/100
github.com/michal-franc/context-doctor
Prawdziwe prompty, budowanie funkcjonalności
> "ok now on hey toadie after the wake word is detected it should open the app even if its in lock mode doable?"
> "could we make this to be kind of like a voice sin function?"
> "i also have critical bug now - watch is never refreshing chat even after sending new voice command"
> "ok but we should retry more often instead of dropping to idle timeout it should be exponential retry"
Prawdziwe prompty, debugowanie
> "can you check the logs i can still see flickering"
> "the screen is turning on - but then when i swipe i am not being taken to the app"
> "i now have the app in listening state its blocked on the overlay"
> "can you check request nr 8 from server and tell me why we need 3.3s to capture response?"
Prawdziwe prompty, reszta
> "ok install on the watch"
> "try again"
> "git push"
> "we change the app name you silly fool :D"
> "dude you keep confused we streamlined the name for both watch and phone"
> "ej wez sprawdz logi z zegarka bo cos websockety szwankuja i mi nie laczy"
> "yeah it works!"
Era personal software?
- Apki budowane wyłącznie dla siebie
- Nie dopieszczone, nie produkcyjne, ale działają dla Ciebie
- Musi działać tylko na Twojej maszynie, dla Twojego workflow
- Jedna osoba może zbudować to co wcześniej wymagało zespołu
Programowanie jako aktywność społeczna? Narzędzia szyte na miarę dla społeczności?
Era CLI
Bez IDE. Bez edytora. Tylko i3wm + tmux + alacritty + polybar
Czasem nawet edytuję notatki przez... Claude. Albo po prostu mówię Toadie żeby to zrobił, z kanapy.
Diagramy, draw.io MCP
Wygenerowane przez Claude przez mcp-drawio, bez ręcznego rysowania
Diagram architektury
Architektura phone relay
Ta prezentacja
Reveal.js + D3.js. Zbudowana w tej sesji z Claude Code.
> "2 slajd o mnie przeczytaj mfranc.com i wrzuc o mnie jakies tam info tez mozesz moje cv sprawdzic"
> "no i tez jest troche bledny popatrz tutaj to jest realny obrazek [screenshot] popraw d3"
> "i ten obrazek zaambedujemy i omowimy https://mfranc.com/images/dictation-architecture-simple.png"
> "albo lepiej zamien ten obrazek ze zrodla w d3.js nowa symulacje pod tytuilem ehh?"
> "przesun resolve hostname o 10px do gory | cache miss 20px na prawo"
> "ok 10 more and we are good :d"
Witamy w nowej erze?
mfranc.com
Q/A