Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lockin

A focus monitoring app for macOS — built for someone I love who wanted a way to stay on task, prioritize what matters during work sessions, and be held accountable.

Describe what you're working on, let AI break it into tasks, and Lockin tracks your app and browser activity in real time — flagging drift and showing how focused you actually were.


Features

  • AI task extraction — describe your work in plain English; Claude breaks it into prioritised tasks
  • Lazy activity classification — apps and websites are automatically classified as on/off-task based on your session description, no preset lists needed
  • Real-time drift alerts — get notified when you've been off-task too long
  • Session timeline — a live activity feed and focus percentage while you work
  • Post-session review — check off tasks and save as a reusable template
  • Privacy-first — all activity is stored locally in SQLite; only the minimum context needed for classification is sent to the Anthropic API (see Data & privacy)

Screenshots

1. Describe your goal — tell Lockin what you're working on in plain English.

Describe your goal

2. Review AI-suggested tasks — edit, reorder, and set priorities before locking in.

Review tasks

3. Focused session — live timer, on/off-task breakdown, and per-activity classification you can override.

Focused session

4. Drift notifications — when you've been off-task too long, Lockin nudges you with a native macOS notification.

Drift notification


Requirements

  • macOS 12+ (Monterey or later)
  • Node.js 18+ and npm 9+
  • Xcode Command Line Tools — required to build the native accessibility module (xcode-select --install)
  • An Anthropic API key (for AI features — get one here)

Setup

1. Clone and install

git clone https://github.com/jeanmw/lockin.git
cd lockin
npm install

postinstall runs electron-rebuild and then builds the native accessibility module at native/lockin-ax (macOS only; no-op elsewhere). If the native build fails (e.g. Xcode Command Line Tools aren't installed), npm install still succeeds and the app will run with browser URL tracking disabled. You can rebuild it later with:

cd native/lockin-ax && npm install && cd ../..

2. (Optional) Regenerate the app icon

resources/icon.png is committed, so you only need this if you want to regenerate it:

node scripts/generate-icon.js

3. Run in development

npm run dev

On first launch you'll be prompted to grant Accessibility permission (required to read active window titles and browser URLs).

4. Add your Anthropic API key

  1. Click Settings in the left sidebar
  2. Scroll to the AI section
  3. Paste your API key (sk-ant-...) and press Enter

The key is stored locally in the app's SQLite database and only sent to the Anthropic API when making classification/suggestion requests.


macOS permissions

Lockin needs two permissions on macOS. Both are prompted by the OS the first time the app tries to use them.

Accessibility

Required to read which app is in the foreground and to read browser URLs via the Accessibility API.

  1. System Settings → Privacy & Security → Accessibility
  2. Enable Lockin (or your terminal / Electron app during dev)

Automation

Required for browser URL fallback via AppleScript (when Accessibility doesn't return a URL).

  1. System Settings → Privacy & Security → Automation
  2. Enable Lockin (or your terminal / Electron app during dev) for the browsers you use (Chrome, Safari, Arc, etc.)

Troubleshooting

  • "Grant Access" prompt won't go away. Toggle the permission off and on in System Settings, then restart Lockin. During development, macOS ties Accessibility permission to the specific binary — rebuilding with a new signing identity can reset the grant. Try removing the old entry and re-adding the app.
  • Browser URL shows empty. Automation permission is missing, or the native module didn't build. Check the main-process console for [ax-reader] Native module not available. — if you see it, rerun the native build step from Setup.
  • Permissions modal re-appears on every launch. The app bundle lost its signing identity (common with unsigned local builds after a clean). Re-grant the permissions once and they'll persist until the next unsigned rebuild.
  • Nothing is tracked at all. The ActivityMonitor uses osascript to query the frontmost app, which requires Automation permission for each target app (not just a single global grant). Open Settings → Permissions to see which grants are missing.

How AI classification works

Lockin tries to be frugal with API calls. Classification is lazy — it only happens when needed, and the result is cached as a rule so the same app/site is never re-classified.

  1. During a session, every activity tick records the foreground app and (for browsers) the URL.
  2. The activity is checked against stored rules (app_rules table) — project-specific rules first, then global rules.
  3. If no rule matches, the domain or app bundle ID is enqueued for classification.
  4. A background worker drains the queue one job at a time, calling Claude Haiku with:
    • Your session description
    • Your session's task descriptions
    • The domain (e.g. github.com) or the app name + bundle ID
  5. The classification result (on_task / off_task / neutral) is stored as an inferred rule. neutral results are not stored — the app stays neutral until you classify it manually.
  6. Rules you create or edit manually are source: 'user' and take precedence over inferred ones.

Window titles are only sent to the API in a separate flow (task attribution — matching current activity to one of your session tasks), and only when task attribution is enabled.

Domains are reduced to their effective root before being stored as rules (app.hubspot.comhubspot.com), except for a small allowlist where subdomains are meaningful (*.amazonaws.com, *.github.io, etc.).

See src/main/services/AIService.ts for the full implementation.


Running tests

npm test           # run once
npm run test:watch # watch mode
npm run typecheck  # TS type-check only

Tests use Vitest. Currently covered: AlertEngine, Classifier, LearningService. Areas without tests (SessionManager, ActivityMonitor, MetricsService, TaskService, AIService) are good first contribution targets — see CONTRIBUTING.md.


Project structure

src/
  main/          Electron main process
    services/    ActivityMonitor, SessionManager, AIService, AlertEngine …
    db/          Drizzle ORM schema + migrations (SQLite)
    ipc/         IPC handler registry
    native/      Loader for the native accessibility module
  preload/       Typed contextBridge bridge
  renderer/      React + Tailwind UI
    screens/     SessionSetup, LiveFocus, Dashboard, Settings
    components/  Sidebar, FocusTimer, AlertOverlay, Logo …
    hooks/       useSession, useTasks, useAlerts …
  shared/        Zod IPC types + constants
native/
  lockin-ax/     Native macOS accessibility module (Objective-C++ / N-API)
scripts/         Build helpers (icon generator, native build, notarization)

Building for production

npm run build      # compile main + renderer
npm run package    # package as .dmg and .zip for arm64
npm run publish    # build + upload to GitHub Releases (requires GH_TOKEN)

Signing and notarization are configured in package.json under build and scripts/notarize.js. See .env.example for the required APPLE_* environment variables.


Data & privacy

Stored locally:

  • All session, activity, rule, and task data lives in a SQLite database at ~/Library/Application Support/Lockin/lockin.db.
  • Your Anthropic API key is stored in the same database as plaintext inside the user_settings table. This is a known limitation; a migration to Electron's safeStorage (macOS Keychain-backed) is planned. If this matters to you, avoid using a key tied to a high-limit billing account.
  • You can wipe everything from Settings → Data → Clear all data.
  • If you used an older build that stored the DB at ~/.config/lockin/lockin.db, the app will automatically copy it to the new location on first launch (the legacy copy is left in place — delete it manually once you've confirmed the migration worked).

Sent to the Anthropic API (only during active sessions, only when AI features are enabled):

When What is sent
Task extraction (session start) Your session description
Rule suggestions (project setup) Your project description
Lazy classification (new app or domain) Session description, session task descriptions, and the domain or app name + bundle ID
Task attribution (optional) Current app name, bundle ID, domain, window title, and your task list

See Anthropic's privacy policy for how they handle API requests.

Never sent:

  • Keystrokes, screen contents, file contents, clipboard contents.
  • Activity data outside of an active session.
  • Anything when AI classification is disabled in Settings.

License

MIT

About

A focus monitoring app for macOS — built for someone I love who wanted a way to stay on task, prioritize what matters during work sessions, and be held accountable.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages