flutter-dev 0.1.9__tar.gz → 0.1.11__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 (44) hide show
  1. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/.env.example +8 -9
  2. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/CHANGELOG.md +33 -0
  3. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/PKG-INFO +42 -16
  4. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/README.md +41 -15
  5. flutter_dev-0.1.11/core/ai_config.py +297 -0
  6. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/fdev.py +17 -8
  7. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/flutter_dev.egg-info/SOURCES.txt +3 -0
  8. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/gemini_api.py +19 -74
  9. flutter_dev-0.1.11/managers/ai.py +232 -0
  10. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/app.py +42 -25
  11. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/doctor.py +44 -31
  12. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/pyproject.toml +1 -1
  13. flutter_dev-0.1.11/tests/test_ai_config.py +187 -0
  14. flutter_dev-0.1.11/tests/test_app_commands.py +66 -0
  15. flutter_dev-0.1.9/managers/ai.py +0 -113
  16. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/LICENSE +0 -0
  17. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/MANIFEST.in +0 -0
  18. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/common_utils.py +0 -0
  19. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/core/__init__.py +0 -0
  20. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/core/constants.py +0 -0
  21. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/core/state.py +0 -0
  22. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/create_page.py +0 -0
  23. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/git_diff_output_editor.py +0 -0
  24. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/install_legacy.py +0 -0
  25. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/__init__.py +0 -0
  26. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/brew.py +0 -0
  27. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/build.py +0 -0
  28. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/datetime.py +0 -0
  29. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/device.py +0 -0
  30. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/git.py +0 -0
  31. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/git_account.py +0 -0
  32. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/localization.py +0 -0
  33. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/merge.py +0 -0
  34. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/mirror.py +0 -0
  35. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/package.py +0 -0
  36. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/project.py +0 -0
  37. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/managers/web_deploy.py +0 -0
  38. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/requirements.txt +0 -0
  39. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/setup.cfg +0 -0
  40. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/setup.py +0 -0
  41. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/switch_ai.py +0 -0
  42. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/tests/test_fdev_cli.py +0 -0
  43. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/tests/test_localization.py +0 -0
  44. {flutter_dev-0.1.9 → flutter_dev-0.1.11}/tests/test_package_commands.py +0 -0
@@ -1,26 +1,25 @@
1
1
  # AI API Keys Configuration
2
- # সব API keys এখানে secure ভাবে রাখুন
3
- # এই file টি .gitignore এ add করতে হবে
2
+ # API keys secure রাখুন এবং কারও সঙ্গে share করবেন না
4
3
 
5
- # Groq API (Current - Active)
6
- GROQ_API_KEY=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
4
+ # Groq API
5
+ GROQ_API_KEY=
7
6
  GROQ_API_URL=https://api.groq.com/openai/v1/chat/completions
8
7
  GROQ_MODEL=llama-3.3-70b-versatile
9
8
 
10
9
  # Mistral AI API
11
- MISTRAL_API_KEY=irrmymmw43lvCcARy08R1TZhHuqMgXwa
10
+ MISTRAL_API_KEY=
12
11
  MISTRAL_API_URL=https://api.mistral.ai/v1/chat/completions
13
- MISTRAL_MODEL=mistral-medium
12
+ MISTRAL_MODEL=mistral-medium-latest
14
13
 
15
14
  # SambaNova API
16
- SAMBANOVA_API_KEY=c5c3f0dc-75db-4621-9d0c-7372efb4a04f
15
+ SAMBANOVA_API_KEY=
17
16
  SAMBANOVA_API_URL=https://api.sambanova.ai/v1/chat/completions
18
17
  SAMBANOVA_MODEL=Meta-Llama-3.3-70B-Instruct
19
18
 
20
19
  # OpenRouter API
21
- OPENROUTER_API_KEY=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
20
+ OPENROUTER_API_KEY=
22
21
  OPENROUTER_API_URL=https://openrouter.ai/api/v1/chat/completions
23
22
  OPENROUTER_MODEL=openai/gpt-3.5-turbo
24
23
 
25
24
  # Default AI Service (groq, mistral, sambanova, openrouter)
26
- DEFAULT_AI_SERVICE=groq
25
+ DEFAULT_AI_SERVICE=groq
@@ -2,6 +2,39 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 0.1.11 - 2026-07-22
6
+
7
+ ### Added
8
+
9
+ - Added Python regression coverage for project-root and outside-project `fdev clear-data` behavior.
10
+
11
+ ### Changed
12
+
13
+ - Changed `fdev clear-data` to target the current project's Android package without confirmation when run from a Flutter project root, matching `fdev uninstall`; outside a project it still confirms before clearing the foreground app.
14
+ - Updated the PyPI publishing workflow to run on pushes to the `deployment` branch as well as manual dispatches.
15
+
16
+ ## 0.1.10 - 2026-07-16
17
+
18
+ ### Added
19
+
20
+ - Added `fdev init` to create or repair persistent AI configuration with safe provider defaults and empty API-key fields.
21
+ - Added interactive hidden API-key entry and default/custom model selection when AI configuration is incomplete.
22
+ - Added `fdev ai <provider> <model>` for changing a provider's model without replacing its API key.
23
+ - Added Python coverage for configuration precedence, dotenv parsing, atomic updates, cancellation, permissions, platform paths, and doctor status.
24
+
25
+ ### Changed
26
+
27
+ - Unified `fdev ai`, `fdev doctor`, `gemini-api`, and `fdev commit` around the same persistent AI configuration resolver.
28
+ - Updated the default Mistral model to `mistral-medium-latest`.
29
+ - Limited build-output directory creation to commands that use those directories.
30
+
31
+ ### Fixed
32
+
33
+ - Removed credentials from `.env.example` and replaced them with empty API-key values.
34
+ - Made AI configuration writes atomic and restricted the generated file to the current user on Unix-like systems.
35
+ - Cancelled AI setup without saving a partial key or sending a request when interrupted during key/model entry.
36
+ - Prevented exported provider/model overrides from being reported as successfully replaced by file-only changes.
37
+
5
38
  ## 0.1.9 - 2026-07-14
6
39
 
7
40
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flutter-dev
3
- Version: 0.1.9
3
+ Version: 0.1.11
4
4
  Summary: A cross-platform CLI toolkit for Flutter development workflows.
5
5
  Author: Royal Court BD
6
6
  License-Expression: MIT
@@ -42,14 +42,14 @@ The package is published on PyPI as `flutter-dev` and installs console commands
42
42
  python -m pip install flutter-dev
43
43
  ```
44
44
 
45
- Current package metadata declares version `0.1.9`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
45
+ Current package metadata declares version `0.1.11`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
46
46
 
47
47
  ## Key Features
48
48
 
49
49
  - Flutter build automation for APK, split APK, AAB, IPA, release install, and Firebase web deploy.
50
50
  - Project maintenance helpers for `flutter clean`, dependency refresh, `build_runner`, localization generation, analysis, formatting, cache repair, and CocoaPods.
51
51
  - Localization CSV helpers for exporting ARB files to translator-friendly CSV files and generating ARB files back from CSV.
52
- - Android device helpers for APK install, app uninstall, foreground app data clearing, screen mirroring with `scrcpy`, wireless ADB setup, and Date & Time settings.
52
+ - Android device helpers for APK install, app uninstall, project/foreground app data clearing, screen mirroring with `scrcpy`, wireless ADB setup, and Date & Time settings.
53
53
  - AI-assisted Git commits using Groq, Mistral, SambaNova, or OpenRouter-compatible chat completion APIs.
54
54
  - Git workflow helpers for version tags, branch sync, deployment branch merge, discard, and GitHub account management through `gh`.
55
55
  - Feature/page generator that creates a `lib/features/<name>` structure using the project's presenter/base-state pattern.
@@ -137,6 +137,9 @@ fdev upgrade
137
137
  # Check environment and optional tools
138
138
  fdev doctor
139
139
 
140
+ # Initialize persistent AI configuration
141
+ fdev init
142
+
140
143
  # Generate localization files
141
144
  fdev lang
142
145
 
@@ -198,7 +201,7 @@ Android APK/AAB build commands rename output files using the Android app label a
198
201
  | --- | --- |
199
202
  | `fdev install` | Installs a built APK from `build/app/outputs/flutter-apk`, prefers `arm64-v8a`, retries after uninstall on failure, then launches the app. |
200
203
  | `fdev uninstall` | In a Flutter project root, uninstalls by package/bundle id. Outside a project, detects the foreground app and asks for confirmation. |
201
- | `fdev clear-data` | Detects the foreground app and runs `adb shell pm clear <package>` on Android. Confirmation is skipped when it matches the current Flutter project; otherwise the default-Yes safety prompt is shown. iOS prints manual options. |
204
+ | `fdev clear-data` | In a Flutter project root, clears the project's Android package data without confirmation. Outside a project, detects the foreground app and shows a default-Yes safety prompt. iOS prints manual options. |
202
205
  | `fdev install-scrcpy` | Installs `scrcpy` using the best supported package manager for the current platform. |
203
206
  | `fdev mirror` | Launches `scrcpy` for a selected Android device. |
204
207
  | `fdev mirror --wireless` | Guides wireless ADB setup using a USB-connected Android device. |
@@ -239,10 +242,12 @@ Switching GitHub accounts runs `gh auth switch` and `gh auth setup-git`, but it
239
242
  | `fdev` | Shows command help. |
240
243
  | `fdev help` | Shows command help. |
241
244
  | `fdev doctor` | Checks platform, Python, Flutter/Dart/Git, Python packages, Android/iOS tools, optional CLIs, `.env`, Flutter project detection, and global `fdev` availability. |
245
+ | `fdev init` | Creates or repairs the persistent AI `.env` with safe provider defaults and empty API keys. |
242
246
  | `fdev upgrade` | Shows the current version, Python environment, package location, and PyPI source, then upgrades with `python -m pip install --upgrade flutter-dev` and reports the resulting version. |
243
247
  | `fdev delete` | Detects the OS, asks for confirmation, then uninstalls the active Python environment's `flutter-dev` package. Use `fdev delete --yes` to skip confirmation. |
244
248
  | `fdev ai` | Shows the current AI provider and opens an interactive provider switch prompt. |
245
249
  | `fdev ai <provider>` | Switches `DEFAULT_AI_SERVICE` to `groq`, `mistral`, `sambanova`, or `openrouter`. |
250
+ | `fdev ai <provider> <model>` | Switches provider when needed and saves a custom model ID for that provider. |
246
251
  | `fdev brew` | Interactive Homebrew package manager for viewing packages, uninstalling with cleanup, clearing cache, and global cleanup. |
247
252
  | `fdev merge` | Reads paths from `paths.txt`, merges file contents into `path_merge_files.txt`, and opens the result when there are no failures. |
248
253
  | `gemini-api` | Tests the currently selected AI provider configuration. |
@@ -266,7 +271,7 @@ GROQ_MODEL=llama-3.3-70b-versatile
266
271
 
267
272
  MISTRAL_API_KEY=your_mistral_api_key_here
268
273
  MISTRAL_API_URL=https://api.mistral.ai/v1/chat/completions
269
- MISTRAL_MODEL=mistral-medium
274
+ MISTRAL_MODEL=mistral-medium-latest
270
275
 
271
276
  SAMBANOVA_API_KEY=your_sambanova_api_key_here
272
277
  SAMBANOVA_API_URL=https://api.sambanova.ai/v1/chat/completions
@@ -283,26 +288,47 @@ The AI runtime searches for configuration in this order:
283
288
 
284
289
  1. `FLUTTER_DEV_ENV`
285
290
  2. `$XDG_CONFIG_HOME/flutter-dev/.env`
286
- 3. `~/.config/flutter-dev/.env`
287
- 4. `~/scripts/flutter-tools/.env`
288
- 5. `.env` beside the installed module/source
289
- 6. `.env` in the current working directory
291
+ 3. `%APPDATA%/flutter-dev/.env` on Windows, otherwise `~/.config/flutter-dev/.env`
292
+ 4. The legacy `~/.config/flutter-dev/.env` path on Windows
293
+ 5. `~/scripts/flutter-tools/.env`
290
294
 
291
295
  Recommended setup:
292
296
 
293
297
  ```bash
294
- mkdir -p ~/.config/flutter-dev
295
- nano ~/.config/flutter-dev/.env
298
+ fdev init
296
299
  ```
297
300
 
298
- Then test:
301
+ `fdev init` never writes real credentials. It creates all provider URL/model
302
+ defaults with empty API-key fields, preserves existing values, and restricts the
303
+ file to the current user on Unix-like systems. On macOS and Linux the default
304
+ path is `~/.config/flutter-dev/.env`.
305
+
306
+ Then select a provider and test:
299
307
 
300
308
  ```bash
301
- fdev ai
309
+ fdev ai mistral
302
310
  gemini-api
303
311
  ```
304
312
 
305
- Note: the current `fdev doctor` `.env` check is more limited than the AI runtime lookup. If `doctor` reports a missing `.env` but `gemini-api` succeeds, the AI configuration is still usable.
313
+ If the selected provider's key is missing, `fdev commit` or `gemini-api` asks
314
+ for it using hidden terminal input and saves it to the active configuration.
315
+ After a new key is entered, press Enter to use the displayed default model or
316
+ enter another model ID for the same provider. Exported environment variables
317
+ continue to take precedence over values stored in `.env`.
318
+
319
+ Pressing `Ctrl+C` during key/model setup cancels the operation without saving a
320
+ partial key or sending an AI request. If an exported variable conflicts with a
321
+ provider/model change, `fdev` reports the override and asks you to unset it
322
+ instead of claiming that the file change became active.
323
+
324
+ Change the model later without replacing the API key:
325
+
326
+ ```bash
327
+ fdev ai groq llama-3.1-8b-instant
328
+ ```
329
+
330
+ `fdev doctor`, `fdev ai`, `gemini-api`, and `fdev commit` use the same persistent
331
+ configuration lookup.
306
332
 
307
333
  ## Feature/Page Generation
308
334
 
@@ -364,7 +390,7 @@ core/constants.py Shared regex patterns, paths, and build command constant
364
390
  core/state.py Selected Android device state
365
391
  managers/build.py APK/AAB/IPA/release-run build workflow
366
392
  managers/project.py setup, cleanup, lang, db, pod, cache repair
367
- managers/app.py install, uninstall, foreground app data clearing
393
+ managers/app.py install, uninstall, project/foreground app data clearing
368
394
  managers/device.py ADB device detection and selection
369
395
  managers/mirror.py scrcpy install, mirror, wireless ADB setup
370
396
  managers/git.py commit, tag, discard, branch sync, deployment merge
@@ -387,7 +413,7 @@ The installed Python layout is top-level modules plus the `core` and `managers`
387
413
  - `fdev tag` updates `pubspec.yaml`, creates a commit, creates a Git tag, and pushes both.
388
414
  - `fdev discard` removes tracked changes and untracked files after confirmation.
389
415
  - `fdev cleanup` applies `dart fix`, formats the project, and runs a major-version dependency upgrade.
390
- - `fdev clear-data` skips confirmation only when the foreground Android app matches the current Flutter project's package ID; otherwise it uses a default-Yes `[Y/n]` confirmation.
416
+ - `fdev clear-data` targets the current project's Android package without confirmation when run from a Flutter project root. Outside a project, it targets the foreground app after a default-Yes `[Y/n]` confirmation.
391
417
  - `fdev uninstall` may fall back to foreground app detection outside a Flutter project root.
392
418
  - `fdev upgrade` and `fdev delete` manage the local `flutter-dev` Python package, not the Flutter app on a device.
393
419
  - `fdev upgrade` reports the installed version and location before upgrading, then shows the before→after version when the active process can verify it immediately.
@@ -8,14 +8,14 @@ The package is published on PyPI as `flutter-dev` and installs console commands
8
8
  python -m pip install flutter-dev
9
9
  ```
10
10
 
11
- Current package metadata declares version `0.1.9`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
11
+ Current package metadata declares version `0.1.11`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
12
12
 
13
13
  ## Key Features
14
14
 
15
15
  - Flutter build automation for APK, split APK, AAB, IPA, release install, and Firebase web deploy.
16
16
  - Project maintenance helpers for `flutter clean`, dependency refresh, `build_runner`, localization generation, analysis, formatting, cache repair, and CocoaPods.
17
17
  - Localization CSV helpers for exporting ARB files to translator-friendly CSV files and generating ARB files back from CSV.
18
- - Android device helpers for APK install, app uninstall, foreground app data clearing, screen mirroring with `scrcpy`, wireless ADB setup, and Date & Time settings.
18
+ - Android device helpers for APK install, app uninstall, project/foreground app data clearing, screen mirroring with `scrcpy`, wireless ADB setup, and Date & Time settings.
19
19
  - AI-assisted Git commits using Groq, Mistral, SambaNova, or OpenRouter-compatible chat completion APIs.
20
20
  - Git workflow helpers for version tags, branch sync, deployment branch merge, discard, and GitHub account management through `gh`.
21
21
  - Feature/page generator that creates a `lib/features/<name>` structure using the project's presenter/base-state pattern.
@@ -103,6 +103,9 @@ fdev upgrade
103
103
  # Check environment and optional tools
104
104
  fdev doctor
105
105
 
106
+ # Initialize persistent AI configuration
107
+ fdev init
108
+
106
109
  # Generate localization files
107
110
  fdev lang
108
111
 
@@ -164,7 +167,7 @@ Android APK/AAB build commands rename output files using the Android app label a
164
167
  | --- | --- |
165
168
  | `fdev install` | Installs a built APK from `build/app/outputs/flutter-apk`, prefers `arm64-v8a`, retries after uninstall on failure, then launches the app. |
166
169
  | `fdev uninstall` | In a Flutter project root, uninstalls by package/bundle id. Outside a project, detects the foreground app and asks for confirmation. |
167
- | `fdev clear-data` | Detects the foreground app and runs `adb shell pm clear <package>` on Android. Confirmation is skipped when it matches the current Flutter project; otherwise the default-Yes safety prompt is shown. iOS prints manual options. |
170
+ | `fdev clear-data` | In a Flutter project root, clears the project's Android package data without confirmation. Outside a project, detects the foreground app and shows a default-Yes safety prompt. iOS prints manual options. |
168
171
  | `fdev install-scrcpy` | Installs `scrcpy` using the best supported package manager for the current platform. |
169
172
  | `fdev mirror` | Launches `scrcpy` for a selected Android device. |
170
173
  | `fdev mirror --wireless` | Guides wireless ADB setup using a USB-connected Android device. |
@@ -205,10 +208,12 @@ Switching GitHub accounts runs `gh auth switch` and `gh auth setup-git`, but it
205
208
  | `fdev` | Shows command help. |
206
209
  | `fdev help` | Shows command help. |
207
210
  | `fdev doctor` | Checks platform, Python, Flutter/Dart/Git, Python packages, Android/iOS tools, optional CLIs, `.env`, Flutter project detection, and global `fdev` availability. |
211
+ | `fdev init` | Creates or repairs the persistent AI `.env` with safe provider defaults and empty API keys. |
208
212
  | `fdev upgrade` | Shows the current version, Python environment, package location, and PyPI source, then upgrades with `python -m pip install --upgrade flutter-dev` and reports the resulting version. |
209
213
  | `fdev delete` | Detects the OS, asks for confirmation, then uninstalls the active Python environment's `flutter-dev` package. Use `fdev delete --yes` to skip confirmation. |
210
214
  | `fdev ai` | Shows the current AI provider and opens an interactive provider switch prompt. |
211
215
  | `fdev ai <provider>` | Switches `DEFAULT_AI_SERVICE` to `groq`, `mistral`, `sambanova`, or `openrouter`. |
216
+ | `fdev ai <provider> <model>` | Switches provider when needed and saves a custom model ID for that provider. |
212
217
  | `fdev brew` | Interactive Homebrew package manager for viewing packages, uninstalling with cleanup, clearing cache, and global cleanup. |
213
218
  | `fdev merge` | Reads paths from `paths.txt`, merges file contents into `path_merge_files.txt`, and opens the result when there are no failures. |
214
219
  | `gemini-api` | Tests the currently selected AI provider configuration. |
@@ -232,7 +237,7 @@ GROQ_MODEL=llama-3.3-70b-versatile
232
237
 
233
238
  MISTRAL_API_KEY=your_mistral_api_key_here
234
239
  MISTRAL_API_URL=https://api.mistral.ai/v1/chat/completions
235
- MISTRAL_MODEL=mistral-medium
240
+ MISTRAL_MODEL=mistral-medium-latest
236
241
 
237
242
  SAMBANOVA_API_KEY=your_sambanova_api_key_here
238
243
  SAMBANOVA_API_URL=https://api.sambanova.ai/v1/chat/completions
@@ -249,26 +254,47 @@ The AI runtime searches for configuration in this order:
249
254
 
250
255
  1. `FLUTTER_DEV_ENV`
251
256
  2. `$XDG_CONFIG_HOME/flutter-dev/.env`
252
- 3. `~/.config/flutter-dev/.env`
253
- 4. `~/scripts/flutter-tools/.env`
254
- 5. `.env` beside the installed module/source
255
- 6. `.env` in the current working directory
257
+ 3. `%APPDATA%/flutter-dev/.env` on Windows, otherwise `~/.config/flutter-dev/.env`
258
+ 4. The legacy `~/.config/flutter-dev/.env` path on Windows
259
+ 5. `~/scripts/flutter-tools/.env`
256
260
 
257
261
  Recommended setup:
258
262
 
259
263
  ```bash
260
- mkdir -p ~/.config/flutter-dev
261
- nano ~/.config/flutter-dev/.env
264
+ fdev init
262
265
  ```
263
266
 
264
- Then test:
267
+ `fdev init` never writes real credentials. It creates all provider URL/model
268
+ defaults with empty API-key fields, preserves existing values, and restricts the
269
+ file to the current user on Unix-like systems. On macOS and Linux the default
270
+ path is `~/.config/flutter-dev/.env`.
271
+
272
+ Then select a provider and test:
265
273
 
266
274
  ```bash
267
- fdev ai
275
+ fdev ai mistral
268
276
  gemini-api
269
277
  ```
270
278
 
271
- Note: the current `fdev doctor` `.env` check is more limited than the AI runtime lookup. If `doctor` reports a missing `.env` but `gemini-api` succeeds, the AI configuration is still usable.
279
+ If the selected provider's key is missing, `fdev commit` or `gemini-api` asks
280
+ for it using hidden terminal input and saves it to the active configuration.
281
+ After a new key is entered, press Enter to use the displayed default model or
282
+ enter another model ID for the same provider. Exported environment variables
283
+ continue to take precedence over values stored in `.env`.
284
+
285
+ Pressing `Ctrl+C` during key/model setup cancels the operation without saving a
286
+ partial key or sending an AI request. If an exported variable conflicts with a
287
+ provider/model change, `fdev` reports the override and asks you to unset it
288
+ instead of claiming that the file change became active.
289
+
290
+ Change the model later without replacing the API key:
291
+
292
+ ```bash
293
+ fdev ai groq llama-3.1-8b-instant
294
+ ```
295
+
296
+ `fdev doctor`, `fdev ai`, `gemini-api`, and `fdev commit` use the same persistent
297
+ configuration lookup.
272
298
 
273
299
  ## Feature/Page Generation
274
300
 
@@ -330,7 +356,7 @@ core/constants.py Shared regex patterns, paths, and build command constant
330
356
  core/state.py Selected Android device state
331
357
  managers/build.py APK/AAB/IPA/release-run build workflow
332
358
  managers/project.py setup, cleanup, lang, db, pod, cache repair
333
- managers/app.py install, uninstall, foreground app data clearing
359
+ managers/app.py install, uninstall, project/foreground app data clearing
334
360
  managers/device.py ADB device detection and selection
335
361
  managers/mirror.py scrcpy install, mirror, wireless ADB setup
336
362
  managers/git.py commit, tag, discard, branch sync, deployment merge
@@ -353,7 +379,7 @@ The installed Python layout is top-level modules plus the `core` and `managers`
353
379
  - `fdev tag` updates `pubspec.yaml`, creates a commit, creates a Git tag, and pushes both.
354
380
  - `fdev discard` removes tracked changes and untracked files after confirmation.
355
381
  - `fdev cleanup` applies `dart fix`, formats the project, and runs a major-version dependency upgrade.
356
- - `fdev clear-data` skips confirmation only when the foreground Android app matches the current Flutter project's package ID; otherwise it uses a default-Yes `[Y/n]` confirmation.
382
+ - `fdev clear-data` targets the current project's Android package without confirmation when run from a Flutter project root. Outside a project, it targets the foreground app after a default-Yes `[Y/n]` confirmation.
357
383
  - `fdev uninstall` may fall back to foreground app detection outside a Flutter project root.
358
384
  - `fdev upgrade` and `fdev delete` manage the local `flutter-dev` Python package, not the Flutter app on a device.
359
385
  - `fdev upgrade` reports the installed version and location before upgrading, then shows the before→after version when the active process can verify it immediately.
@@ -0,0 +1,297 @@
1
+ """Shared AI configuration paths, defaults, and persistence helpers."""
2
+
3
+ import os
4
+ import platform
5
+ import re
6
+ import tempfile
7
+ from pathlib import Path
8
+
9
+ from dotenv import dotenv_values
10
+
11
+
12
+ VALID_AI_SERVICES = ("groq", "mistral", "sambanova", "openrouter")
13
+
14
+ AI_SERVICE_DEFAULTS = {
15
+ "groq": {
16
+ "api_key_env": "GROQ_API_KEY",
17
+ "api_url_env": "GROQ_API_URL",
18
+ "model_env": "GROQ_MODEL",
19
+ "api_url": "https://api.groq.com/openai/v1/chat/completions",
20
+ "model": "llama-3.3-70b-versatile",
21
+ },
22
+ "mistral": {
23
+ "api_key_env": "MISTRAL_API_KEY",
24
+ "api_url_env": "MISTRAL_API_URL",
25
+ "model_env": "MISTRAL_MODEL",
26
+ "api_url": "https://api.mistral.ai/v1/chat/completions",
27
+ "model": "mistral-medium-latest",
28
+ },
29
+ "sambanova": {
30
+ "api_key_env": "SAMBANOVA_API_KEY",
31
+ "api_url_env": "SAMBANOVA_API_URL",
32
+ "model_env": "SAMBANOVA_MODEL",
33
+ "api_url": "https://api.sambanova.ai/v1/chat/completions",
34
+ "model": "Meta-Llama-3.3-70B-Instruct",
35
+ },
36
+ "openrouter": {
37
+ "api_key_env": "OPENROUTER_API_KEY",
38
+ "api_url_env": "OPENROUTER_API_URL",
39
+ "model_env": "OPENROUTER_MODEL",
40
+ "api_url": "https://openrouter.ai/api/v1/chat/completions",
41
+ "model": "openai/gpt-3.5-turbo",
42
+ },
43
+ }
44
+
45
+
46
+ def get_preferred_env_path():
47
+ """Return the user config path where fdev should create its .env file."""
48
+ explicit_path = os.getenv("FLUTTER_DEV_ENV")
49
+ if explicit_path:
50
+ return Path(explicit_path).expanduser()
51
+
52
+ xdg_config_home = os.getenv("XDG_CONFIG_HOME")
53
+ if xdg_config_home:
54
+ return Path(xdg_config_home).expanduser() / "flutter-dev" / ".env"
55
+
56
+ if platform.system() == "Windows" and os.getenv("APPDATA"):
57
+ return Path(os.environ["APPDATA"]).expanduser() / "flutter-dev" / ".env"
58
+
59
+ # Keep the established path on macOS and Linux for backward compatibility.
60
+ return Path.home() / ".config" / "flutter-dev" / ".env"
61
+
62
+
63
+ def get_persistent_env_candidates():
64
+ """Return user-owned config candidates without project-local fallbacks."""
65
+ preferred_path = get_preferred_env_path()
66
+ candidates = [
67
+ preferred_path,
68
+ Path.home() / ".config" / "flutter-dev" / ".env",
69
+ Path.home() / "scripts" / "flutter-tools" / ".env",
70
+ ]
71
+ unique_candidates = []
72
+ seen = set()
73
+ for candidate in candidates:
74
+ normalized = str(candidate.expanduser())
75
+ if normalized not in seen:
76
+ seen.add(normalized)
77
+ unique_candidates.append(candidate.expanduser())
78
+ return unique_candidates
79
+
80
+
81
+ def find_persistent_env_file():
82
+ """Return an existing user-owned config file, or None."""
83
+ # Explicit path controls must not silently fall back to another file.
84
+ if os.getenv("FLUTTER_DEV_ENV") or os.getenv("XDG_CONFIG_HOME"):
85
+ preferred_path = get_preferred_env_path()
86
+ return preferred_path if preferred_path.is_file() else None
87
+
88
+ for candidate in get_persistent_env_candidates():
89
+ if candidate.is_file():
90
+ return candidate
91
+ return None
92
+
93
+
94
+ def get_active_env_path():
95
+ """Return the existing runtime config, or the preferred creation path."""
96
+ return find_persistent_env_file() or get_preferred_env_path()
97
+
98
+
99
+ def build_env_template(default_service="groq"):
100
+ """Build a safe config template containing no API credentials."""
101
+ return f"""# AI API Keys Configuration
102
+ # API keys secure রাখুন এবং কারও সঙ্গে share করবেন না
103
+
104
+ # Groq API
105
+ GROQ_API_KEY=
106
+ GROQ_API_URL={AI_SERVICE_DEFAULTS['groq']['api_url']}
107
+ GROQ_MODEL={AI_SERVICE_DEFAULTS['groq']['model']}
108
+
109
+ # Mistral AI API
110
+ MISTRAL_API_KEY=
111
+ MISTRAL_API_URL={AI_SERVICE_DEFAULTS['mistral']['api_url']}
112
+ MISTRAL_MODEL={AI_SERVICE_DEFAULTS['mistral']['model']}
113
+
114
+ # SambaNova API
115
+ SAMBANOVA_API_KEY=
116
+ SAMBANOVA_API_URL={AI_SERVICE_DEFAULTS['sambanova']['api_url']}
117
+ SAMBANOVA_MODEL={AI_SERVICE_DEFAULTS['sambanova']['model']}
118
+
119
+ # OpenRouter API
120
+ OPENROUTER_API_KEY=
121
+ OPENROUTER_API_URL={AI_SERVICE_DEFAULTS['openrouter']['api_url']}
122
+ OPENROUTER_MODEL={AI_SERVICE_DEFAULTS['openrouter']['model']}
123
+
124
+ # Default AI Service (groq, mistral, sambanova, openrouter)
125
+ DEFAULT_AI_SERVICE={default_service}
126
+ """
127
+
128
+
129
+ def read_env_file(env_path):
130
+ """Read an env file with the same parser used by the AI runtime."""
131
+ env_path = Path(env_path)
132
+ if not env_path.is_file():
133
+ return {}
134
+
135
+ parsed = dotenv_values(env_path, interpolate=False, encoding="utf-8")
136
+ return {key: value or "" for key, value in parsed.items()}
137
+
138
+
139
+ def resolve_ai_config(file_values, environ=None):
140
+ """Resolve one serializable AI configuration from file and process data."""
141
+ process_values = os.environ if environ is None else environ
142
+
143
+ def effective_value(key, fallback=""):
144
+ if key in process_values and process_values[key]:
145
+ return process_values[key]
146
+ return file_values.get(key, fallback)
147
+
148
+ providers = {}
149
+ relevant_environment_keys = {"DEFAULT_AI_SERVICE"}
150
+ for service in VALID_AI_SERVICES:
151
+ defaults = AI_SERVICE_DEFAULTS[service]
152
+ relevant_environment_keys.update(
153
+ {
154
+ defaults["api_key_env"],
155
+ defaults["api_url_env"],
156
+ defaults["model_env"],
157
+ }
158
+ )
159
+ providers[service] = {
160
+ "api_key": effective_value(defaults["api_key_env"]),
161
+ "api_url": effective_value(
162
+ defaults["api_url_env"], defaults["api_url"]
163
+ ),
164
+ "model": effective_value(defaults["model_env"], defaults["model"]),
165
+ }
166
+
167
+ service = effective_value("DEFAULT_AI_SERVICE", "groq").strip().lower()
168
+ return {
169
+ "service": service,
170
+ "providers": providers,
171
+ "environment_overrides": sorted(
172
+ key
173
+ for key in relevant_environment_keys
174
+ if process_values.get(key)
175
+ ),
176
+ }
177
+
178
+
179
+ def load_resolved_ai_config(env_path, environ=None):
180
+ """Load and resolve configuration while retaining its source path."""
181
+ resolved = resolve_ai_config(read_env_file(env_path), environ=environ)
182
+ resolved["env_path"] = str(Path(env_path))
183
+ return resolved
184
+
185
+
186
+ def update_env_file_value(env_path, key, value):
187
+ """Update one env value atomically."""
188
+ update_env_file_values(env_path, {key: value})
189
+
190
+
191
+ def update_env_file_values(env_path, updates):
192
+ """Update env values in one atomic write, removing duplicate target keys."""
193
+ normalized_updates = {}
194
+ for key, value in updates.items():
195
+ value = str(value)
196
+ if "\n" in value or "\r" in value:
197
+ raise ValueError(f"Invalid newline in {key}")
198
+ normalized_updates[key] = value
199
+
200
+ env_path = Path(env_path)
201
+ env_path.parent.mkdir(parents=True, exist_ok=True)
202
+ content = env_path.read_text(encoding="utf-8") if env_path.exists() else ""
203
+ lines = content.splitlines()
204
+ output_lines = []
205
+ written_keys = set()
206
+ assignment_pattern = re.compile(
207
+ r"^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*="
208
+ )
209
+
210
+ for line in lines:
211
+ match = assignment_pattern.match(line)
212
+ key = match.group(1) if match else None
213
+ if key not in normalized_updates:
214
+ output_lines.append(line)
215
+ continue
216
+ if key in written_keys:
217
+ continue
218
+ output_lines.append(f"{key}={normalized_updates[key]}")
219
+ written_keys.add(key)
220
+
221
+ missing_keys = [
222
+ key for key in normalized_updates if key not in written_keys
223
+ ]
224
+ if missing_keys and output_lines and output_lines[-1].strip():
225
+ output_lines.append("")
226
+ output_lines.extend(
227
+ f"{key}={normalized_updates[key]}" for key in missing_keys
228
+ )
229
+
230
+ _atomic_write_env(env_path, "\n".join(output_lines) + "\n")
231
+
232
+
233
+ def ensure_env_file(env_path=None):
234
+ """Create the config and add any missing default fields without overwriting."""
235
+ env_path = Path(env_path or get_preferred_env_path()).expanduser()
236
+ env_path.parent.mkdir(parents=True, exist_ok=True)
237
+ created = not env_path.exists()
238
+
239
+ if created:
240
+ _atomic_write_env(env_path, build_env_template())
241
+ else:
242
+ existing = read_env_file(env_path)
243
+ missing_values = {}
244
+ for service in VALID_AI_SERVICES:
245
+ defaults = AI_SERVICE_DEFAULTS[service]
246
+ missing_defaults = {
247
+ defaults["api_key_env"]: "",
248
+ defaults["api_url_env"]: defaults["api_url"],
249
+ defaults["model_env"]: defaults["model"],
250
+ }
251
+ for key, value in missing_defaults.items():
252
+ if key not in existing or (value and not existing[key]):
253
+ missing_values[key] = value
254
+ if not existing.get("DEFAULT_AI_SERVICE"):
255
+ missing_values["DEFAULT_AI_SERVICE"] = "groq"
256
+ if missing_values:
257
+ update_env_file_values(env_path, missing_values)
258
+
259
+ _protect_env_file(env_path)
260
+ return env_path, created
261
+
262
+
263
+ def _protect_env_file(env_path):
264
+ """Restrict config permissions to the current user on Unix-like systems."""
265
+ if os.name != "nt":
266
+ try:
267
+ Path(env_path).chmod(0o600)
268
+ except OSError:
269
+ pass
270
+
271
+
272
+ def _atomic_write_env(env_path, content):
273
+ """Replace an env file atomically with owner-only Unix permissions."""
274
+ env_path = Path(env_path)
275
+ env_path.parent.mkdir(parents=True, exist_ok=True)
276
+ temp_path = None
277
+ try:
278
+ with tempfile.NamedTemporaryFile(
279
+ mode="w",
280
+ encoding="utf-8",
281
+ dir=env_path.parent,
282
+ prefix=f".{env_path.name}.",
283
+ delete=False,
284
+ ) as temp_file:
285
+ temp_path = Path(temp_file.name)
286
+ temp_file.write(content)
287
+ temp_file.flush()
288
+ os.fsync(temp_file.fileno())
289
+ _protect_env_file(temp_path)
290
+ os.replace(temp_path, env_path)
291
+ _protect_env_file(env_path)
292
+ finally:
293
+ if temp_path and temp_path.exists():
294
+ try:
295
+ temp_path.unlink()
296
+ except OSError:
297
+ pass