Thanks for your interest in Ghostwork — the first agent you don't prompt. It watches how you work, learns recurring workflows, and earns the right to run them autonomously.
We're early. The core loop is working. Good first issues are the fastest way to help.
- Code of conduct
- What we're building
- Development setup
- Project layout
- Running the app
- How to contribute
- Coding guidelines
- Architecture primer
- Good first issues
- License
Be respectful, constructive, and honest. Ghostwork handles screen activity — treat privacy and security issues with urgency. Do not commit API keys, .env files, or user data.
Ghostwork has a single continuous loop:
| Phase | What happens |
|---|---|
| Observe | Screenpipe captures every UI event and OCR frame on your Mac |
| Learn | Every 2 min: events → sessions. Every 30 min: sessions → rules (via LLM). Nightly: rules → compiled skills |
| Act | Every 10 s: current context → LLM checks if any rule applies → executes at earned tier |
Autonomy tiers — earned, never assumed:
- Supervised (default) — executes and shows a HUD notification; Cmd+Z undoes it
- Autonomous — runs silently after ≥5 accepted executions with fewer than 2 recent rejections
The north-star moment: "It noticed I do LinkedIn outreach every Tuesday and handled it while I got coffee."
- macOS 12+ (AX automation is macOS-specific today)
- Node.js 20+
- Screenpipe installed and running
- OpenRouter API key (openrouter.ai/keys) — required for rule extraction and trigger decisions
- Anthropic API key (optional) — used for the AX-first native computer use executor
git clone https://github.com/hvardhan878/ghostwork.git
cd ghostwork
npm install
npx @electron/rebuild -f -w better-sqlite3
cp .env.example .env # add your keysOn first run, grant when prompted:
- Screen Recording — required by Screenpipe
- Accessibility — required for AX tree interaction and keyboard automation
src/
main/ Electron main process (Node.js)
main.ts App lifecycle, tray, IPC handlers, cron jobs
actionEngine.ts 10 s context poll → LLM trigger decision → dispatch
sessionIngester.ts 2 min poll → raw_events + per-event prediction scoring
extractor.ts 30 min batch → 7-category structured rule extraction
consolidation.ts Nightly NREM (episodes→rules) + REM (rules→skills) + GC
computerUse.ts AX-first executor + Claude vision fallback
axDriver.ts macOS accessibility tree (AXUIElement via AppleScript)
skillEngine.ts Browser skill compile + deterministic replay + rollback
approvals.ts Shadow-mode approval queue (staged actions)
context.ts AppleScript + Screenpipe OCR → current UserContext
screenpipeDb.ts Direct SQLite queries to Screenpipe DB
db.ts GhostWork SQLite: rules, episodes, skills, approvals
renderer/
index.html Full UI — Activity, Timeline, Behaviour, Settings tabs
Compiled output goes to dist/. Always edit src/, never dist/.
npm start # TypeScript build + launch Electron
npm run build # TypeScript compile only (no launch)
npm run dist # Package as .dmgLogs appear in the terminal. Key prefixes:
| Prefix | Module |
|---|---|
[engine] |
Action engine — context polls, trigger decisions |
[ingester] |
Session ingester — raw event collection |
[extractor] |
Rule extraction from sessions |
[consolidation] |
Nightly NREM/REM/GC cycle |
[computer-use] |
AX-first executor + vision fallback |
[skill] |
Skill replay and rollback |
[approvals] |
Staged action approval queue |
Database location:
~/Library/Application Support/ghostwork/ghostwork.db
# or ~/Library/Application Support/Electron/ghostwork.db during dev
Browse issues labeled good first issue — these are scoped to single files with clear acceptance criteria.
Comment "I'd like to work on this" before starting so we avoid duplicate work.
git checkout -b fix/short-descriptionUse prefixes: fix/, feat/, docs/, refactor/.
- Keep diffs focused — one issue per PR
- Match existing TypeScript style (
strictmode, no unnecessary abstractions) - Run
npm run buildbefore opening a PR — it must compile clean - If you touch execution, IPC, or UI: manual test on macOS
Fill out the PR description with:
- What changed and why
- How you tested (steps to reproduce + verify)
- Screenshots for any UI changes
- Link to the issue:
Fixes #123
Maintainers aim to review within 48 hours. Once approved, we'll merge.
- Strict mode is on — no
anyunless truly unavoidable - Main process code in
src/main/; renderer is vanilla HTML/JS (no React, no build framework) - New IPC handlers: add to
main.ts, expose viapreload.ts, consume inindex.html
- Schema changes go in
db.tsinitDb()usingCREATE TABLE IF NOT EXISTS - Additive column migrations use the existing
safeAddColumn(db, table, col, type)helper - Never log or commit user activity data
- Prefer AX tree interactions (
axDriver.ts) over pixel clicks for native apps - Mark externally visible steps (send email, post, submit form) with
external: true - Shadow mode must gate outbound actions unless
externalAllowedis explicitly set
- Match existing CSS variables in
index.html(--surface,--border,--text-muted, etc.) - Keep the menu-bar-first UX — avoid adding dashboard complexity without discussion
Clear, imperative subject lines:
fix(engine): skip rule check when frontmost app is excluded
feat(ui): add search filter to Behaviour tab rule cards
docs: update CONTRIBUTING for current architecture
┌──────────────────────────────────────────────────────────────┐
│ Screenpipe — frames · ui_events · audio · clipboard │
└───────────────────────────┬──────────────────────────────────┘
│ every 2 min
┌───────────────────────────▼──────────────────────────────────┐
│ Session Ingester — groups events into sessions │
│ Prediction pass: sliding 5-event window → prediction_error │
└───────────────────────────┬──────────────────────────────────┘
│ every 30 min
┌───────────────────────────▼──────────────────────────────────┐
│ Extractor — 7-category structured rule extraction │
│ High-delta events (prediction_error ≥ 0.7) prioritised │
└───────────────────────────┬──────────────────────────────────┘
│ nightly
┌───────────────────────────▼──────────────────────────────────┐
│ Consolidation — NREM: stitch sessions → episodes │
│ REM: rules → compiled skills │
│ GC: demote poor skills, prune old events │
└───────────────────────────┬──────────────────────────────────┘
│ every 10 s
┌───────────────────────────▼──────────────────────────────────┐
│ Action Engine — context → LLM trigger → supervised/auto │
└───────────────────────────┬──────────────────────────────────┘
│
┌───────────────────────────▼──────────────────────────────────┐
│ AX-First Executor │
│ ax_list_elements + ax_click_element for native apps │
│ Claude vision fallback for browsers / AX-empty apps │
└──────────────────────────────────────────────────────────────┘
See architecture.md for the full breakdown.
These are scoped to a single file with clear acceptance criteria and no backend work required:
- Show Screenpipe connection status in Settings tab
- Copy button on rule condition and action text
- Show last extraction time in Behaviour tab header
Browse all: issues labeled good first issue
Comment on an issue before you start work.
By contributing, you agree that your contributions will be licensed under the GNU General Public License v3.0.
Ghostwork is GPL — derivative works must remain open source under the same license.
Join the Discord community or comment on an issue. For security concerns, do not open public issues with exploit details — contact the maintainer directly.