# Productivity Daemon

> Productivity Daemon

- Skill: `lucadominguez/productivity-daemon` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lucadominguez/productivity-daemon`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lucadominguez/productivity-daemon/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: lucadominguez (https://skillmd.com/u/lucadominguez)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lucadominguez/productivity-daemon

---

# Productivity Daemon

Electron + React + TypeScript + sql.js + Anthropic SDK app — an "attention antivirus" that monitors browsing, classifies distractions via AI, and blocks at multiple layers.

**Code location:** `C:\Users\Lenovo\Desktop\AI\daemon` (Windows path), `/mnt/c/Users/Lenovo/Desktop/AI/daemon` (WSL path)

## Support Files

- `references/debug-api.md` — Full debug API endpoint reference
- `scripts/probe-template.mjs` — Reusable Node.js probe script for API testing (auto-discovers port)

## Quick Start

```bash
# From Windows terminal (as Administrator for full blocking):
cd C:\Users\Lenovo\Desktop\AI\daemon
npm run dev
```

The app exposes a **debug HTTP API on `http://127.0.0.1:9119`** (falls back to 9120-9123 if port is held by a stale process — the hot-reload port fallback is normal in dev mode).

## Debug API Testing — USE NODE.JS, NOT POWERSHELL

**Critical pitfall:** PowerShell `Invoke-RestMethod` / `Invoke-WebRequest` corrupts large JSON responses when piped through WSL subprocess — `UnicodeDecodeError: invalid start byte` on non-UTF8 bytes in the Electron process output.

**Always use Node.js `fetch()` scripts** for API testing. Write `.mjs` files in `scripts/` and run them via:
```bash
powershell.exe -NoProfile -Command "cd 'C:\Users\Lenovo\Desktop\AI\daemon'; node scripts/<name>.mjs 2>&1"
```

## sql.js Stale Data Pitfall

The app uses sql.js (SQLite compiled to WASM) loaded into memory on startup. **Disk changes to the DB file do not take effect until the app restarts** — the in-memory instance is isolated.

Even worse: `db.export()` + `fs.writeFileSync()` via sql.js sometimes fails to persist DELETEs silently. If stale inference data is masking a code fix:
1. Kill the app (`Stop-Process -Name electron -Force`)
2. **Delete the DB file entirely**: `C:\ProgramData\ProductivityDaemon\daemon.db`
3. Restart — the app recreates a fresh DB from state.json

The state.json at `C:\ProgramData\ProductivityDaemon\state.json` is the durable source of truth (blocklists, settings, sessions). The SQLite DB stores inferences, events, and agent messages with 90-day retention.

## Elevation Testing

The app has 5 blocking layers that only activate when `elevation === 'full'`:
- hosts file redirect (127.0.0.1)
- DNS-over-HTTPS server disable
- Windows Firewall outbound block rules
- Browser policy injection (Chrome/Edge)
- Process killer

**Script-based elevation (Start-Process -Verb RunAs) does not work** — UAC requires manual user interaction at the GUI prompt. To test with elevation=full:
1. Right-click Windows Terminal → "Run as Administrator"
2. `cd C:\Users\Lenovo\Desktop\AI\daemon && npm run dev`
3. Verify: `GET /state` → `elevation: "full"`

Without elevation, the app functions as an interstitial blocker only (warning windows in the UI). The full blocking stack is untested until a manual Admin restart is performed.

## Key Debug Endpoints

See `references/debug-api.md` for the complete reference. Quick checks:

| Endpoint | Purpose |
|----------|---------|
| `GET /ping` | Health check — PID, uptime, port |
| `GET /state` | Full store: elevation, blocklist, sessions, inferences, settings |
| `GET /inferences` | AI classifications (?status=pending\|auto_applied\|confirmed\|rejected) |
| `GET /blocklist` | Active domain/process blocks |
| `GET /logs?n=N` | Structured debug log ring buffer |
| `POST /inject/url` | Run URL through full inference pipeline |
| `POST /inject/chat` | Send message to Claude agent (requires API key) |
| `POST /inject/scan` | Run FocusScan for installed distractor detection |
| `POST /inject/block` | Add domain to blocklist (now persists to store) |
| `POST /inject/sweep` | Trigger background inference sweep |

## Confidence Threshold Architecture

Located in `src/main/inference/InferenceEngine.ts`:
- `CONFIDENCE_AUTO_BLOCK = 0.85` — min for auto-apply in auto mode
- `CONFIDENCE_SUGGEST = 0.60` — min for suggestion in ask mode
- `CATEGORY_AUTO_BLOCK_SCORE` — per-category base scores (adult 0.97, social_media 0.88, video_streaming 0.85, etc.)
- `HARDBLOCK_CATEGORIES` — categories that skip the "no goals" confidence discount and always use baseScore + 0.05

Current HARDBLOCK_CATEGORIES: `adult, gambling, dating, social_media, video_streaming`

## Common Debugging Workflow

1. `GET /ping` — confirm app is alive
2. `GET /state` — check elevation, blocklist state, blockingMode
3. `POST /inject/url` with known distraction → verify inference pipeline
4. Check `/inferences` and `/logs` for results
5. If confidence values look wrong: check for stale SQLite data (see pitfall above)
6. If endpoints return 404: check app PID — old process may still hold the port. Kill all Electron processes and restart.

## Known Quirks

- **Port reuse in dev mode**: Hot reload restarts the main process before the OS releases the port. DebugServer tries 9119→9120→9121→9122→9123. Always check `/ping` for the actual port, or read `C:\ProgramData\ProductivityDaemon\debug-port`.
- **Ring buffer starts empty**: `/logs?n=5` returns 0 entries on fresh start — normal. The buffer fills as events fire.
- **friendster.com appears as inference**: Stale data from pre-fix sessions. Delete the DB to clear.
- **Blocklist via debug API vs GUI**: The `/inject/block` endpoint now persists correctly (was a previous P0 bug). Both paths use the same store.

