fsq-mac 0.1.1__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 (74) hide show
  1. fsq_mac-0.1.1/.claude/skills/mac-automation.md +206 -0
  2. fsq_mac-0.1.1/.github/workflows/ci.yml +57 -0
  3. fsq_mac-0.1.1/.github/workflows/publish.yml +57 -0
  4. fsq_mac-0.1.1/.gitignore +39 -0
  5. fsq_mac-0.1.1/CLAUDE.md +51 -0
  6. fsq_mac-0.1.1/LICENSE +21 -0
  7. fsq_mac-0.1.1/PKG-INFO +12 -0
  8. fsq_mac-0.1.1/README.md +124 -0
  9. fsq_mac-0.1.1/docs/architecture-review.md +369 -0
  10. fsq_mac-0.1.1/docs/architecture.md +134 -0
  11. fsq_mac-0.1.1/docs/cli-reference.md +197 -0
  12. fsq_mac-0.1.1/docs/plans/2026-04-03-phase0-foundation-design.md +100 -0
  13. fsq_mac-0.1.1/docs/plans/2026-04-03-phase0-implementation.md +609 -0
  14. fsq_mac-0.1.1/docs/plans/2026-04-03-phase2-core-differentiators-design.md +258 -0
  15. fsq_mac-0.1.1/docs/plans/2026-04-03-phase2-core-differentiators-implementation.md +378 -0
  16. fsq_mac-0.1.1/docs/plans/2026-04-03-phase3-ci-design.md +136 -0
  17. fsq_mac-0.1.1/docs/plans/2026-04-03-phase3-ci-implementation.md +173 -0
  18. fsq_mac-0.1.1/docs/plans/2026-04-03-phase3-trace-loop-design.md +258 -0
  19. fsq_mac-0.1.1/docs/plans/2026-04-03-phase3-trace-loop-implementation.md +334 -0
  20. fsq_mac-0.1.1/docs/plans/2026-04-06-phase4-codegen-perf-plugins-docs.md +171 -0
  21. fsq_mac-0.1.1/docs/plugins.md +190 -0
  22. fsq_mac-0.1.1/docs/quickstart.md +172 -0
  23. fsq_mac-0.1.1/docs/releasing.md +151 -0
  24. fsq_mac-0.1.1/docs/testing/manual-e2e-test-plan.md +346 -0
  25. fsq_mac-0.1.1/docs/testing/phase-0-1-test-plan.md +273 -0
  26. fsq_mac-0.1.1/docs/testing/phase-4-manual-e2e-test-plan.md +13 -0
  27. fsq_mac-0.1.1/docs/trace-codegen.md +228 -0
  28. fsq_mac-0.1.1/pyproject.toml +40 -0
  29. fsq_mac-0.1.1/src/fsq_mac/__init__.py +6 -0
  30. fsq_mac-0.1.1/src/fsq_mac/adapters/__init__.py +56 -0
  31. fsq_mac-0.1.1/src/fsq_mac/adapters/appium_mac2.py +1224 -0
  32. fsq_mac-0.1.1/src/fsq_mac/adapters/protocol.py +108 -0
  33. fsq_mac-0.1.1/src/fsq_mac/cli.py +333 -0
  34. fsq_mac-0.1.1/src/fsq_mac/client.py +175 -0
  35. fsq_mac-0.1.1/src/fsq_mac/codegen.py +158 -0
  36. fsq_mac-0.1.1/src/fsq_mac/conf/config.template.json +14 -0
  37. fsq_mac-0.1.1/src/fsq_mac/core.py +711 -0
  38. fsq_mac-0.1.1/src/fsq_mac/daemon.py +496 -0
  39. fsq_mac-0.1.1/src/fsq_mac/doctor.py +204 -0
  40. fsq_mac-0.1.1/src/fsq_mac/formatters.py +89 -0
  41. fsq_mac-0.1.1/src/fsq_mac/models.py +303 -0
  42. fsq_mac-0.1.1/src/fsq_mac/session.py +140 -0
  43. fsq_mac-0.1.1/src/fsq_mac/trace.py +205 -0
  44. fsq_mac-0.1.1/tests/__init__.py +0 -0
  45. fsq_mac-0.1.1/tests/conftest.py +48 -0
  46. fsq_mac-0.1.1/tests/test_adapter_delays.py +56 -0
  47. fsq_mac-0.1.1/tests/test_adapter_helpers.py +151 -0
  48. fsq_mac-0.1.1/tests/test_adapter_methods.py +739 -0
  49. fsq_mac-0.1.1/tests/test_adapter_registry.py +91 -0
  50. fsq_mac-0.1.1/tests/test_ci_workflow.py +54 -0
  51. fsq_mac-0.1.1/tests/test_cli.py +522 -0
  52. fsq_mac-0.1.1/tests/test_cli_version.py +20 -0
  53. fsq_mac-0.1.1/tests/test_client.py +287 -0
  54. fsq_mac-0.1.1/tests/test_codegen.py +227 -0
  55. fsq_mac-0.1.1/tests/test_core.py +476 -0
  56. fsq_mac-0.1.1/tests/test_daemon.py +387 -0
  57. fsq_mac-0.1.1/tests/test_daemon_lock.py +37 -0
  58. fsq_mac-0.1.1/tests/test_doctor.py +182 -0
  59. fsq_mac-0.1.1/tests/test_error_propagation.py +62 -0
  60. fsq_mac-0.1.1/tests/test_find.py +42 -0
  61. fsq_mac-0.1.1/tests/test_formatters.py +137 -0
  62. fsq_mac-0.1.1/tests/test_inspect_refs.py +51 -0
  63. fsq_mac-0.1.1/tests/test_models.py +167 -0
  64. fsq_mac-0.1.1/tests/test_plugin_system.py +89 -0
  65. fsq_mac-0.1.1/tests/test_protocol.py +32 -0
  66. fsq_mac-0.1.1/tests/test_routes.py +162 -0
  67. fsq_mac-0.1.1/tests/test_screenshot.py +74 -0
  68. fsq_mac-0.1.1/tests/test_semantics.py +92 -0
  69. fsq_mac-0.1.1/tests/test_session.py +43 -0
  70. fsq_mac-0.1.1/tests/test_timeout.py +41 -0
  71. fsq_mac-0.1.1/tests/test_trace.py +171 -0
  72. fsq_mac-0.1.1/tests/test_tree_cache.py +171 -0
  73. fsq_mac-0.1.1/tests/test_type_core.py +65 -0
  74. fsq_mac-0.1.1/tests/test_type_verify.py +53 -0
@@ -0,0 +1,206 @@
1
+ # macOS Automation with fsq-mac
2
+
3
+ Use when the user asks to automate, interact with, test, or control a macOS native application. Triggers: mac automation, click button, UI testing, accessibility, element inspect, screenshot, app launch, macOS app, native app automation.
4
+
5
+ ## Prerequisites
6
+
7
+ - Appium server running (`appium` in a terminal)
8
+ - Mac2 driver installed (`appium driver install mac2`)
9
+ - macOS Accessibility permissions granted
10
+ - Run `mac doctor` to verify setup
11
+
12
+ ## Core Workflow
13
+
14
+ Every automation session follows this pattern:
15
+
16
+ 1. **Start session** — `mac session start`
17
+ 2. **Launch/activate app** — `mac app launch <bundle_id>`
18
+ 3. **Inspect UI** — `mac element inspect` to discover elements
19
+ 4. **Act** — click, type, scroll, etc. using element refs (e0, e1, ...)
20
+ 5. **Verify** — `mac capture screenshot` or re-inspect to confirm result
21
+ 6. **End session** — `mac session end`
22
+
23
+ ## Command Reference
24
+
25
+ ### Session
26
+ | Command | Description |
27
+ |---------|-------------|
28
+ | `mac session start` | Start a new automation session |
29
+ | `mac session get` | Get current session info |
30
+ | `mac session list` | List all sessions |
31
+ | `mac session end` | End current session |
32
+
33
+ ### Application
34
+ | Command | Description |
35
+ |---------|-------------|
36
+ | `mac app launch <bundle_id>` | Launch app (e.g. `com.apple.calculator`) |
37
+ | `mac app activate <bundle_id>` | Bring app to front |
38
+ | `mac app current` | Get frontmost app info |
39
+ | `mac app list` | List running apps |
40
+ | `mac app terminate <bundle_id>` | Terminate app (requires `--allow-dangerous`) |
41
+
42
+ ### Element
43
+ | Command | Description |
44
+ |---------|-------------|
45
+ | `mac element inspect` | List all visible UI elements with refs (e0, e1, ...) |
46
+ | `mac element find <locator>` | Find element by locator value |
47
+ | `mac element click <ref>` | Click element (e.g. `e5`) |
48
+ | `mac element right-click <ref>` | Right-click element |
49
+ | `mac element double-click <ref>` | Double-click element |
50
+ | `mac element type <ref> <text>` | Type text into element |
51
+ | `mac element scroll <ref> [up|down|left|right]` | Scroll element |
52
+ | `mac element hover <ref>` | Hover over element |
53
+ | `mac element drag <source> <target>` | Drag from source to target element |
54
+
55
+ ### Input (no element target)
56
+ | Command | Description |
57
+ |---------|-------------|
58
+ | `mac input key <key>` | Press key (return, space, tab, escape, etc.) |
59
+ | `mac input hotkey <combo>` | Key combo (e.g. `command+c`, `command+shift+s`) |
60
+ | `mac input text <text>` | Type text to focused element |
61
+
62
+ ### Capture
63
+ | Command | Description |
64
+ |---------|-------------|
65
+ | `mac capture screenshot [path]` | Take screenshot (default: ./screenshot.png) |
66
+ | `mac capture screenshot --element <ref> [path]` | Screenshot a specific element |
67
+ | `mac capture screenshot --rect x,y,w,h [path]` | Screenshot a region |
68
+ | `mac capture ui-tree` | Get raw UI element tree (XML) |
69
+
70
+ ### Window
71
+ | Command | Description |
72
+ |---------|-------------|
73
+ | `mac window current` | Get frontmost window info |
74
+ | `mac window list` | List windows of managed app |
75
+ | `mac window focus <index>` | Focus window by index |
76
+
77
+ ### Wait
78
+ | Command | Description |
79
+ |---------|-------------|
80
+ | `mac wait element <locator>` | Wait for element to appear |
81
+ | `mac wait window <title>` | Wait for window to appear |
82
+ | `mac wait app <bundle_id>` | Wait for app to start |
83
+
84
+ ### Doctor
85
+ | Command | Description |
86
+ |---------|-------------|
87
+ | `mac doctor` | Run all diagnostics |
88
+ | `mac doctor permissions` | Check Accessibility permissions |
89
+ | `mac doctor backend` | Check Appium server and Mac2 driver |
90
+
91
+ ## Global Options
92
+
93
+ | Option | Description |
94
+ |--------|-------------|
95
+ | `--session <id>` | Target a specific session (default: most recent) |
96
+ | `--strategy <strategy>` | Locator strategy: accessibility_id, name, xpath, class_name, ios_predicate |
97
+ | `--timeout <ms>` | Timeout in milliseconds (default: 120000) |
98
+ | `--pretty` | Human-friendly output (default: compact JSON for agents) |
99
+ | `--allow-dangerous` | Allow dangerous operations (e.g. app terminate) |
100
+ | `--version` | Show version |
101
+
102
+ ## Response Format
103
+
104
+ Every command returns a JSON envelope:
105
+
106
+ ```json
107
+ {
108
+ "ok": true,
109
+ "command": "element.click",
110
+ "session_id": "s1",
111
+ "data": {},
112
+ "error": null,
113
+ "meta": {
114
+ "backend": "appium_mac2",
115
+ "duration_ms": 1234,
116
+ "timestamp": "2026-04-03T10:00:00Z",
117
+ "frontmost_app": "com.apple.calculator",
118
+ "frontmost_window": "Calculator"
119
+ }
120
+ }
121
+ ```
122
+
123
+ On error:
124
+
125
+ ```json
126
+ {
127
+ "ok": false,
128
+ "command": "element.click",
129
+ "error": {
130
+ "code": "ELEMENT_NOT_FOUND",
131
+ "message": "Element 'e5' not found",
132
+ "retryable": true,
133
+ "suggested_next_action": "mac element inspect",
134
+ "doctor_hint": null
135
+ }
136
+ }
137
+ ```
138
+
139
+ ## Error Recovery
140
+
141
+ When a command fails, check `error.code` and follow this recovery strategy:
142
+
143
+ | Error Code | Recovery |
144
+ |------------|----------|
145
+ | `SESSION_NOT_FOUND` | Run `mac session start` |
146
+ | `BACKEND_UNAVAILABLE` | Run `mac doctor backend`, ensure Appium is running |
147
+ | `ELEMENT_NOT_FOUND` | Run `mac element inspect` to refresh elements, find correct ref |
148
+ | `ELEMENT_REFERENCE_STALE` | Run `mac element inspect` — refs are invalidated after mutations |
149
+ | `ELEMENT_AMBIGUOUS` | Use `--first-match` or refine the locator strategy |
150
+ | `TYPE_VERIFICATION_FAILED` | Typed value didn't match; re-type or verify the target field |
151
+ | `ACTION_BLOCKED` | Add `--allow-dangerous` flag for dangerous operations |
152
+ | `TIMEOUT` | Increase `--timeout` or check if the target exists |
153
+ | `PERMISSION_DENIED` | Run `mac doctor permissions` and grant Accessibility access |
154
+
155
+ ## Element References
156
+
157
+ - `mac element inspect` assigns refs: `e0`, `e1`, `e2`, ...
158
+ - Refs are **scoped to the last inspect/find** — a new inspect invalidates all previous refs
159
+ - After any mutation (click, type, etc.), refs may become stale
160
+ - Pattern: **inspect -> act -> re-inspect** for multi-step flows
161
+
162
+ ## Safety Model
163
+
164
+ Commands are classified into three safety levels:
165
+
166
+ - **SAFE** — read-only operations (inspect, screenshot, list, wait, doctor)
167
+ - **GUARDED** — operations that modify state (click, type, launch, activate)
168
+ - **DANGEROUS** — destructive operations (terminate) — requires `--allow-dangerous`
169
+
170
+ ## Common Patterns
171
+
172
+ ### Calculator: 5 + 3
173
+
174
+ ```bash
175
+ mac session start
176
+ mac app launch com.apple.calculator
177
+ mac element inspect # discover elements
178
+ mac element click e5 # click "5"
179
+ mac element inspect # re-inspect after mutation
180
+ mac element click e2 # click "+"
181
+ mac element inspect
182
+ mac element click e3 # click "3"
183
+ mac element inspect
184
+ mac element click e8 # click "="
185
+ mac capture screenshot ./result.png # verify result
186
+ mac session end
187
+ ```
188
+
189
+ ### Type text into a search field
190
+
191
+ ```bash
192
+ mac session start
193
+ mac app activate com.apple.safari
194
+ mac element inspect # find the search field
195
+ mac element type e12 "hello world" # type into it
196
+ mac input hotkey command+a # select all
197
+ mac capture screenshot ./typed.png
198
+ ```
199
+
200
+ ### Tips
201
+
202
+ - Always run `mac element inspect` before interacting with elements
203
+ - After any click/type, run `mac element inspect` again — refs are invalidated
204
+ - Use `mac capture screenshot` to visually verify results between steps
205
+ - Use `--pretty` when debugging interactively, omit for agent consumption
206
+ - Common bundle IDs: `com.apple.calculator`, `com.apple.Safari`, `com.apple.finder`, `com.apple.TextEdit`, `com.apple.systempreferences`
@@ -0,0 +1,57 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ["v*"]
7
+ pull_request:
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ['3.10', '3.11', '3.12']
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v5
26
+ with:
27
+ enable-cache: true
28
+
29
+ - name: Install dependencies
30
+ run: uv sync --group dev
31
+
32
+ - name: Run tests
33
+ run: uv run pytest tests/ --junitxml=test-results/junit.xml --cov=fsq_mac --cov-report=xml:test-results/coverage.xml --cov-report=term-missing
34
+
35
+ - name: Upload JUnit XML
36
+ if: always()
37
+ uses: actions/upload-artifact@v4
38
+ with:
39
+ name: junit-results-py${{ matrix.python-version }}
40
+ path: test-results/junit.xml
41
+ if-no-files-found: error
42
+
43
+ - name: Upload coverage report
44
+ if: always()
45
+ uses: actions/upload-artifact@v4
46
+ with:
47
+ name: coverage-py${{ matrix.python-version }}
48
+ path: test-results/coverage.xml
49
+ if-no-files-found: ignore
50
+
51
+ - name: Upload runtime artifacts
52
+ if: always()
53
+ uses: actions/upload-artifact@v4
54
+ with:
55
+ name: runtime-artifacts-py${{ matrix.python-version }}
56
+ path: artifacts/
57
+ if-no-files-found: ignore
@@ -0,0 +1,57 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python-version: ['3.10', '3.11', '3.12']
13
+ steps:
14
+ - name: Checkout
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v5
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+
22
+ - name: Install uv
23
+ uses: astral-sh/setup-uv@v5
24
+ with:
25
+ enable-cache: true
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --group dev
29
+
30
+ - name: Run tests
31
+ run: uv run pytest tests/ --junitxml=test-results/junit.xml --cov=fsq_mac --cov-report=xml:test-results/coverage.xml --cov-report=term-missing
32
+
33
+ publish:
34
+ needs: test
35
+ runs-on: ubuntu-latest
36
+ permissions:
37
+ contents: read
38
+ id-token: write
39
+ environment:
40
+ name: pypi
41
+ steps:
42
+ - name: Checkout
43
+ uses: actions/checkout@v4
44
+
45
+ - name: Set up Python
46
+ uses: actions/setup-python@v5
47
+ with:
48
+ python-version: '3.12'
49
+
50
+ - name: Install uv
51
+ uses: astral-sh/setup-uv@v5
52
+
53
+ - name: Build distributions
54
+ run: uv build
55
+
56
+ - name: Publish to PyPI
57
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,39 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+
10
+ # Virtual environments
11
+ .venv/
12
+ venv/
13
+
14
+ # IDE
15
+ .idea/
16
+ .vscode/
17
+ *.swp
18
+ *.swo
19
+
20
+ # State directory (local)
21
+ .fsq-mac/
22
+
23
+ # Config (user-specific)
24
+ src/fsq_mac/conf/appium_conf.json
25
+
26
+ # OS
27
+ .DS_Store
28
+ Thumbs.db
29
+
30
+ # Test artifacts
31
+ .pytest_cache/
32
+ .coverage
33
+ htmlcov/
34
+
35
+ # uv
36
+ uv.lock
37
+
38
+ # Runtime artifacts
39
+ artifacts/
@@ -0,0 +1,51 @@
1
+ # CLAUDE.md
2
+
3
+ ## Project Overview
4
+
5
+ fsq-mac is an agent-first macOS native application automation CLI. It uses a daemon/client architecture where the CLI communicates over HTTP with a Starlette/Uvicorn daemon that drives Appium Mac2.
6
+
7
+ ## Build & Run
8
+
9
+ ```bash
10
+ # Install dependencies
11
+ uv sync
12
+
13
+ # Install with dev dependencies
14
+ uv sync --group dev
15
+
16
+ # Run tests
17
+ uv run pytest tests/ -v
18
+
19
+ # Run the CLI
20
+ uv run mac session start
21
+ ```
22
+
23
+ ## Architecture
24
+
25
+ ```
26
+ src/fsq_mac/
27
+ ├── cli.py # CLI entry point (argparse)
28
+ ├── client.py # HTTP client → daemon
29
+ ├── daemon.py # Starlette HTTP daemon
30
+ ├── core.py # Product semantics layer
31
+ ├── session.py # Multi-session management
32
+ ├── models.py # Data models, ErrorCode enum, response helpers
33
+ ├── formatters.py # Output formatters (JSON, pretty, table)
34
+ ├── doctor.py # Environment diagnostics
35
+ └── adapters/
36
+ └── appium_mac2.py # Appium Mac2 WebDriver adapter
37
+ ```
38
+
39
+ - `cli.py` → `client.py` → HTTP → `daemon.py` → `core.py` → `session.py` → `adapters/appium_mac2.py`
40
+ - State directory: `~/.fsq-mac/`
41
+ - Daemon auto-starts on first CLI call, auto-exits after 30 min idle
42
+
43
+ ## Key Conventions
44
+
45
+ - **Python 3.10+**, managed with `uv`
46
+ - **src layout**: all source under `src/fsq_mac/`, imports use `from fsq_mac.module import ...`
47
+ - **Response envelope**: Every command returns `{"ok": bool, "data": {...}, "error": {"code": "...", "message": "...", "retryable": bool, "suggested_next_action": "..."}, "meta": {...}}`
48
+ - **Safety classification**: SAFE / GUARDED / DANGEROUS — dangerous commands require `--allow-dangerous`
49
+ - **Element refs**: Static refs (`e0`, `e1`, ...) bound during `element inspect`, invalidated on mutation
50
+ - **AppleScript**: Used for frontmost app/window queries; use `_safe_applescript_str()` to prevent injection
51
+ - **Commit messages**: Present-tense verb, first line under 50 chars
fsq_mac-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Microsoft Corporation
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.
fsq_mac-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: fsq-mac
3
+ Version: 0.1.1
4
+ Summary: Agent-first macOS automation CLI
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: appium-python-client>=2.0.0
8
+ Requires-Dist: httpx>=0.27.0
9
+ Requires-Dist: psutil>=5.9.0
10
+ Requires-Dist: selenium>=4.0.0
11
+ Requires-Dist: starlette>=0.38.0
12
+ Requires-Dist: uvicorn>=0.30.0
@@ -0,0 +1,124 @@
1
+ # fsq-mac
2
+
3
+ Agent-first macOS native application automation CLI.
4
+
5
+ ## Overview
6
+
7
+ `fsq-mac` is a daemon/client CLI tool that drives macOS native applications via Appium Mac2. It is designed for AI agent consumption — every command returns a structured JSON envelope with `ok`, `error.code`, `retryable`, and `suggested_next_action` fields.
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ CLI (cli.py) → HTTP Client → Daemon (Starlette/Uvicorn) → Core → Adapter (Appium Mac2)
13
+ ```
14
+
15
+ - **Daemon auto-lifecycle**: Starts on first CLI call, auto-exits after 30 min idle
16
+ - **Session management**: Monotonic IDs, stale cleanup on restart
17
+ - **Safety classification**: Commands are SAFE / GUARDED / DANGEROUS (gated by `--allow-dangerous`)
18
+
19
+ ## Prerequisites
20
+
21
+ - Python 3.10+
22
+ - [Appium](https://appium.io/) with [mac2 driver](https://github.com/nicedoc/appium-mac2-driver)
23
+ - macOS Accessibility permissions granted to the terminal
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ uv sync
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ```bash
34
+ # Start a session (daemon auto-starts)
35
+ mac session start
36
+
37
+ # Inspect the frontmost app UI tree
38
+ mac element inspect --pretty
39
+
40
+ # Click an element by ref
41
+ mac element click e3
42
+
43
+ # Type text into an element
44
+ mac element type e5 "hello world"
45
+
46
+ # Get frontmost application info
47
+ mac app current --pretty
48
+
49
+ # Get frontmost window info (title, position, size)
50
+ mac window current --pretty
51
+
52
+ # Wait for an element
53
+ mac element wait "Submit" accessibility_id --timeout 5000
54
+
55
+ # End session
56
+ mac session end s1
57
+
58
+ # Check environment
59
+ mac doctor
60
+ ```
61
+
62
+ ## Commands
63
+
64
+ | Command | Description |
65
+ |---------|-------------|
66
+ | `session start` | Start automation session |
67
+ | `session end <id>` | End a session |
68
+ | `session list` | List active sessions |
69
+ | `element inspect` | Inspect UI tree, bind element refs |
70
+ | `element find <query> <by>` | Find elements by locator |
71
+ | `element click <ref>` | Click an element |
72
+ | `element type <ref> <text>` | Type text into an element (with verification) |
73
+ | `element attr <ref> <name>` | Get element attribute |
74
+ | `element scroll <ref> <dir>` | Scroll an element |
75
+ | `element wait <query> <by>` | Wait for element to appear |
76
+ | `app current` | Get frontmost application info |
77
+ | `app list` | List running applications |
78
+ | `app launch <bundle_id>` | Launch an application |
79
+ | `app terminate <bundle_id>` | Terminate an application |
80
+ | `app activate <bundle_id>` | Activate (bring to front) an application |
81
+ | `window current` | Get frontmost window info (title, position, size) |
82
+ | `window list` | List windows of frontmost app |
83
+ | `window switch <index>` | Switch to a window by index |
84
+ | `screenshot` | Take a screenshot |
85
+ | `doctor` | Check environment and dependencies |
86
+
87
+
88
+ ## CI
89
+
90
+ GitHub Actions runs the repository test suite from `.github/workflows/ci.yml`.
91
+ The CI test command is `uv run pytest tests/ --junitxml=test-results/junit.xml`.
92
+ CI uploads `test-results/junit.xml` and any generated `artifacts/` directory as workflow artifacts.
93
+
94
+ ## Release
95
+
96
+ Package publishing is handled by `.github/workflows/publish.yml`.
97
+ Pushing a tag matching `v*` runs the full test matrix first, then builds the wheel and sdist, and finally publishes to PyPI via Trusted Publishing if all tests pass.
98
+
99
+ See [docs/releasing.md](docs/releasing.md) for the full release procedure and PyPI Trusted Publishing setup.
100
+
101
+ Example:
102
+
103
+ ```bash
104
+ git tag v0.1.1
105
+ git push origin v0.1.1
106
+ ```
107
+
108
+ ## Development
109
+
110
+ ```bash
111
+ # Install with dev dependencies
112
+ uv sync --group dev
113
+
114
+ # Run tests
115
+ uv run pytest tests/ -v
116
+ ```
117
+
118
+ ## State Directory
119
+
120
+ Session state is stored in `~/.fsq-mac/`. The daemon writes PID and port files there for lifecycle management.
121
+
122
+ ## License
123
+
124
+ MIT