jvpilot 0.1.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 (46) hide show
  1. jvpilot-0.1.0/LICENSE +21 -0
  2. jvpilot-0.1.0/PKG-INFO +185 -0
  3. jvpilot-0.1.0/README.md +165 -0
  4. jvpilot-0.1.0/app/__init__.py +1 -0
  5. jvpilot-0.1.0/app/__main__.py +5 -0
  6. jvpilot-0.1.0/app/app_catalog.py +55 -0
  7. jvpilot-0.1.0/app/cli.py +362 -0
  8. jvpilot-0.1.0/app/config.py +49 -0
  9. jvpilot-0.1.0/app/decision.py +282 -0
  10. jvpilot-0.1.0/app/desktop.py +441 -0
  11. jvpilot-0.1.0/app/interactions.py +548 -0
  12. jvpilot-0.1.0/app/models.py +61 -0
  13. jvpilot-0.1.0/app/runtime.py +369 -0
  14. jvpilot-0.1.0/app/safety.py +65 -0
  15. jvpilot-0.1.0/app/semantics.py +302 -0
  16. jvpilot-0.1.0/app/speech_model.py +30 -0
  17. jvpilot-0.1.0/app/speech_runtime.py +260 -0
  18. jvpilot-0.1.0/app/text_input.py +424 -0
  19. jvpilot-0.1.0/app/verification.py +153 -0
  20. jvpilot-0.1.0/app/web_navigation.py +154 -0
  21. jvpilot-0.1.0/app/workflows.py +218 -0
  22. jvpilot-0.1.0/jvpilot.egg-info/PKG-INFO +185 -0
  23. jvpilot-0.1.0/jvpilot.egg-info/SOURCES.txt +44 -0
  24. jvpilot-0.1.0/jvpilot.egg-info/dependency_links.txt +1 -0
  25. jvpilot-0.1.0/jvpilot.egg-info/entry_points.txt +2 -0
  26. jvpilot-0.1.0/jvpilot.egg-info/requires.txt +6 -0
  27. jvpilot-0.1.0/jvpilot.egg-info/top_level.txt +1 -0
  28. jvpilot-0.1.0/pyproject.toml +29 -0
  29. jvpilot-0.1.0/requirements.txt +6 -0
  30. jvpilot-0.1.0/setup.cfg +4 -0
  31. jvpilot-0.1.0/tests/test_cli_e2e.py +181 -0
  32. jvpilot-0.1.0/tests/test_config.py +41 -0
  33. jvpilot-0.1.0/tests/test_decision.py +127 -0
  34. jvpilot-0.1.0/tests/test_interactions_e2e.py +406 -0
  35. jvpilot-0.1.0/tests/test_native_controls_e2e.py +90 -0
  36. jvpilot-0.1.0/tests/test_native_discovery_e2e.py +327 -0
  37. jvpilot-0.1.0/tests/test_packaging_e2e.py +124 -0
  38. jvpilot-0.1.0/tests/test_release_e2e.py +57 -0
  39. jvpilot-0.1.0/tests/test_release_regressions_e2e.py +238 -0
  40. jvpilot-0.1.0/tests/test_reported_regressions_e2e.py +312 -0
  41. jvpilot-0.1.0/tests/test_request_e2e.py +135 -0
  42. jvpilot-0.1.0/tests/test_speech_e2e.py +139 -0
  43. jvpilot-0.1.0/tests/test_streaming_commands_e2e.py +131 -0
  44. jvpilot-0.1.0/tests/test_typing_e2e.py +747 -0
  45. jvpilot-0.1.0/tests/test_web_e2e.py +274 -0
  46. jvpilot-0.1.0/tests/test_workflow_e2e.py +731 -0
jvpilot-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aayan Mapara
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.
jvpilot-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,185 @@
1
+ Metadata-Version: 2.4
2
+ Name: jvpilot
3
+ Version: 0.1.0
4
+ Summary: Control an Apple Silicon Mac with spoken commands.
5
+ Author: Aayan Mapara
6
+ License-Expression: MIT
7
+ Classifier: Environment :: Console
8
+ Classifier: Operating System :: MacOS :: MacOS X
9
+ Classifier: Programming Language :: Python :: 3
10
+ Requires-Python: >=3.12
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: typesafe-sdk==0.7.0
14
+ Requires-Dist: python-dotenv<2.0,>=1.0
15
+ Requires-Dist: pyobjc-framework-Cocoa==12.2.2
16
+ Requires-Dist: pyobjc-framework-ApplicationServices==12.2.2
17
+ Requires-Dist: pyobjc-framework-Quartz==12.2.2
18
+ Requires-Dist: mlx-audio[stt]==0.5.7
19
+ Dynamic: license-file
20
+
21
+ # Jev Pilot
22
+
23
+ Control your Mac with spoken commands. Jev Pilot listens through your microphone, transcribes speech locally, asks Jev to choose a grounded action, and checks the result before continuing.
24
+
25
+ This early macOS command-line release is designed for short, explicit commands. Start listening in Terminal, speak commands in the apps you want to control, and keep talking while the session remains open.
26
+
27
+ ## What you can do
28
+
29
+ - Open or switch to installed apps, including familiar names such as Chrome and VS Code.
30
+ - Visit websites and search Google or YouTube, including in a named browser.
31
+ - Dictate into the focused text field and explicitly ask to press Enter.
32
+ - Create a note in Apple Notes or a document in TextEdit, then dictate its contents.
33
+ - Enter full screen or minimize the current window or a named running app.
34
+ - Run multiple commands in one listening session.
35
+
36
+ Clipboard editing, browser tabs/history/reload/zoom, scrolling, and Finder search are known failures deferred to v0.2. The full list is below. More complex workflows can stop when Jev Pilot cannot find or verify a required control.
37
+
38
+ ## Install
39
+
40
+ You need an Apple Silicon Mac, [uv](https://docs.astral.sh/uv/getting-started/installation/), and a TypeSafe account with a Jev API key. Internet access is needed for Jev decisions and the initial speech-model download. Speech transcription runs locally after the model is cached.
41
+
42
+ After `jvpilot` is published to PyPI, install it with:
43
+
44
+ ```sh
45
+ uv tool install --python 3.12 jvpilot
46
+ uv tool update-shell
47
+ ```
48
+
49
+ Restart Terminal if needed for the `jvpilot` command to appear on your PATH. To install from a source checkout, see [Local development](#local-development).
50
+
51
+ The first listening run downloads the Nemotron speech model automatically if it is not already cached. This can take several minutes. Later runs reuse the cached model.
52
+
53
+ Create the configuration directory:
54
+
55
+ ```sh
56
+ mkdir -p "$HOME/Library/Application Support/Jev Pilot"
57
+ ```
58
+
59
+ Create `~/Library/Application Support/Jev Pilot/.env` with your key:
60
+
61
+ ```text
62
+ TYPESAFE_API_KEY=your_key_here
63
+ ```
64
+
65
+ Keep this file private. You can also set `TYPESAFE_API_KEY` in your shell. The environment variable takes precedence, followed by a source checkout's `.env`, then the user configuration file above. Installed use does not load `.env` files from the current directory or `site-packages`.
66
+
67
+ ## Grant macOS permissions
68
+
69
+ Allow **Microphone** access for the Terminal/Python host macOS identifies when you first listen. If access was denied, check **System Settings → Privacy & Security → Microphone**.
70
+
71
+ Desktop controls also need **Accessibility** access. Request it with:
72
+
73
+ ```sh
74
+ jvpilot --request-accessibility
75
+ ```
76
+
77
+ Enable the identified host under **System Settings → Privacy & Security → Accessibility**. Restart that host if needed, then rerun the command until it reports `accessibility_trusted: true`. Opening or switching apps alone does not require Accessibility; most other controls do.
78
+
79
+ ## Start and stop listening
80
+
81
+ In Terminal, start a session with:
82
+
83
+ ```sh
84
+ jvpilot --listen --auto-approve-basic
85
+ ```
86
+
87
+ This skips routine confidence prompts for app opening/switching, website navigation, and Google/YouTube searches. Other approvals and safety checks stay active. Run `jvpilot --listen` to keep those routine prompts too.
88
+
89
+ To skip routine confidence prompts for other supported commands as well, use:
90
+
91
+ ```sh
92
+ jvpilot --listen --yes
93
+ ```
94
+
95
+ These commands always require approval, even with `--yes`: cutting text, closing a browser tab, pressing Enter outside Notes or TextEdit, and using an unfamiliar control. Clipboard editing and browser tab controls are deferred to v0.2. Existing safety blocks and target/result checks remain active; password, payment, purchase, and deletion requests are blocked.
96
+
97
+ Then:
98
+
99
+ 1. Say a command, such as **“open Safari.”**
100
+ 2. Pause briefly so Jev Pilot can finish the utterance. The default pause is about 650 ms.
101
+ 3. Wait for **“Ready for the next command.”**
102
+ 4. Say another command without restarting the listener.
103
+
104
+ If a `Y / n` prompt appears, enter `y` to approve or `n` to decline. After a command fails or is declined, the listener normally stays active; speak a fresh command after the result. Queued follow-up commands behind a failure are discarded.
105
+
106
+ Stop by saying **“stop listening,” “stop pilot,”** or **“quit pilot.”** You can also press **Control-C** in Terminal. Stopping cancels pending work before its next action; it cannot undo an action already sent. Run the start command again for a new session.
107
+
108
+ ## Example commands
109
+
110
+ | What you want | Say |
111
+ |---|---|
112
+ | Open an installed app | “open Cursor” · “open Notes” · “open Music” |
113
+ | Switch apps | “switch to Chrome” · “switch to Finder” |
114
+ | Visit a website | “go to YouTube” · “go to stripe dot com” |
115
+ | Choose a browser | “open example.com in Chrome” |
116
+ | Search Google | “google best ramen near me” |
117
+ | Search YouTube | “search for Messi soccer videos on YouTube” |
118
+ | Search in a named browser | “search YouTube for lofi hip hop in Safari” |
119
+ | Dictate into the focused field | “type hello world” |
120
+ | Dictate, then press Enter | “type hello world and hit enter” |
121
+ | Create and fill a note | “create a new note in Notes and type hello world” |
122
+ | Create and fill a document | “create a new document in TextEdit and type hello world” |
123
+ | Select text | “select all” |
124
+ | Enter full screen | “maximize this window” · “maximize Safari” |
125
+ | Minimize a window | “minimize this window” · “minimize Safari” |
126
+ | Stop the session | “stop listening” |
127
+
128
+ Text commands need an editable field; select-all also needs existing text. Typing inserts at the caret or replaces selected text. “Maximize” means enter full screen. Use short, explicit requests and name the app for document workflows. If a command is misheard, wait for its result and speak a fresh instruction; completed actions are not automatically undone.
129
+
130
+ ## Deferred to v0.2
131
+
132
+ These controls failed current manual testing and are not supported in v0.1:
133
+
134
+ - Copy and paste; cut and paste; undo and redo.
135
+ - Creating, switching, and closing browser tabs.
136
+ - Browser back/forward history, reload, and zoom.
137
+ - Scrolling and going to the top or bottom.
138
+ - Finder search and Finder scrolling.
139
+
140
+ Experimental handlers may still receive these requests. They need real-app fixes and acceptance testing before they can be advertised as supported. Website navigation and Google/YouTube searches are supported in v0.1.
141
+
142
+ ## Limits and privacy
143
+
144
+ Menus must expose supported English labels. Browser compatibility varies; opening tabs in Helium and other browser-specific controls remain future work. Quitting apps, creating Terminal windows, submitting Terminal commands, and choosing a Music playlist are not supported. Secure, read-only, or unreadable text fields cannot be edited.
145
+
146
+ An `unsupported` result means no suitable action was available. An `unverified` result means an action may have happened, but Jev Pilot could not confirm it. Check the app before repeating a command, especially note creation or typing.
147
+
148
+ Speech transcription runs on your Mac. Jev requests send your spoken goal and bounded app/control metadata to TypeSafe. Existing document values and clipboard contents stay local. Session reports contain transcripts and dictated text; review them before sharing.
149
+
150
+ **Do not speak passwords, API keys, access tokens, or other credentials.** In v0.1, spoken goals are sent to TypeSafe and saved in local session reports; credential detection and redaction are not provided. Finder candidate filenames may be sent to TypeSafe, but their full file paths stay local unless you include a path in your spoken goal.
151
+
152
+ ## Troubleshooting
153
+
154
+ - **Check speech without desktop actions:** run `jvpilot --transcribe-only`, speak a few phrases, then press Control-C. This does not call Jev or require an API key or Accessibility access.
155
+ - **Commands split too early:** increase the pause, for example `jvpilot --listen --auto-approve-basic --silence-ms 900`. Lower it for faster turn-taking.
156
+ - **Quiet speech is missed:** lower `--speech-threshold` slightly from its default `0.012`; raise it if background noise triggers utterances.
157
+ - **Desktop controls are unavailable:** check Accessibility for the same Terminal/Python host used to listen. The app must expose the relevant control or editable field.
158
+ - **An app opens but a workflow stops:** check the result before trying again. Opening, creation, focus, and typing are separate steps.
159
+ - **A v0.2 command fails:** these controls are listed above as known limitations. Check the app before repeating an experimental command that reports `unverified`.
160
+ - **Share a failure:** the latest session report is saved after each command to `~/Library/Application Support/Jev Pilot/Reports/python-cli-last.json`. Use `--report .build/my-session.json` to choose another path. Remove private transcript or dictation content before sharing.
161
+
162
+ ## Local development
163
+
164
+ The active runtime is the Python CLI in `app/`; the retained Swift sources are historical references. From a source checkout, use uv to create a local environment and install the project in editable mode:
165
+
166
+ ```sh
167
+ uv venv --python 3.12
168
+ source .venv/bin/activate
169
+ uv pip install -e .
170
+ jvpilot --listen --auto-approve-basic
171
+ ```
172
+
173
+ Jev Pilot's PyPI package and installed command are both named `jvpilot`; `python -m app` is also available. Package version and build settings live in `pyproject.toml`; dependencies are listed in `requirements.txt`.
174
+
175
+ In each new terminal, run `source .venv/bin/activate` from the checkout before starting the development CLI or running checks. Install the test/build tools once, then run the checks:
176
+
177
+ ```sh
178
+ uv pip install pip 'setuptools>=77' packaging pyyaml
179
+ python -m unittest discover -s tests -p 'test_*.py' -v
180
+ python -m compileall -q app scripts tests
181
+ ```
182
+
183
+ The offline suites use simulated desktop, speech, and provider services. They do not establish real-app compatibility. Opt-in provider checks use `JEV_LIVE_JEV=1`, require your API key, and make real API requests with simulated desktop actions. Native acceptance scripts in `scripts/` require macOS; the TextEdit typing check creates and edits a disposable document. Finder search, scrolling, and browser controls remain deferred regardless of offline test results.
184
+
185
+ See [the architecture guide](docs/ARCHITECTURE.md) for implementation details. Restart the listener after changing code.
@@ -0,0 +1,165 @@
1
+ # Jev Pilot
2
+
3
+ Control your Mac with spoken commands. Jev Pilot listens through your microphone, transcribes speech locally, asks Jev to choose a grounded action, and checks the result before continuing.
4
+
5
+ This early macOS command-line release is designed for short, explicit commands. Start listening in Terminal, speak commands in the apps you want to control, and keep talking while the session remains open.
6
+
7
+ ## What you can do
8
+
9
+ - Open or switch to installed apps, including familiar names such as Chrome and VS Code.
10
+ - Visit websites and search Google or YouTube, including in a named browser.
11
+ - Dictate into the focused text field and explicitly ask to press Enter.
12
+ - Create a note in Apple Notes or a document in TextEdit, then dictate its contents.
13
+ - Enter full screen or minimize the current window or a named running app.
14
+ - Run multiple commands in one listening session.
15
+
16
+ Clipboard editing, browser tabs/history/reload/zoom, scrolling, and Finder search are known failures deferred to v0.2. The full list is below. More complex workflows can stop when Jev Pilot cannot find or verify a required control.
17
+
18
+ ## Install
19
+
20
+ You need an Apple Silicon Mac, [uv](https://docs.astral.sh/uv/getting-started/installation/), and a TypeSafe account with a Jev API key. Internet access is needed for Jev decisions and the initial speech-model download. Speech transcription runs locally after the model is cached.
21
+
22
+ After `jvpilot` is published to PyPI, install it with:
23
+
24
+ ```sh
25
+ uv tool install --python 3.12 jvpilot
26
+ uv tool update-shell
27
+ ```
28
+
29
+ Restart Terminal if needed for the `jvpilot` command to appear on your PATH. To install from a source checkout, see [Local development](#local-development).
30
+
31
+ The first listening run downloads the Nemotron speech model automatically if it is not already cached. This can take several minutes. Later runs reuse the cached model.
32
+
33
+ Create the configuration directory:
34
+
35
+ ```sh
36
+ mkdir -p "$HOME/Library/Application Support/Jev Pilot"
37
+ ```
38
+
39
+ Create `~/Library/Application Support/Jev Pilot/.env` with your key:
40
+
41
+ ```text
42
+ TYPESAFE_API_KEY=your_key_here
43
+ ```
44
+
45
+ Keep this file private. You can also set `TYPESAFE_API_KEY` in your shell. The environment variable takes precedence, followed by a source checkout's `.env`, then the user configuration file above. Installed use does not load `.env` files from the current directory or `site-packages`.
46
+
47
+ ## Grant macOS permissions
48
+
49
+ Allow **Microphone** access for the Terminal/Python host macOS identifies when you first listen. If access was denied, check **System Settings → Privacy & Security → Microphone**.
50
+
51
+ Desktop controls also need **Accessibility** access. Request it with:
52
+
53
+ ```sh
54
+ jvpilot --request-accessibility
55
+ ```
56
+
57
+ Enable the identified host under **System Settings → Privacy & Security → Accessibility**. Restart that host if needed, then rerun the command until it reports `accessibility_trusted: true`. Opening or switching apps alone does not require Accessibility; most other controls do.
58
+
59
+ ## Start and stop listening
60
+
61
+ In Terminal, start a session with:
62
+
63
+ ```sh
64
+ jvpilot --listen --auto-approve-basic
65
+ ```
66
+
67
+ This skips routine confidence prompts for app opening/switching, website navigation, and Google/YouTube searches. Other approvals and safety checks stay active. Run `jvpilot --listen` to keep those routine prompts too.
68
+
69
+ To skip routine confidence prompts for other supported commands as well, use:
70
+
71
+ ```sh
72
+ jvpilot --listen --yes
73
+ ```
74
+
75
+ These commands always require approval, even with `--yes`: cutting text, closing a browser tab, pressing Enter outside Notes or TextEdit, and using an unfamiliar control. Clipboard editing and browser tab controls are deferred to v0.2. Existing safety blocks and target/result checks remain active; password, payment, purchase, and deletion requests are blocked.
76
+
77
+ Then:
78
+
79
+ 1. Say a command, such as **“open Safari.”**
80
+ 2. Pause briefly so Jev Pilot can finish the utterance. The default pause is about 650 ms.
81
+ 3. Wait for **“Ready for the next command.”**
82
+ 4. Say another command without restarting the listener.
83
+
84
+ If a `Y / n` prompt appears, enter `y` to approve or `n` to decline. After a command fails or is declined, the listener normally stays active; speak a fresh command after the result. Queued follow-up commands behind a failure are discarded.
85
+
86
+ Stop by saying **“stop listening,” “stop pilot,”** or **“quit pilot.”** You can also press **Control-C** in Terminal. Stopping cancels pending work before its next action; it cannot undo an action already sent. Run the start command again for a new session.
87
+
88
+ ## Example commands
89
+
90
+ | What you want | Say |
91
+ |---|---|
92
+ | Open an installed app | “open Cursor” · “open Notes” · “open Music” |
93
+ | Switch apps | “switch to Chrome” · “switch to Finder” |
94
+ | Visit a website | “go to YouTube” · “go to stripe dot com” |
95
+ | Choose a browser | “open example.com in Chrome” |
96
+ | Search Google | “google best ramen near me” |
97
+ | Search YouTube | “search for Messi soccer videos on YouTube” |
98
+ | Search in a named browser | “search YouTube for lofi hip hop in Safari” |
99
+ | Dictate into the focused field | “type hello world” |
100
+ | Dictate, then press Enter | “type hello world and hit enter” |
101
+ | Create and fill a note | “create a new note in Notes and type hello world” |
102
+ | Create and fill a document | “create a new document in TextEdit and type hello world” |
103
+ | Select text | “select all” |
104
+ | Enter full screen | “maximize this window” · “maximize Safari” |
105
+ | Minimize a window | “minimize this window” · “minimize Safari” |
106
+ | Stop the session | “stop listening” |
107
+
108
+ Text commands need an editable field; select-all also needs existing text. Typing inserts at the caret or replaces selected text. “Maximize” means enter full screen. Use short, explicit requests and name the app for document workflows. If a command is misheard, wait for its result and speak a fresh instruction; completed actions are not automatically undone.
109
+
110
+ ## Deferred to v0.2
111
+
112
+ These controls failed current manual testing and are not supported in v0.1:
113
+
114
+ - Copy and paste; cut and paste; undo and redo.
115
+ - Creating, switching, and closing browser tabs.
116
+ - Browser back/forward history, reload, and zoom.
117
+ - Scrolling and going to the top or bottom.
118
+ - Finder search and Finder scrolling.
119
+
120
+ Experimental handlers may still receive these requests. They need real-app fixes and acceptance testing before they can be advertised as supported. Website navigation and Google/YouTube searches are supported in v0.1.
121
+
122
+ ## Limits and privacy
123
+
124
+ Menus must expose supported English labels. Browser compatibility varies; opening tabs in Helium and other browser-specific controls remain future work. Quitting apps, creating Terminal windows, submitting Terminal commands, and choosing a Music playlist are not supported. Secure, read-only, or unreadable text fields cannot be edited.
125
+
126
+ An `unsupported` result means no suitable action was available. An `unverified` result means an action may have happened, but Jev Pilot could not confirm it. Check the app before repeating a command, especially note creation or typing.
127
+
128
+ Speech transcription runs on your Mac. Jev requests send your spoken goal and bounded app/control metadata to TypeSafe. Existing document values and clipboard contents stay local. Session reports contain transcripts and dictated text; review them before sharing.
129
+
130
+ **Do not speak passwords, API keys, access tokens, or other credentials.** In v0.1, spoken goals are sent to TypeSafe and saved in local session reports; credential detection and redaction are not provided. Finder candidate filenames may be sent to TypeSafe, but their full file paths stay local unless you include a path in your spoken goal.
131
+
132
+ ## Troubleshooting
133
+
134
+ - **Check speech without desktop actions:** run `jvpilot --transcribe-only`, speak a few phrases, then press Control-C. This does not call Jev or require an API key or Accessibility access.
135
+ - **Commands split too early:** increase the pause, for example `jvpilot --listen --auto-approve-basic --silence-ms 900`. Lower it for faster turn-taking.
136
+ - **Quiet speech is missed:** lower `--speech-threshold` slightly from its default `0.012`; raise it if background noise triggers utterances.
137
+ - **Desktop controls are unavailable:** check Accessibility for the same Terminal/Python host used to listen. The app must expose the relevant control or editable field.
138
+ - **An app opens but a workflow stops:** check the result before trying again. Opening, creation, focus, and typing are separate steps.
139
+ - **A v0.2 command fails:** these controls are listed above as known limitations. Check the app before repeating an experimental command that reports `unverified`.
140
+ - **Share a failure:** the latest session report is saved after each command to `~/Library/Application Support/Jev Pilot/Reports/python-cli-last.json`. Use `--report .build/my-session.json` to choose another path. Remove private transcript or dictation content before sharing.
141
+
142
+ ## Local development
143
+
144
+ The active runtime is the Python CLI in `app/`; the retained Swift sources are historical references. From a source checkout, use uv to create a local environment and install the project in editable mode:
145
+
146
+ ```sh
147
+ uv venv --python 3.12
148
+ source .venv/bin/activate
149
+ uv pip install -e .
150
+ jvpilot --listen --auto-approve-basic
151
+ ```
152
+
153
+ Jev Pilot's PyPI package and installed command are both named `jvpilot`; `python -m app` is also available. Package version and build settings live in `pyproject.toml`; dependencies are listed in `requirements.txt`.
154
+
155
+ In each new terminal, run `source .venv/bin/activate` from the checkout before starting the development CLI or running checks. Install the test/build tools once, then run the checks:
156
+
157
+ ```sh
158
+ uv pip install pip 'setuptools>=77' packaging pyyaml
159
+ python -m unittest discover -s tests -p 'test_*.py' -v
160
+ python -m compileall -q app scripts tests
161
+ ```
162
+
163
+ The offline suites use simulated desktop, speech, and provider services. They do not establish real-app compatibility. Opt-in provider checks use `JEV_LIVE_JEV=1`, require your API key, and make real API requests with simulated desktop actions. Native acceptance scripts in `scripts/` require macOS; the TextEdit typing check creates and edits a disposable document. Finder search, scrolling, and browser controls remain deferred regardless of offline test results.
164
+
165
+ See [the architecture guide](docs/ARCHITECTURE.md) for implementation details. Restart the listener after changing code.
@@ -0,0 +1 @@
1
+ """Python-first Jev Pilot runtime."""
@@ -0,0 +1,5 @@
1
+ """Run the package with `python -m app`."""
2
+
3
+ from .cli import main
4
+
5
+ raise SystemExit(main())
@@ -0,0 +1,55 @@
1
+ """Discover launchable app bundles without traversing their embedded helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+ import plistlib
8
+ import re
9
+
10
+
11
+ APPLICATION_ROOTS = (Path("/Applications"), Path.home() / "Applications", Path("/System/Applications"),
12
+ Path("/System/Library/CoreServices"))
13
+
14
+ # Familiar spoken names supplement actual installed bundle names.
15
+ APP_ALIASES = (
16
+ ("Finder", "com.apple.finder", ("finder",)),
17
+ ("Terminal", "com.apple.Terminal", ("terminal",)),
18
+ ("Visual Studio Code", "com.microsoft.VSCode", ("vs code", "vscode", "visual studio code", "code")),
19
+ ("Safari", "com.apple.Safari", ("safari",)),
20
+ ("Google Chrome", "com.google.Chrome", ("chrome", "google chrome")),
21
+ ("System Settings", "com.apple.systempreferences", ("settings", "system settings")),
22
+ ("Notes", "com.apple.Notes", ("notes",)),
23
+ ("TextEdit", "com.apple.TextEdit", ("textedit", "text edit")),
24
+ )
25
+
26
+
27
+ def discover_applications(roots=APPLICATION_ROOTS) -> tuple:
28
+ """Read app metadata in standard install folders; deduplicate by bundle identity."""
29
+
30
+ apps = {}
31
+ for root in roots:
32
+ for directory, folders, _ in os.walk(root):
33
+ bundles = sorted(name for name in folders if name.casefold().endswith(".app"))
34
+ folders[:] = sorted(name for name in folders if name not in bundles and not name.startswith(".")
35
+ and not name.casefold().endswith((".framework", ".bundle", ".xpc", ".plugin", ".appex", ".prefpane")))
36
+ for name in bundles:
37
+ path = Path(directory) / name
38
+ try:
39
+ with (path / "Contents/Info.plist").open("rb") as handle:
40
+ info = plistlib.load(handle)
41
+ except (OSError, ValueError, plistlib.InvalidFileException):
42
+ continue
43
+ if not isinstance(info, dict) or info.get("LSBackgroundOnly"):
44
+ continue
45
+ identifier = info.get("CFBundleIdentifier")
46
+ display = info.get("CFBundleDisplayName") or info.get("CFBundleName") or path.stem
47
+ if (not isinstance(identifier, str) or not re.fullmatch(r"[a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)+", identifier)
48
+ or len(identifier) > 240 or not isinstance(display, str) or not 1 <= len(display) <= 240):
49
+ continue
50
+ aliases = {display.casefold(), path.stem.casefold()}
51
+ aliases.update(alias for _, bundle, names in APP_ALIASES if bundle == identifier for alias in names)
52
+ if identifier in apps:
53
+ aliases.update(apps[identifier][2])
54
+ apps[identifier] = (display, identifier, tuple(sorted(aliases)))
55
+ return tuple(sorted(apps.values(), key=lambda item: (item[0].casefold(), item[1])))