Don't start a timer. Just study.
StudyOS is an automatic study-time tracker for students. Instead of requiring you to manually start and stop timers, it reads computer activity from your existing ActivityWatch installation, figures out which activity is actually studying, classifies it into subjects, merges it into meaningful study sessions, and turns the result into useful learning analytics.
ActivityWatch StudyOS
(sensor) → (brain) → UI
raw activity classification dashboard / timeline
StudyOS is local-first and private: everything runs on your machine, the raw data stays in a local SQLite database, nothing ever leaves your computer.
Status: Phases 1–5 mostly done. The full pipeline works end-to-end with tests: ActivityWatch → classification → sessions → SQLite → local web UI (dashboard, timeline, manual timer, subjects, rules, analytics, settings, weekly review). The UI is a PWA installable on iPhone/desktop, and a Tauri desktop shell is included for Windows. Statistics auto-sync in the background and you get a notification when a goal is reached. An optional AI learning mode (Phase 5) lets Google Gemini propose new classification rules for activity that is still unclassified.
.\run.batThat starts the backend and the web UI, then opens
http://localhost:4173 in your browser. On Windows you can also run the
Tauri desktop app instead (see below). To auto-start StudyOS every time you
log in, place a shortcut to run.bat in your Startup folder
(shell:startup), or install with the desktop bundle which registers an
app you can launch anytime.
- Windows / macOS / Linux with Python 3.12+
- ActivityWatch running locally (default API at
http://127.0.0.1:5600) with at least the window and afk watchers
python -m venv .venv
.venv\Scripts\pip install -r backend/requirements.txtcd backend
..\.venv\Scripts\python.exe -m uvicorn studyos.api.main:app --port 8765Then visit http://127.0.0.1:8765 (OpenAPI docs at /docs).
Hot reload during development:
..\.venv\Scripts\python.exe -m uvicorn studyos.api.main:app --port 8765 --reloadcd web
npm install
npm run devThen visit http://localhost:5173. The Vite dev server proxies /api to the
backend at http://127.0.0.1:8765, so keep the backend running (see above).
Production build:
cd web
npm run build# one-time prerequisites: Rust (rustup) + Visual Studio Build Tools (VC++ workload)
# 1. build the standalone backend binary (bundles the API + a Python runtime):
.\.venv\Scripts\pyinstaller.exe --onefile --name studyos-backend --paths backend --distpath desktop\src-tauri\resources --workpath "$env:TEMP\studyos-pyi" --specpath "$env:TEMP\studyos-pyi" backend\launcher_desktop.py
# 2. build the installer (embeds the web UI + the backend binary):
cd desktop
npx @tauri-apps/cli@latest build # produces StudyOS_<version>_x64-setup.exe (+ .msi)
# 3. copy the installer to the release folder (outside the repo):
Copy-Item src-tauri\target\release\bundle\nsis\StudyOS_*_x64-setup.exe C:\Users\nguye\Documents\GitHub\studyos-release\The installed app is fully standalone: it embeds the web UI and starts the
backend itself, so it needs no Python, Node or the repository. Data lives
in %APPDATA%\com.studyos.desktop\studyos.db (existing dev data is
migrated on first run). A "StudyOS" shortcut in the Startup folder starts
it automatically at login.
.\.venv\Scripts\python.exe -m pytest backendWindows note: if your %TEMP% directory is locked (e.g. leftover
pytest-of-* folder with broken ACLs), pass an explicit temp dir:
.\.venv\Scripts\python.exe -m pytest backend --basetemp=backend/.pytest_tmpActivity data flow (Phase 1):
ActivityWatch REST API
│ GET /api/0/buckets/.../events
▼
ActivityWatchClient (studyos/aw) graceful if AW is stopped
▼
normalize (studyos/normalize) app / title / domain extraction,
overlap clipping, AFK subtraction
▼
Classification engine (studyos/classification)
│ deterministic rules only (no AI), user rules beat system rules
▼
Session builder (studyos/sessions) merge events per subject,
│ short gaps, distraction accounting, AFK excluded
▼
SQLite (studyos/models + studyos/db) subjects, rules, events, sessions
Backend layout:
| Module | Responsibility |
|---|---|
studyos/aw/client.py |
thin ActiveWatch REST client (never crashes when AW is down, reports aw_connected instead) |
studyos/normalize.py |
ActivityWatch event → normalized event; domain extraction; overlap clipping; AFK interval math |
studyos/classification/ |
deterministic rule engine + built-in (system) rules |
studyos/sessions/builder.py |
merges classified events into study sessions |
studyos/services/ingest.py |
the sync pipeline: fetch → normalize → classify → rebuild sessions |
studyos/api/ |
FastAPI app with health / sync / events / sessions / subjects / rules / unknown-classification endpoints |
| Method | Path | Purpose |
|---|---|---|
| GET | /api/health |
server + ActivityWatch connection state |
| POST | /api/sync |
run the ActivityWatch → sessions pipeline |
| GET | /api/sessions |
study sessions (filterable by time / subject) |
| GET | /api/sessions/{id} |
session detail with its activity breakdown |
| GET | /api/events |
raw normalized events |
| GET | /api/unknown |
unknown activity, grouped for review |
| POST | /api/events/{id}/classify |
manually classify + optional "remember" rule |
| CRUD | /api/subjects, /api/rules |
manage subjects and classification rules |
| GET | /api/ai/status |
AI learning state (enabled, model, key hint, last run) |
| POST | /api/ai/classify |
run the AI learning pass on demand |
Every normalized event is classified as exactly one of:
study— belongs to a subject (builds sessions),non-study— e.g. entertainment (never enters a session),distraction— counted inside a session but not as study time,ignore— invisible to StudyOS,unknown— awaiting a manual decision (see/api/unknown).
Rules are (app / title / domain) → outcome conditions with an explicit
priority. Lower priority number wins. Built-in system rules start at
priority 10000, so any user rule overrides the defaults.
"Remember this classification" teaches StudyOS: it stores a user rule from your manual choice and the next sync applies it to matching historical events as well.
AI learning (optional, Phase 5). When enabled with a Gemini API key,
StudyOS collects window activity that still matches no rule, asks the model
which subject it belongs to (or whether it is a distraction etc.), and stores
new rules automatically — named ai: … — once a suggestion clears the
confidence threshold. Runs in the background at most once an hour plus on
demand from Settings, and only after a successful sync.
- StudyOS never collects keystrokes, passwords, clipboard, screenshots, microphone, or camera data.
- All data is stored locally (SQLite file in
backend/data/). - ActivityWatch itself is the only activity sensor, also fully local.
- Blacklist controls for apps/domains, plus raw-activity retention limits (both configurable in the Settings page).
- AI learning is off by default: nothing is sent anywhere until you paste your own Gemini API key. Then only the app/title strings of still unclassified windows leave your machine (to Google's API, only as long as needed), and yours — not StudyOS's — is the account behind that call. The key is kept in your local database, write-only: it is never shown in the UI, never returned by the API, and never pinged to any other service.
- Phase 1 — done: ActivityWatch integration, classification, session builder, SQLite, API, tests.
- Phase 2 — done: web UI (dashboard, timeline, manual timer, session editing, subject and rule management).
- Phase 3 — done: analytics (daily/weekly/monthly/yearly stats, goals, weekly review, streaks, study heatmap, focus & distraction metrics).
- Phase 4 — done: PWA install, privacy controls (per-app/domain blacklists, raw-activity retention), background auto-sync with goal notifications, Tauri desktop shell for Windows.
- Phase 5 — done: optional AI learning (Gemini) that proposes new classification rules from still-unclassified activity, with a confidence threshold, on-demand runs, and write-only key storage.
Out of scope: calendars, cloud sync, accounts, social features, TODOs, notes, flashcards.
