ghost-in-the-droid 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. ghost_in_the_droid-1.0.0/LICENSE +21 -0
  2. ghost_in_the_droid-1.0.0/PKG-INFO +404 -0
  3. ghost_in_the_droid-1.0.0/README.md +360 -0
  4. ghost_in_the_droid-1.0.0/ghost_in_the_droid.egg-info/PKG-INFO +404 -0
  5. ghost_in_the_droid-1.0.0/ghost_in_the_droid.egg-info/SOURCES.txt +83 -0
  6. ghost_in_the_droid-1.0.0/ghost_in_the_droid.egg-info/dependency_links.txt +1 -0
  7. ghost_in_the_droid-1.0.0/ghost_in_the_droid.egg-info/entry_points.txt +3 -0
  8. ghost_in_the_droid-1.0.0/ghost_in_the_droid.egg-info/requires.txt +28 -0
  9. ghost_in_the_droid-1.0.0/ghost_in_the_droid.egg-info/top_level.txt +1 -0
  10. ghost_in_the_droid-1.0.0/gitd/__init__.py +0 -0
  11. ghost_in_the_droid-1.0.0/gitd/alembic/alembic/env.py +89 -0
  12. ghost_in_the_droid-1.0.0/gitd/alembic/env.py +89 -0
  13. ghost_in_the_droid-1.0.0/gitd/app.py +126 -0
  14. ghost_in_the_droid-1.0.0/gitd/bots/__init__.py +0 -0
  15. ghost_in_the_droid-1.0.0/gitd/bots/common/__init__.py +0 -0
  16. ghost_in_the_droid-1.0.0/gitd/bots/common/adb.py +780 -0
  17. ghost_in_the_droid-1.0.0/gitd/bots/common/discover_rids.py +313 -0
  18. ghost_in_the_droid-1.0.0/gitd/bots/common/elements.py +290 -0
  19. ghost_in_the_droid-1.0.0/gitd/cli.py +578 -0
  20. ghost_in_the_droid-1.0.0/gitd/config.py +36 -0
  21. ghost_in_the_droid-1.0.0/gitd/mcp_server.py +560 -0
  22. ghost_in_the_droid-1.0.0/gitd/models/__init__.py +36 -0
  23. ghost_in_the_droid-1.0.0/gitd/models/base.py +50 -0
  24. ghost_in_the_droid-1.0.0/gitd/models/bot.py +42 -0
  25. ghost_in_the_droid-1.0.0/gitd/models/chat.py +36 -0
  26. ghost_in_the_droid-1.0.0/gitd/models/phone.py +34 -0
  27. ghost_in_the_droid-1.0.0/gitd/models/schedule.py +68 -0
  28. ghost_in_the_droid-1.0.0/gitd/models/skill_compat.py +50 -0
  29. ghost_in_the_droid-1.0.0/gitd/routers/__init__.py +0 -0
  30. ghost_in_the_droid-1.0.0/gitd/routers/agent_chat.py +186 -0
  31. ghost_in_the_droid-1.0.0/gitd/routers/bot.py +277 -0
  32. ghost_in_the_droid-1.0.0/gitd/routers/creator.py +356 -0
  33. ghost_in_the_droid-1.0.0/gitd/routers/emulators.py +311 -0
  34. ghost_in_the_droid-1.0.0/gitd/routers/explorer.py +210 -0
  35. ghost_in_the_droid-1.0.0/gitd/routers/misc.py +101 -0
  36. ghost_in_the_droid-1.0.0/gitd/routers/phone.py +501 -0
  37. ghost_in_the_droid-1.0.0/gitd/routers/scheduler.py +444 -0
  38. ghost_in_the_droid-1.0.0/gitd/routers/skills.py +502 -0
  39. ghost_in_the_droid-1.0.0/gitd/routers/stats.py +11 -0
  40. ghost_in_the_droid-1.0.0/gitd/routers/streaming.py +535 -0
  41. ghost_in_the_droid-1.0.0/gitd/routers/streaming_viewers.py +113 -0
  42. ghost_in_the_droid-1.0.0/gitd/routers/tests.py +370 -0
  43. ghost_in_the_droid-1.0.0/gitd/routers/tools_hub.py +155 -0
  44. ghost_in_the_droid-1.0.0/gitd/schemas/__init__.py +1 -0
  45. ghost_in_the_droid-1.0.0/gitd/schemas/common.py +16 -0
  46. ghost_in_the_droid-1.0.0/gitd/services/__init__.py +0 -0
  47. ghost_in_the_droid-1.0.0/gitd/services/_emulator_helpers.py +319 -0
  48. ghost_in_the_droid-1.0.0/gitd/services/_emulator_pool.py +161 -0
  49. ghost_in_the_droid-1.0.0/gitd/services/_job_helpers.py +209 -0
  50. ghost_in_the_droid-1.0.0/gitd/services/agent_chat.py +733 -0
  51. ghost_in_the_droid-1.0.0/gitd/services/agent_chat_claude_code.py +207 -0
  52. ghost_in_the_droid-1.0.0/gitd/services/agent_tools.py +486 -0
  53. ghost_in_the_droid-1.0.0/gitd/services/db_helpers.py +137 -0
  54. ghost_in_the_droid-1.0.0/gitd/services/device_context.py +903 -0
  55. ghost_in_the_droid-1.0.0/gitd/services/emulator_service.py +400 -0
  56. ghost_in_the_droid-1.0.0/gitd/services/job_engine.py +498 -0
  57. ghost_in_the_droid-1.0.0/gitd/services/scheduler_service.py +301 -0
  58. ghost_in_the_droid-1.0.0/gitd/skills/__init__.py +4 -0
  59. ghost_in_the_droid-1.0.0/gitd/skills/_base/__init__.py +18 -0
  60. ghost_in_the_droid-1.0.0/gitd/skills/_base/actions/__init__.py +10 -0
  61. ghost_in_the_droid-1.0.0/gitd/skills/_base/actions/core.py +168 -0
  62. ghost_in_the_droid-1.0.0/gitd/skills/_run_skill.py +235 -0
  63. ghost_in_the_droid-1.0.0/gitd/skills/auto_creator.py +424 -0
  64. ghost_in_the_droid-1.0.0/gitd/skills/base.py +483 -0
  65. ghost_in_the_droid-1.0.0/gitd/skills/gmail_utils.py +235 -0
  66. ghost_in_the_droid-1.0.0/gitd/skills/macro_recorder.py +196 -0
  67. ghost_in_the_droid-1.0.0/gitd/skills/play_store/__init__.py +19 -0
  68. ghost_in_the_droid-1.0.0/gitd/skills/play_store/actions/__init__.py +0 -0
  69. ghost_in_the_droid-1.0.0/gitd/skills/play_store/actions/core.py +140 -0
  70. ghost_in_the_droid-1.0.0/gitd/skills/play_store/workflows.py +68 -0
  71. ghost_in_the_droid-1.0.0/gitd/skills/send_gmail_email/__init__.py +1 -0
  72. ghost_in_the_droid-1.0.0/gitd/skills/send_gmail_email/actions/__init__.py +0 -0
  73. ghost_in_the_droid-1.0.0/gitd/skills/send_gmail_email/workflows/__init__.py +0 -0
  74. ghost_in_the_droid-1.0.0/gitd/skills/test_pic/__init__.py +1 -0
  75. ghost_in_the_droid-1.0.0/gitd/skills/tiktok/__init__.py +21 -0
  76. ghost_in_the_droid-1.0.0/gitd/skills/tiktok/actions/__init__.py +8 -0
  77. ghost_in_the_droid-1.0.0/gitd/skills/tiktok/actions/core.py +119 -0
  78. ghost_in_the_droid-1.0.0/gitd/skills/tiktok/workflows/__init__.py +1 -0
  79. ghost_in_the_droid-1.0.0/gitd/skills/tiktok/workflows/publish_draft.py +33 -0
  80. ghost_in_the_droid-1.0.0/gitd/skills/tiktok/workflows/upload_video.py +57 -0
  81. ghost_in_the_droid-1.0.0/gitd/tools/__init__.py +0 -0
  82. ghost_in_the_droid-1.0.0/gitd/tools/tts.py +124 -0
  83. ghost_in_the_droid-1.0.0/gitd/tools/video_overlay.py +198 -0
  84. ghost_in_the_droid-1.0.0/pyproject.toml +66 -0
  85. ghost_in_the_droid-1.0.0/setup.cfg +4 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ghost in the Droid Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,404 @@
1
+ Metadata-Version: 2.4
2
+ Name: ghost-in-the-droid
3
+ Version: 1.0.0
4
+ Summary: Give any AI agent an Android body. Open-source phone automation via ADB — tap, swipe, type, run skills, scale across devices.
5
+ License: MIT
6
+ Project-URL: Homepage, https://ghostinthedroid.com
7
+ Project-URL: Repository, https://github.com/ghost-in-the-droid/android-agent
8
+ Project-URL: Documentation, https://ghostinthedroid.com/getting-started/installation/
9
+ Project-URL: Issues, https://github.com/ghost-in-the-droid/android-agent/issues
10
+ Keywords: android,adb,automation,ghost,agent,mcp,fastapi,phone-farm
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Framework :: FastAPI
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Software Development :: Testing
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: fastapi>=0.115
21
+ Requires-Dist: uvicorn[standard]>=0.30
22
+ Requires-Dist: sqlalchemy>=2.0
23
+ Requires-Dist: alembic>=1.13
24
+ Requires-Dist: pydantic>=2.9
25
+ Requires-Dist: pydantic-settings>=2.5
26
+ Requires-Dist: requests>=2.31
27
+ Requires-Dist: python-dotenv>=1.0
28
+ Requires-Dist: pyyaml>=6.0
29
+ Requires-Dist: psutil>=5.9
30
+ Requires-Dist: openai>=1.0
31
+ Requires-Dist: mcp>=1.20
32
+ Provides-Extra: llm
33
+ Requires-Dist: anthropic>=0.30; extra == "llm"
34
+ Provides-Extra: analytics
35
+ Requires-Dist: google-analytics-data>=0.18; extra == "analytics"
36
+ Provides-Extra: ocr
37
+ Requires-Dist: rapidocr-onnxruntime>=1.3; extra == "ocr"
38
+ Provides-Extra: test
39
+ Requires-Dist: playwright>=1.40; extra == "test"
40
+ Requires-Dist: pytest>=7.0; extra == "test"
41
+ Provides-Extra: all
42
+ Requires-Dist: ghost-in-the-droid[analytics,llm,ocr,test]; extra == "all"
43
+ Dynamic: license-file
44
+
45
+ # Ghost in the Droid
46
+
47
+ <p align="center">
48
+ <img src="docs/assets/mascot/12-the-tap.png" alt="Ghost tapping a phone" width="200" />
49
+ </p>
50
+
51
+ <p align="center">
52
+ <strong>Summon a ghost into your Android.</strong><br/>
53
+ It sees the screen. It taps the buttons. It never sleeps.
54
+ </p>
55
+
56
+ <p align="center">
57
+ <a href="https://ghostinthedroid.com">Website</a> &middot;
58
+ <a href="https://ghostinthedroid.com/getting-started/installation/">Docs</a> &middot;
59
+ <a href="https://ghostinthedroid.com/skills/">Skill Hub</a>
60
+ </p>
61
+
62
+ ![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue)
63
+ ![License: MIT](https://img.shields.io/badge/license-MIT-green)
64
+ ![FastAPI](https://img.shields.io/badge/FastAPI-0.115-teal)
65
+
66
+ ---
67
+
68
+ ## What It Does
69
+
70
+ Open-source Python framework for controlling real Android phones via ADB. No app installed. No footprint. Pure automation.
71
+
72
+ Define **skills** for any app, run them from the dashboard or API, scale across a phone farm.
73
+
74
+ **The ghost taps what you'd tap**
75
+ - 50+ ADB methods — tap, swipe, type, clipboard, stealth variants
76
+ - Live phone screen streaming (MJPEG and WebRTC)
77
+ - Interactive touch-to-tap on the streamed screen
78
+ - Multi-device phone farm with per-device job queues
79
+
80
+ **Forge reusable skills for any app**
81
+ - YAML-based UI element definitions per app
82
+ - Python action classes with precondition checks
83
+ - Multi-step workflows that chain actions together
84
+ - Built-in skills for TikTok and Play Store
85
+ - **Skill Hub** — browse, search, and install skills from the community registry
86
+ - Install from CLI: `android-agent skill install tiktok`
87
+
88
+ **Teach the ghost new tricks**
89
+ - BFS-based auto app explorer — discovers every screen and transition
90
+ - LLM-assisted Skill Creator — chat with AI while viewing the live device stream
91
+ - The AI identifies UI elements and generates action/workflow code
92
+
93
+ **Scale the haunting**
94
+ - Multi-device phone farm with per-device job queues
95
+ - Bot runner: queue, schedule, and monitor automation jobs
96
+ - Per-device integration tests with ADB screen recording
97
+
98
+ ---
99
+
100
+ ## Requirements
101
+
102
+ - **Python 3.10+**
103
+ - **Android phone** with USB debugging enabled (Settings > Developer Options > USB Debugging)
104
+ - **ADB** installed and on PATH (`adb devices` should list your phone)
105
+ - **Node.js 18+** (for the frontend dev server)
106
+
107
+ ---
108
+
109
+ ## Quick Start
110
+
111
+ ```bash
112
+ # 1. Clone the repo
113
+ git clone https://github.com/ghost-in-the-droid/android-agent.git
114
+ cd ghost-in-the-droid
115
+
116
+ # 2. Install Python dependencies
117
+ pip install -e ".[all]"
118
+
119
+ # 3. Verify ADB sees your device
120
+ adb devices
121
+
122
+ # 4. Start the backend
123
+ python3 run.py
124
+ # API running at http://localhost:5055
125
+ # Interactive API docs at http://localhost:5055/docs
126
+
127
+ # 5. Start the frontend (separate terminal)
128
+ cd frontend
129
+ npm install
130
+ npx vite --host 0.0.0.0 --port 6175
131
+ # Dashboard at http://localhost:6175
132
+ ```
133
+
134
+ ### Environment Variables
135
+
136
+ Copy `.env.example` to `.env` (if provided) or create a `.env` file in the project root. The server reads configuration via Pydantic Settings. Optional variables include:
137
+
138
+ | Variable | Purpose |
139
+ |----------|---------|
140
+ | `OPENAI_API_KEY` | LLM features (Skill Creator, Agent Chat) |
141
+ | `ANTHROPIC_API_KEY` | Alternative LLM provider |
142
+ | `OPENROUTER_API_KEY` | OpenRouter LLM provider |
143
+ | `DEFAULT_DEVICE` | ADB serial (auto-detected if empty) |
144
+
145
+ ---
146
+
147
+ ## Architecture
148
+
149
+ ```
150
+ android-agent/
151
+ run.py # Entry point: Uvicorn on :5055
152
+ gitd/
153
+ app.py # FastAPI app factory + plugin hook
154
+ config.py # Pydantic settings from .env
155
+ models/ # SQLAlchemy 2.0 ORM
156
+ schemas/ # Pydantic v2 request/response validation
157
+ routers/ # FastAPI route handlers
158
+ services/ # Business logic helpers
159
+ skills/ # Skill packages per app
160
+ _base/ # Base classes (Skill, Action, Workflow)
161
+ tiktok/ # TikTok skill (elements, actions, workflows)
162
+ play_store/ # Play Store skill (install, update, search)
163
+ bots/
164
+ common/
165
+ adb.py # Device class: tap, swipe, dump XML, wait_for
166
+ mcp_server.py # MCP server — expose tools for any LLM agent
167
+ alembic/ # Database migrations
168
+ frontend/ # Vue 3 + Vite + TypeScript + Tailwind CSS
169
+ src/
170
+ App.vue # Tab shell (9 tabs)
171
+ views/ # One view per tab
172
+ composables/ # Typed API fetch wrapper
173
+ portal/ # Kotlin companion app (WebRTC, accessibility)
174
+ site/ # Docs site (Astro + Starlight)
175
+ tests/ # Integration tests (require a connected phone)
176
+ docs/ # Architecture docs
177
+ ```
178
+
179
+ ### How It Fits Together
180
+
181
+ 1. **Backend** (`run.py`) starts a FastAPI server on port 5055 with routers covering device control, skills, bots, scheduling, and streaming.
182
+ 2. **Frontend** (`frontend/`) is a Vue 3 SPA that talks to the backend via `/api/*`. Vite proxies API calls to the backend during development.
183
+ 3. **Skills** define how to interact with a specific app. Each skill has a `elements.yaml` (UI elements), Python actions (atomic operations), and workflows (multi-step sequences).
184
+ 4. **Bots** are long-running subprocess scripts that use the `Device` class from `bots/common/adb.py` to control the phone. The backend spawns and manages them.
185
+ 5. **Database** is SQLite with SQLAlchemy 2.0 ORM and Alembic migrations.
186
+
187
+ ---
188
+
189
+ ## API Documentation
190
+
191
+ The backend auto-generates interactive API docs via FastAPI:
192
+
193
+ - **Swagger UI**: [http://localhost:5055/docs](http://localhost:5055/docs)
194
+ - **ReDoc**: [http://localhost:5055/redoc](http://localhost:5055/redoc)
195
+
196
+ API domains: phone, streaming, skills, creator, explorer, agent-chat, bot, scheduler, tests, tools, misc.
197
+
198
+ ---
199
+
200
+ ## Dashboard Tabs
201
+
202
+ | Tab | What It Does |
203
+ |-----|-------------|
204
+ | Phone Agent | Live device stream (MJPEG/WebRTC), tap/swipe on screen, multi-device view |
205
+ | Scheduler | Cron-style job scheduling with queue management |
206
+ | Skill Hub | Browse installed skills, run actions and workflows, export/delete |
207
+ | Skill Creator | LLM-assisted skill builder with live device stream |
208
+ | Skill Miner | Auto app explorer — BFS state discovery with screenshots |
209
+ | Tools | Utility tools hub |
210
+ | Manual Run | Start/stop bot jobs, queue management, logs |
211
+ | Tests | Per-device test runner with screen recording playback |
212
+ | Emulators | Create, boot, snapshot, and manage Android emulators |
213
+
214
+ ---
215
+
216
+ ## MCP Server — Give Any AI Agent an Android Body
217
+
218
+ The project ships an [MCP](https://modelcontextprotocol.io) server with 35 tools for Android control. Any MCP-compatible AI client (Claude Code, Claude Desktop, Cursor, VS Code Copilot, Windsurf) can use them.
219
+
220
+ ### Install
221
+
222
+ One command — works with Claude Code, Codex, Cursor, VS Code Copilot, Windsurf:
223
+
224
+ ```bash
225
+ claude mcp add android-agent -- uvx --from ghost-in-the-droid android-agent-mcp
226
+ ```
227
+
228
+ That's it. `uvx` installs the package, creates an isolated env, and runs the MCP server. No clone, no venv, no manual setup.
229
+
230
+ **Other clients** — same command, different registration:
231
+
232
+ ```bash
233
+ # Codex (OpenAI)
234
+ codex mcp add android-agent -- uvx --from ghost-in-the-droid android-agent-mcp
235
+ ```
236
+
237
+ **Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
238
+ ```json
239
+ {
240
+ "mcpServers": {
241
+ "android-agent": {
242
+ "command": "uvx",
243
+ "args": ["--from", "ghost-in-the-droid", "android-agent-mcp"]
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ **VS Code Copilot** (`.vscode/mcp.json`):
250
+ ```json
251
+ {
252
+ "servers": {
253
+ "android-agent": {
254
+ "command": "uvx",
255
+ "args": ["--from", "ghost-in-the-droid", "android-agent-mcp"]
256
+ }
257
+ }
258
+ }
259
+ ```
260
+
261
+ **Cursor** (`.cursor/mcp.json`) / **Windsurf** (`~/.codeium/windsurf/mcp_config.json`):
262
+ ```json
263
+ {
264
+ "mcpServers": {
265
+ "android-agent": {
266
+ "command": "uvx",
267
+ "args": ["--from", "ghost-in-the-droid", "android-agent-mcp"]
268
+ }
269
+ }
270
+ }
271
+ ```
272
+
273
+ **For contributors** who clone the repo: the `.mcp.json` is already there — 35 tools available on first `claude` launch.
274
+
275
+ ### Available tools
276
+
277
+ | Category | Tools |
278
+ |----------|-------|
279
+ | Screen | `screenshot`, `get_elements`, `get_screen_tree`, `get_screen_xml`, `screenshot_annotated`, `screenshot_cropped` |
280
+ | Interaction | `tap`, `tap_element`, `swipe`, `long_press`, `type_text`, `type_unicode`, `press_back`, `press_home`, `press_key` |
281
+ | Apps | `launch_app`, `search_apps`, `list_apps`, `launch_intent` |
282
+ | Context | `get_phone_state`, `classify_screen`, `find_on_screen`, `ocr_screen`, `ocr_region` |
283
+ | Device | `list_devices`, `clipboard_get`, `clipboard_set`, `get_notifications`, `open_notifications`, `toggle_overlay` |
284
+ | Skills | `list_skills`, `run_workflow`, `run_action`, `create_skill`, `explore_app` |
285
+
286
+ ---
287
+
288
+ ## Skill Hub CLI
289
+
290
+ ```bash
291
+ # Search the public registry
292
+ android-agent skill search tiktok
293
+
294
+ # Install a skill
295
+ android-agent skill install tiktok
296
+
297
+ # Install from any GitHub repo
298
+ android-agent skill install github.com/someone/their-skill
299
+
300
+ # List installed skills
301
+ android-agent skill list
302
+
303
+ # Update a skill
304
+ android-agent skill update tiktok
305
+
306
+ # Remove a skill
307
+ android-agent skill remove tiktok
308
+
309
+ # Validate before publishing
310
+ android-agent skill validate ./my-skill/
311
+ ```
312
+
313
+ The skill registry lives in [`registry/`](registry/) in this repo. Community skills are auto-discovered nightly from repos tagged `android-agent-skill`.
314
+
315
+ ## Teach the Ghost a New App
316
+
317
+ Two ways to forge a skill:
318
+
319
+ **Community skill** (your own repo):
320
+ 1. Create a new repo with `skill.yaml`, `elements.yaml`, actions, workflows
321
+ 2. Tag your repo with the `android-agent-skill` topic
322
+ 3. It appears on the hub automatically (nightly scraper)
323
+
324
+ **Official skill** (PR to this repo):
325
+ 1. Build and test as a community skill first
326
+ 2. Open a PR adding your skill to [`registry/`](registry/)
327
+ 3. CI validates, maintainer reviews, gets "Official" badge
328
+
329
+ Each skill needs:
330
+ - `skill.yaml` — metadata (name, version, app package, actions, workflows)
331
+ - `elements.yaml` — UI element resource IDs and descriptions
332
+ - `actions/` — Python classes extending `Action` with `precondition()` and `execute()`
333
+ - `workflows/` — Python classes extending `Workflow` with `steps()`
334
+
335
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide.
336
+
337
+ ---
338
+
339
+ ## Tech Stack
340
+
341
+ | Layer | Technology |
342
+ |-------|-----------|
343
+ | Backend | FastAPI, Uvicorn, Python 3.10+ |
344
+ | ORM | SQLAlchemy 2.0 (Mapped types) |
345
+ | Migrations | Alembic |
346
+ | Validation | Pydantic v2 |
347
+ | Database | SQLite (WAL mode) |
348
+ | Frontend | Vue 3 (Composition API), TypeScript, Vite, Tailwind CSS 4 |
349
+ | Charts | Chart.js 4, Plotly.js |
350
+ | Device Control | ADB (Android Debug Bridge) |
351
+ | Streaming | MJPEG, WebRTC (via Ghost Portal companion app) |
352
+ | LLM | OpenAI, Anthropic (optional) |
353
+ | Linting | Ruff |
354
+ | Testing | pytest, Playwright (optional) |
355
+
356
+ ---
357
+
358
+ ## Running Tests
359
+
360
+ Tests are integration tests that require a connected Android phone:
361
+
362
+ ```bash
363
+ # Run all tests on a specific device
364
+ DEVICE=<serial> python3 -m pytest tests/ -v --tb=short
365
+
366
+ # Run a single test file
367
+ DEVICE=<serial> python3 -m pytest tests/test_00_baseline.py -v
368
+ ```
369
+
370
+ Get your device serial from `adb devices`.
371
+
372
+ ---
373
+
374
+ ## Database Migrations
375
+
376
+ The project uses Alembic for schema migrations:
377
+
378
+ ```bash
379
+ # Generate a migration after editing a model
380
+ alembic revision --autogenerate -m "add new_field to my_table"
381
+
382
+ # Apply pending migrations
383
+ alembic upgrade head
384
+
385
+ # Rollback one migration
386
+ alembic downgrade -1
387
+ ```
388
+
389
+ ---
390
+
391
+ ## Contributing
392
+
393
+ The ghost gets stronger with every skill. See [CONTRIBUTING.md](CONTRIBUTING.md) for:
394
+
395
+ - Adding skills for new apps (highest impact)
396
+ - Writing actions and workflows
397
+ - Backend architecture and code style
398
+ - PR process
399
+
400
+ ---
401
+
402
+ ## License
403
+
404
+ [MIT](LICENSE) — The ghost is free. The ghost is open source. The ghost is yours.