flutter-dev 0.1.5__tar.gz → 0.1.6__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 (42) hide show
  1. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/CHANGELOG.md +15 -0
  2. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/PKG-INFO +24 -5
  3. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/README.md +23 -4
  4. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/fdev.py +14 -0
  5. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/flutter_dev.egg-info/SOURCES.txt +3 -1
  6. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/gui.py +8 -3
  7. flutter_dev-0.1.6/managers/package.py +148 -0
  8. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/pyproject.toml +1 -1
  9. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/tests/test_fdev_cli.py +22 -0
  10. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/tests/test_gui.py +38 -0
  11. flutter_dev-0.1.6/tests/test_package_commands.py +129 -0
  12. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/.env.example +0 -0
  13. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/LICENSE +0 -0
  14. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/MANIFEST.in +0 -0
  15. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/common_utils.py +0 -0
  16. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/core/__init__.py +0 -0
  17. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/core/constants.py +0 -0
  18. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/core/state.py +0 -0
  19. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/create_page.py +0 -0
  20. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/gemini_api.py +0 -0
  21. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/git_diff_output_editor.py +0 -0
  22. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/install_legacy.py +0 -0
  23. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/__init__.py +0 -0
  24. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/ai.py +0 -0
  25. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/app.py +0 -0
  26. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/brew.py +0 -0
  27. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/build.py +0 -0
  28. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/datetime.py +0 -0
  29. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/device.py +0 -0
  30. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/doctor.py +0 -0
  31. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/git.py +0 -0
  32. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/git_account.py +0 -0
  33. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/localization.py +0 -0
  34. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/merge.py +0 -0
  35. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/mirror.py +0 -0
  36. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/project.py +0 -0
  37. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/web_deploy.py +0 -0
  38. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/requirements.txt +0 -0
  39. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/setup.cfg +0 -0
  40. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/setup.py +0 -0
  41. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/switch_ai.py +0 -0
  42. {flutter_dev-0.1.5 → flutter_dev-0.1.6}/tests/test_localization.py +0 -0
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to this project are documented in this file.
4
4
 
5
+ ## 0.1.6 - 2026-06-25
6
+
7
+ ### Added
8
+
9
+ - Added `fdev upgrade` to update the local `flutter-dev` package from PyPI after detecting the current OS.
10
+ - Added `fdev delete` to uninstall the local `flutter-dev` package after detecting the current OS and confirming the destructive action.
11
+
12
+ ### Changed
13
+
14
+ - `fdev upgrade` and `fdev delete` now print the exact Python executable they target before running `sys.executable -m pip`.
15
+
16
+ ### Fixed
17
+
18
+ - Reworked the Tkinter dashboard layout to avoid platform-sensitive paned-window sizing that could open a blank `fdev` popup after installing from PyPI.
19
+
5
20
  ## 0.1.5 - 2026-06-24
6
21
 
7
22
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flutter-dev
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: A cross-platform CLI toolkit for Flutter development workflows.
5
5
  Author: Royal Court BD
6
6
  License-Expression: MIT
@@ -39,10 +39,10 @@ Dynamic: license-file
39
39
  The package is published on PyPI as `flutter-dev` and installs console commands for day-to-day Flutter work:
40
40
 
41
41
  ```bash
42
- pip install flutter-dev
42
+ python -m pip install flutter-dev
43
43
  ```
44
44
 
45
- Current package metadata declares version `0.1.5`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
45
+ Current package metadata declares version `0.1.6`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
46
46
 
47
47
  ## Key Features
48
48
 
@@ -55,7 +55,7 @@ Current package metadata declares version `0.1.5`, Python `>=3.8`, MIT license,
55
55
  - Feature/page generator that creates a `lib/features/<name>` structure using the project's presenter/base-state pattern.
56
56
  - Small Tkinter desktop dashboard opened by running `fdev` with no command, with live colored command logs.
57
57
  - Environment diagnostics through `fdev doctor`.
58
- - Small utilities for Homebrew cleanup, file merging from `paths.txt`, AI provider switching, and git diff prompt export.
58
+ - Small utilities for Homebrew cleanup, file merging from `paths.txt`, AI provider switching, PyPI self-upgrade/delete, and git diff prompt export.
59
59
 
60
60
  ## Installation
61
61
 
@@ -71,7 +71,7 @@ Verify the CLI is available:
71
71
  fdev doctor
72
72
  ```
73
73
 
74
- `pip install flutter-dev` installs the Python package, Python dependencies, and console scripts. It does not install Flutter, Dart, Git, Android SDK tools, CocoaPods, Firebase CLI, GitHub CLI, `scrcpy`, Java/JDK, or VS Code CLI.
74
+ `python -m pip install flutter-dev` installs the Python package, Python dependencies, and console scripts into the Python environment selected by `python`. It does not install Flutter, Dart, Git, Android SDK tools, CocoaPods, Firebase CLI, GitHub CLI, `scrcpy`, Java/JDK, or VS Code CLI.
75
75
 
76
76
  The desktop dashboard uses Python Tkinter. Some Python distributions install this separately; for Homebrew Python, install the matching package such as `brew install python-tk@3.14`.
77
77
 
@@ -135,6 +135,9 @@ fdev
135
135
  # Show command help
136
136
  fdev help
137
137
 
138
+ # Upgrade the fdev CLI from PyPI
139
+ fdev upgrade
140
+
138
141
  # Check environment and optional tools
139
142
  fdev doctor
140
143
 
@@ -239,6 +242,8 @@ Switching GitHub accounts runs `gh auth switch` and `gh auth setup-git`, but it
239
242
  | `fdev` | Opens a small Tkinter desktop dashboard for running `fdev` commands with live colored logs and command input. The terminal process stays running while the window is open. |
240
243
  | `fdev help` | Shows command help without opening the dashboard. |
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 upgrade` | Detects the OS, then upgrades the active Python environment's `flutter-dev` package from PyPI with `python -m pip install --upgrade flutter-dev`. |
246
+ | `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. |
242
247
  | `fdev ai` | Shows the current AI provider and opens an interactive provider switch prompt. |
243
248
  | `fdev ai <provider>` | Switches `DEFAULT_AI_SERVICE` to `groq`, `mistral`, `sambanova`, or `openrouter`. |
244
249
  | `fdev brew` | Interactive Homebrew package manager for viewing packages, uninstalling with cleanup, clearing cache, and global cleanup. |
@@ -368,6 +373,7 @@ managers/mirror.py scrcpy install, mirror, wireless ADB setup
368
373
  managers/git.py commit, tag, discard, branch sync, deployment merge
369
374
  managers/git_account.py GitHub account manager around gh CLI
370
375
  managers/doctor.py Environment diagnostics
376
+ managers/package.py PyPI self-upgrade and local uninstall commands
371
377
  managers/ai.py AI provider status and switching
372
378
  managers/brew.py Interactive Homebrew manager
373
379
  managers/merge.py paths.txt to path_merge_files.txt merger
@@ -386,6 +392,8 @@ The installed Python layout is top-level modules plus the `core` and `managers`
386
392
  - `fdev cleanup` applies `dart fix`, formats the project, and runs a major-version dependency upgrade.
387
393
  - `fdev clear-data` clears app data on Android after confirmation.
388
394
  - `fdev uninstall` may fall back to foreground app detection outside a Flutter project root.
395
+ - `fdev upgrade` and `fdev delete` manage the local `flutter-dev` Python package, not the Flutter app on a device.
396
+ - `fdev delete` removes the `fdev` command from the current Python environment after confirmation; reinstall with `python -m pip install flutter-dev`.
389
397
  - `fdev` with no command opens the desktop dashboard. Use `fdev help` to print command usage.
390
398
  - Most command subcommands create build output directories at startup in the current working directory.
391
399
  - Some subcommands return failure internally without always mapping that failure to a non-zero process exit code, so automation should inspect command output carefully.
@@ -414,6 +422,17 @@ On Windows, use:
414
422
  where fdev
415
423
  ```
416
424
 
425
+ ### Upgrade Or Remove fdev
426
+
427
+ Use the built-in package management commands from the Python environment where `fdev` is installed:
428
+
429
+ ```bash
430
+ fdev upgrade
431
+ fdev delete
432
+ ```
433
+
434
+ Both commands print the detected OS and exact Python executable before running. They use that executable with `-m pip`, not a hardcoded `python`, `python3`, `pip`, or `pip3`, so the active venv/global Python that launched `fdev` is the environment that gets upgraded or deleted. On Windows, `fdev delete` and `fdev upgrade` schedule the same `sys.executable -m pip ...` operation in a delayed command process so the currently running console launcher can exit first.
435
+
417
436
  ### Flutter Project Not Detected
418
437
 
419
438
  Run project-specific commands from the directory that contains `pubspec.yaml`.
@@ -5,10 +5,10 @@
5
5
  The package is published on PyPI as `flutter-dev` and installs console commands for day-to-day Flutter work:
6
6
 
7
7
  ```bash
8
- pip install flutter-dev
8
+ python -m pip install flutter-dev
9
9
  ```
10
10
 
11
- Current package metadata declares version `0.1.5`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
11
+ Current package metadata declares version `0.1.6`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
12
12
 
13
13
  ## Key Features
14
14
 
@@ -21,7 +21,7 @@ Current package metadata declares version `0.1.5`, Python `>=3.8`, MIT license,
21
21
  - Feature/page generator that creates a `lib/features/<name>` structure using the project's presenter/base-state pattern.
22
22
  - Small Tkinter desktop dashboard opened by running `fdev` with no command, with live colored command logs.
23
23
  - Environment diagnostics through `fdev doctor`.
24
- - Small utilities for Homebrew cleanup, file merging from `paths.txt`, AI provider switching, and git diff prompt export.
24
+ - Small utilities for Homebrew cleanup, file merging from `paths.txt`, AI provider switching, PyPI self-upgrade/delete, and git diff prompt export.
25
25
 
26
26
  ## Installation
27
27
 
@@ -37,7 +37,7 @@ Verify the CLI is available:
37
37
  fdev doctor
38
38
  ```
39
39
 
40
- `pip install flutter-dev` installs the Python package, Python dependencies, and console scripts. It does not install Flutter, Dart, Git, Android SDK tools, CocoaPods, Firebase CLI, GitHub CLI, `scrcpy`, Java/JDK, or VS Code CLI.
40
+ `python -m pip install flutter-dev` installs the Python package, Python dependencies, and console scripts into the Python environment selected by `python`. It does not install Flutter, Dart, Git, Android SDK tools, CocoaPods, Firebase CLI, GitHub CLI, `scrcpy`, Java/JDK, or VS Code CLI.
41
41
 
42
42
  The desktop dashboard uses Python Tkinter. Some Python distributions install this separately; for Homebrew Python, install the matching package such as `brew install python-tk@3.14`.
43
43
 
@@ -101,6 +101,9 @@ fdev
101
101
  # Show command help
102
102
  fdev help
103
103
 
104
+ # Upgrade the fdev CLI from PyPI
105
+ fdev upgrade
106
+
104
107
  # Check environment and optional tools
105
108
  fdev doctor
106
109
 
@@ -205,6 +208,8 @@ Switching GitHub accounts runs `gh auth switch` and `gh auth setup-git`, but it
205
208
  | `fdev` | Opens a small Tkinter desktop dashboard for running `fdev` commands with live colored logs and command input. The terminal process stays running while the window is open. |
206
209
  | `fdev help` | Shows command help without opening the dashboard. |
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 upgrade` | Detects the OS, then upgrades the active Python environment's `flutter-dev` package from PyPI with `python -m pip install --upgrade flutter-dev`. |
212
+ | `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. |
208
213
  | `fdev ai` | Shows the current AI provider and opens an interactive provider switch prompt. |
209
214
  | `fdev ai <provider>` | Switches `DEFAULT_AI_SERVICE` to `groq`, `mistral`, `sambanova`, or `openrouter`. |
210
215
  | `fdev brew` | Interactive Homebrew package manager for viewing packages, uninstalling with cleanup, clearing cache, and global cleanup. |
@@ -334,6 +339,7 @@ managers/mirror.py scrcpy install, mirror, wireless ADB setup
334
339
  managers/git.py commit, tag, discard, branch sync, deployment merge
335
340
  managers/git_account.py GitHub account manager around gh CLI
336
341
  managers/doctor.py Environment diagnostics
342
+ managers/package.py PyPI self-upgrade and local uninstall commands
337
343
  managers/ai.py AI provider status and switching
338
344
  managers/brew.py Interactive Homebrew manager
339
345
  managers/merge.py paths.txt to path_merge_files.txt merger
@@ -352,6 +358,8 @@ The installed Python layout is top-level modules plus the `core` and `managers`
352
358
  - `fdev cleanup` applies `dart fix`, formats the project, and runs a major-version dependency upgrade.
353
359
  - `fdev clear-data` clears app data on Android after confirmation.
354
360
  - `fdev uninstall` may fall back to foreground app detection outside a Flutter project root.
361
+ - `fdev upgrade` and `fdev delete` manage the local `flutter-dev` Python package, not the Flutter app on a device.
362
+ - `fdev delete` removes the `fdev` command from the current Python environment after confirmation; reinstall with `python -m pip install flutter-dev`.
355
363
  - `fdev` with no command opens the desktop dashboard. Use `fdev help` to print command usage.
356
364
  - Most command subcommands create build output directories at startup in the current working directory.
357
365
  - Some subcommands return failure internally without always mapping that failure to a non-zero process exit code, so automation should inspect command output carefully.
@@ -380,6 +388,17 @@ On Windows, use:
380
388
  where fdev
381
389
  ```
382
390
 
391
+ ### Upgrade Or Remove fdev
392
+
393
+ Use the built-in package management commands from the Python environment where `fdev` is installed:
394
+
395
+ ```bash
396
+ fdev upgrade
397
+ fdev delete
398
+ ```
399
+
400
+ Both commands print the detected OS and exact Python executable before running. They use that executable with `-m pip`, not a hardcoded `python`, `python3`, `pip`, or `pip3`, so the active venv/global Python that launched `fdev` is the environment that gets upgraded or deleted. On Windows, `fdev delete` and `fdev upgrade` schedule the same `sys.executable -m pip ...` operation in a delayed command process so the currently running console launcher can exit first.
401
+
383
402
  ### Flutter Project Not Detected
384
403
 
385
404
  Run project-specific commands from the directory that contains `pubspec.yaml`.
@@ -73,6 +73,10 @@ from managers.git_account import (
73
73
  from managers.gui import (
74
74
  launch_gui,
75
75
  )
76
+ from managers.package import (
77
+ delete_fdev_command,
78
+ upgrade_fdev_command,
79
+ )
76
80
 
77
81
 
78
82
  def show_usage(exit_code=1):
@@ -118,6 +122,8 @@ def show_usage(exit_code=1):
118
122
  print(f"\n{BLUE}Utility Commands:{NC}")
119
123
  print(" doctor Check environment health (tools, deps, config)")
120
124
  print(" brew Interactive Homebrew package manager")
125
+ print(" upgrade Upgrade fdev from PyPI")
126
+ print(" delete Uninstall fdev from this computer")
121
127
  print(" help Show this help")
122
128
  print(" merge Merge files from paths.txt into single output file")
123
129
  print(" ai Show current AI service or switch to another")
@@ -142,6 +148,8 @@ def show_usage(exit_code=1):
142
148
  print(f" {GREEN}fdev git config{NC} # Update git user.name / user.email")
143
149
  print(f" {GREEN}fdev arb-to-csv{NC} # Export lib/l10n ARB files to translations.csv")
144
150
  print(f" {GREEN}fdev csv-to-arb{NC} # Generate lib/l10n ARB files from translations.csv")
151
+ print(f" {GREEN}fdev upgrade{NC} # Upgrade flutter-dev from PyPI")
152
+ print(f" {GREEN}fdev delete{NC} # Remove flutter-dev/fdev from this computer")
145
153
  print(f" {GREEN}fdev{NC} # Open small desktop dashboard")
146
154
 
147
155
  print(f"\n{BLUE}Note:{NC}")
@@ -259,6 +267,12 @@ def main():
259
267
  elif command == "brew":
260
268
  brew_manager()
261
269
 
270
+ # Package self-management commands
271
+ elif command == "upgrade":
272
+ sys.exit(upgrade_fdev_command(sys.argv[2:]))
273
+ elif command == "delete":
274
+ sys.exit(delete_fdev_command(sys.argv[2:]))
275
+
262
276
  # AI command
263
277
  elif command == "ai":
264
278
  if len(sys.argv) < 3:
@@ -30,8 +30,10 @@ managers/gui.py
30
30
  managers/localization.py
31
31
  managers/merge.py
32
32
  managers/mirror.py
33
+ managers/package.py
33
34
  managers/project.py
34
35
  managers/web_deploy.py
35
36
  tests/test_fdev_cli.py
36
37
  tests/test_gui.py
37
- tests/test_localization.py
38
+ tests/test_localization.py
39
+ tests/test_package_commands.py
@@ -194,6 +194,8 @@ def get_gui_sections():
194
194
  "Tools",
195
195
  [
196
196
  GuiCommand("Brew", ["brew"], "Open Homebrew package manager."),
197
+ GuiCommand("Upgrade fdev", ["upgrade"], "Upgrade flutter-dev from PyPI."),
198
+ GuiCommand("Delete fdev", ["delete"], "Uninstall flutter-dev from this computer.", confirm=True),
197
199
  GuiCommand("Merge Files", ["merge"], "Merge paths listed in paths.txt."),
198
200
  GuiCommand(
199
201
  "AI Switch",
@@ -335,18 +337,21 @@ class FdevGui:
335
337
  self.status_label = ttk.Label(header, text="Ready")
336
338
  self.status_label.grid(row=0, column=1, sticky="e")
337
339
 
338
- body = ttk.PanedWindow(content, orient=tk.VERTICAL)
340
+ body = ttk.Frame(content)
339
341
  body.grid(row=1, column=0, sticky="nsew")
342
+ body.columnconfigure(0, weight=1)
343
+ body.rowconfigure(0, weight=0)
344
+ body.rowconfigure(1, weight=1)
340
345
 
341
346
  self.command_frame = ttk.Frame(body, padding=(0, 0, 0, 6))
347
+ self.command_frame.grid(row=0, column=0, sticky="ew")
342
348
  self.command_frame.columnconfigure(0, weight=1)
343
349
  self.command_frame.columnconfigure(1, weight=1)
344
- body.add(self.command_frame, weight=2)
345
350
 
346
351
  log_frame = ttk.Frame(body)
352
+ log_frame.grid(row=1, column=0, sticky="nsew", pady=(6, 0))
347
353
  log_frame.columnconfigure(0, weight=1)
348
354
  log_frame.rowconfigure(0, weight=1)
349
- body.add(log_frame, weight=3)
350
355
 
351
356
  self.log_text = tk.Text(
352
357
  log_frame,
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Package Manager - self-management commands for the installed fdev package.
4
+ """
5
+
6
+ import argparse
7
+ import platform
8
+ import subprocess
9
+ import sys
10
+
11
+ from common_utils import BLUE, GREEN, RED, YELLOW, NC
12
+
13
+ PACKAGE_NAME = "flutter-dev"
14
+
15
+
16
+ def get_os_info():
17
+ """
18
+ Detect the operating system before package self-management actions.
19
+ Returns a tuple of (key, display_name).
20
+ """
21
+ system = platform.system()
22
+
23
+ if system == "Darwin":
24
+ return "macos", "macOS"
25
+ if system == "Linux":
26
+ return "linux", "Linux"
27
+ if system == "Windows":
28
+ return "windows", "Windows"
29
+
30
+ return "unknown", system or "Unknown"
31
+
32
+
33
+ def _print_os_info(os_name):
34
+ print(f"{BLUE}Detected OS: {GREEN}{os_name}{NC}")
35
+
36
+
37
+ def _print_python_environment():
38
+ print(f"{BLUE}Python environment: {GREEN}{sys.executable}{NC}")
39
+
40
+
41
+ def _build_pip_command(pip_args):
42
+ return [sys.executable, "-m", "pip", *pip_args]
43
+
44
+
45
+ def _run_pip_direct(pip_args):
46
+ result = subprocess.run(_build_pip_command(pip_args))
47
+ return result.returncode
48
+
49
+
50
+ def _build_windows_detached_command(pip_args):
51
+ pip_command = subprocess.list2cmdline(_build_pip_command(pip_args))
52
+ delayed_command = f"timeout /t 2 /nobreak > nul & {pip_command}"
53
+ return [
54
+ "cmd",
55
+ "/c",
56
+ "start",
57
+ "fdev package command",
58
+ "cmd",
59
+ "/c",
60
+ delayed_command,
61
+ ]
62
+
63
+
64
+ def _run_windows_detached(pip_args, action_label):
65
+ command = _build_windows_detached_command(pip_args)
66
+ creationflags = (
67
+ getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
68
+ | getattr(subprocess, "DETACHED_PROCESS", 0)
69
+ )
70
+ subprocess.Popen(command, creationflags=creationflags)
71
+ print(f"{YELLOW}{action_label} has been scheduled in a new Windows command process.{NC}")
72
+ print(f"{YELLOW}Close this terminal after the command starts, then open a new one.{NC}")
73
+ return 0
74
+
75
+
76
+ def _confirm_delete():
77
+ print(f"{RED}This will uninstall the local {PACKAGE_NAME} package and remove the fdev command.{NC}")
78
+ answer = input(f"Delete {GREEN}{PACKAGE_NAME}{NC} from this Python environment? (y/N): ")
79
+ return answer.strip().lower() == "y"
80
+
81
+
82
+ def _delete_parser():
83
+ parser = argparse.ArgumentParser(
84
+ prog="fdev delete",
85
+ description="Uninstall the local flutter-dev package that provides fdev.",
86
+ )
87
+ parser.add_argument(
88
+ "-y",
89
+ "--yes",
90
+ action="store_true",
91
+ help="Skip confirmation and uninstall flutter-dev.",
92
+ )
93
+ return parser
94
+
95
+
96
+ def _upgrade_parser():
97
+ return argparse.ArgumentParser(
98
+ prog="fdev upgrade",
99
+ description="Upgrade the local flutter-dev package from PyPI.",
100
+ )
101
+
102
+
103
+ def delete_fdev_command(argv=None):
104
+ """Uninstall the local flutter-dev package from the active Python environment."""
105
+ args = _delete_parser().parse_args(argv)
106
+ os_key, os_name = get_os_info()
107
+ _print_os_info(os_name)
108
+ _print_python_environment()
109
+
110
+ if not args.yes and not _confirm_delete():
111
+ print(f"{YELLOW}Operation cancelled.{NC}")
112
+ return 1
113
+
114
+ pip_args = ["uninstall", "-y", PACKAGE_NAME]
115
+ print(f"{YELLOW}Uninstalling {PACKAGE_NAME} from the current Python environment...{NC}")
116
+
117
+ if os_key == "windows":
118
+ return _run_windows_detached(pip_args, "Uninstall")
119
+
120
+ result = _run_pip_direct(pip_args)
121
+ if result == 0:
122
+ print(f"{GREEN}✓ {PACKAGE_NAME} uninstalled successfully.{NC}")
123
+ print(f"{BLUE}Install again with: {GREEN}python -m pip install {PACKAGE_NAME}{NC}")
124
+ else:
125
+ print(f"{RED}✗ Failed to uninstall {PACKAGE_NAME}.{NC}")
126
+ return result
127
+
128
+
129
+ def upgrade_fdev_command(argv=None):
130
+ """Upgrade the local flutter-dev package from PyPI in the active environment."""
131
+ _upgrade_parser().parse_args(argv)
132
+ os_key, os_name = get_os_info()
133
+ _print_os_info(os_name)
134
+ _print_python_environment()
135
+
136
+ pip_args = ["install", "--upgrade", PACKAGE_NAME]
137
+ print(f"{YELLOW}Upgrading {PACKAGE_NAME} from PyPI...{NC}")
138
+
139
+ if os_key == "windows":
140
+ return _run_windows_detached(pip_args, "Upgrade")
141
+
142
+ result = _run_pip_direct(pip_args)
143
+ if result == 0:
144
+ print(f"{GREEN}✓ {PACKAGE_NAME} upgraded successfully.{NC}")
145
+ print(f"{BLUE}Restart your terminal if the fdev command still uses the old version.{NC}")
146
+ else:
147
+ print(f"{RED}✗ Failed to upgrade {PACKAGE_NAME}.{NC}")
148
+ return result
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "flutter-dev"
7
- version = "0.1.5"
7
+ version = "0.1.6"
8
8
  description = "A cross-platform CLI toolkit for Flutter development workflows."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -38,6 +38,28 @@ class FdevCliTests(unittest.TestCase):
38
38
  show_usage.assert_called_once_with()
39
39
  launch_gui.assert_not_called()
40
40
 
41
+ def test_delete_command_routes_to_package_delete(self):
42
+ with (
43
+ patch.object(sys, "argv", ["fdev", "delete", "--yes"]),
44
+ patch.object(fdev, "delete_fdev_command", return_value=0) as delete_command,
45
+ ):
46
+ with self.assertRaises(SystemExit) as raised:
47
+ fdev.main()
48
+
49
+ self.assertEqual(raised.exception.code, 0)
50
+ delete_command.assert_called_once_with(["--yes"])
51
+
52
+ def test_upgrade_command_routes_to_package_upgrade(self):
53
+ with (
54
+ patch.object(sys, "argv", ["fdev", "upgrade"]),
55
+ patch.object(fdev, "upgrade_fdev_command", return_value=0) as upgrade_command,
56
+ ):
57
+ with self.assertRaises(SystemExit) as raised:
58
+ fdev.main()
59
+
60
+ self.assertEqual(raised.exception.code, 0)
61
+ upgrade_command.assert_called_once_with([])
62
+
41
63
 
42
64
  if __name__ == "__main__":
43
65
  unittest.main()
@@ -1,8 +1,10 @@
1
1
  import sys
2
2
  import unittest
3
3
  from pathlib import Path
4
+ from tkinter import TclError
4
5
 
5
6
  from managers.gui import (
7
+ FdevGui,
6
8
  GuiCommand,
7
9
  parse_ansi_segments,
8
10
  build_fdev_command,
@@ -86,6 +88,8 @@ class GuiCatalogTests(unittest.TestCase):
86
88
  ("deploy",),
87
89
  ("doctor",),
88
90
  ("brew",),
91
+ ("upgrade",),
92
+ ("delete",),
89
93
  ("merge",),
90
94
  ("ai",),
91
95
  ("ai", "groq"),
@@ -140,6 +144,40 @@ class GuiCatalogTests(unittest.TestCase):
140
144
  ["sync", "dev", "main"],
141
145
  )
142
146
 
147
+ def test_dashboard_uses_stable_grid_layout_without_paned_window(self):
148
+ try:
149
+ import tkinter as tk
150
+ except ImportError:
151
+ self.skipTest("Tkinter is not available")
152
+
153
+ try:
154
+ root = tk.Tk()
155
+ except TclError as error:
156
+ self.skipTest(f"Tk display is not available: {error}")
157
+
158
+ try:
159
+ app = FdevGui(root)
160
+ root.update_idletasks()
161
+
162
+ widget_classes = {
163
+ widget.winfo_class()
164
+ for widget in _walk_widgets(root)
165
+ }
166
+
167
+ self.assertNotIn("TPanedwindow", widget_classes)
168
+ self.assertEqual(app.section_label.cget("text"), "Dashboard")
169
+ self.assertGreaterEqual(len(app.command_frame.winfo_children()), 3)
170
+ self.assertGreater(app.command_frame.winfo_width(), 100)
171
+ self.assertGreater(app.log_text.winfo_width(), 100)
172
+ finally:
173
+ root.destroy()
174
+
175
+
176
+ def _walk_widgets(widget):
177
+ yield widget
178
+ for child in widget.winfo_children():
179
+ yield from _walk_widgets(child)
180
+
143
181
 
144
182
  if __name__ == "__main__":
145
183
  unittest.main()
@@ -0,0 +1,129 @@
1
+ import io
2
+ import subprocess
3
+ import unittest
4
+ from contextlib import redirect_stdout
5
+ from unittest.mock import patch
6
+
7
+ from managers import package
8
+
9
+
10
+ class PackageCommandTests(unittest.TestCase):
11
+ def test_delete_yes_runs_pip_uninstall_in_current_python_environment(self):
12
+ completed = subprocess.CompletedProcess(
13
+ ["/venv/bin/python", "-m", "pip", "uninstall", "-y", "flutter-dev"],
14
+ 0,
15
+ )
16
+
17
+ with (
18
+ patch.object(package.platform, "system", return_value="Darwin"),
19
+ patch.object(package.sys, "executable", "/venv/bin/python"),
20
+ patch.object(package.subprocess, "run", return_value=completed) as run,
21
+ ):
22
+ result = package.delete_fdev_command(["--yes"])
23
+
24
+ self.assertEqual(result, 0)
25
+ run.assert_called_once_with(
26
+ ["/venv/bin/python", "-m", "pip", "uninstall", "-y", "flutter-dev"]
27
+ )
28
+
29
+ def test_delete_cancelled_by_user_does_not_run_pip(self):
30
+ with (
31
+ patch.object(package.platform, "system", return_value="Linux"),
32
+ patch("builtins.input", return_value="n"),
33
+ patch.object(package.subprocess, "run") as run,
34
+ ):
35
+ result = package.delete_fdev_command([])
36
+
37
+ self.assertEqual(result, 1)
38
+ run.assert_not_called()
39
+
40
+ def test_upgrade_runs_pip_upgrade_in_current_python_environment(self):
41
+ completed = subprocess.CompletedProcess(
42
+ ["/venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"],
43
+ 0,
44
+ )
45
+
46
+ with (
47
+ patch.object(package.platform, "system", return_value="Linux"),
48
+ patch.object(package.sys, "executable", "/venv/bin/python"),
49
+ patch.object(package.subprocess, "run", return_value=completed) as run,
50
+ ):
51
+ result = package.upgrade_fdev_command([])
52
+
53
+ self.assertEqual(result, 0)
54
+ run.assert_called_once_with(
55
+ ["/venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"]
56
+ )
57
+
58
+ def test_windows_delete_schedules_detached_uninstall(self):
59
+ with (
60
+ patch.object(package.platform, "system", return_value="Windows"),
61
+ patch.object(package.sys, "executable", r"C:\Python\python.exe"),
62
+ patch.object(package.subprocess, "Popen") as popen,
63
+ ):
64
+ result = package.delete_fdev_command(["--yes"])
65
+
66
+ self.assertEqual(result, 0)
67
+ popen.assert_called_once()
68
+ command = popen.call_args.args[0]
69
+ self.assertEqual(command[0], "cmd")
70
+ joined_command = " ".join(command)
71
+ self.assertIn("uninstall", joined_command)
72
+ self.assertIn("flutter-dev", joined_command)
73
+
74
+ def test_windows_detached_command_quotes_python_paths_with_spaces(self):
75
+ with patch.object(package.sys, "executable", r"C:\Program Files\Python312\python.exe"):
76
+ command = package._build_windows_detached_command(["install", "--upgrade", "flutter-dev"])
77
+
78
+ self.assertEqual(command[:3], ["cmd", "/c", "start"])
79
+ self.assertEqual(command[3], "fdev package command")
80
+ self.assertEqual(command[4:6], ["cmd", "/c"])
81
+ self.assertIn('"C:\\Program Files\\Python312\\python.exe"', command[6])
82
+ self.assertIn("-m pip install --upgrade flutter-dev", command[6])
83
+
84
+ def test_linux_delete_uses_running_python_not_python3_or_pip3(self):
85
+ completed = subprocess.CompletedProcess(
86
+ ["/home/user/project/.venv/bin/python", "-m", "pip", "uninstall", "-y", "flutter-dev"],
87
+ 0,
88
+ )
89
+
90
+ with (
91
+ patch.object(package.platform, "system", return_value="Linux"),
92
+ patch.object(package.sys, "executable", "/home/user/project/.venv/bin/python"),
93
+ patch.object(package.subprocess, "run", return_value=completed) as run,
94
+ ):
95
+ result = package.delete_fdev_command(["--yes"])
96
+
97
+ self.assertEqual(result, 0)
98
+ run.assert_called_once_with(
99
+ ["/home/user/project/.venv/bin/python", "-m", "pip", "uninstall", "-y", "flutter-dev"]
100
+ )
101
+
102
+ def test_upgrade_prints_target_python_environment(self):
103
+ completed = subprocess.CompletedProcess(
104
+ ["/home/user/project/.venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"],
105
+ 0,
106
+ )
107
+ output = io.StringIO()
108
+
109
+ with (
110
+ patch.object(package.platform, "system", return_value="Linux"),
111
+ patch.object(package.sys, "executable", "/home/user/project/.venv/bin/python"),
112
+ patch.object(package.subprocess, "run", return_value=completed),
113
+ redirect_stdout(output),
114
+ ):
115
+ result = package.upgrade_fdev_command([])
116
+
117
+ self.assertEqual(result, 0)
118
+ self.assertIn("Python environment:", output.getvalue())
119
+ self.assertIn("/home/user/project/.venv/bin/python", output.getvalue())
120
+
121
+ def test_command_help_returns_zero(self):
122
+ with self.assertRaises(SystemExit) as raised:
123
+ package.delete_fdev_command(["--help"])
124
+
125
+ self.assertEqual(raised.exception.code, 0)
126
+
127
+
128
+ if __name__ == "__main__":
129
+ unittest.main()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes