voice-mode 2.29.0__py3-none-any.whl → 2.31.0__py3-none-any.whl

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 (63) hide show
  1. voice_mode/__version__.py +1 -1
  2. voice_mode/cli.py +171 -200
  3. voice_mode/frontend/.next/BUILD_ID +1 -1
  4. voice_mode/frontend/.next/app-build-manifest.json +5 -5
  5. voice_mode/frontend/.next/build-manifest.json +3 -3
  6. voice_mode/frontend/.next/next-minimal-server.js.nft.json +1 -1
  7. voice_mode/frontend/.next/next-server.js.nft.json +1 -1
  8. voice_mode/frontend/.next/prerender-manifest.json +1 -1
  9. voice_mode/frontend/.next/required-server-files.json +1 -1
  10. voice_mode/frontend/.next/server/app/_not-found/page.js +1 -1
  11. voice_mode/frontend/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  12. voice_mode/frontend/.next/server/app/_not-found.html +1 -1
  13. voice_mode/frontend/.next/server/app/_not-found.rsc +1 -1
  14. voice_mode/frontend/.next/server/app/api/connection-details/route.js +2 -2
  15. voice_mode/frontend/.next/server/app/favicon.ico/route.js +2 -2
  16. voice_mode/frontend/.next/server/app/index.html +1 -1
  17. voice_mode/frontend/.next/server/app/index.rsc +2 -2
  18. voice_mode/frontend/.next/server/app/page.js +3 -3
  19. voice_mode/frontend/.next/server/app/page_client-reference-manifest.js +1 -1
  20. voice_mode/frontend/.next/server/chunks/994.js +1 -1
  21. voice_mode/frontend/.next/server/middleware-build-manifest.js +1 -1
  22. voice_mode/frontend/.next/server/next-font-manifest.js +1 -1
  23. voice_mode/frontend/.next/server/next-font-manifest.json +1 -1
  24. voice_mode/frontend/.next/server/pages/404.html +1 -1
  25. voice_mode/frontend/.next/server/pages/500.html +1 -1
  26. voice_mode/frontend/.next/server/server-reference-manifest.json +1 -1
  27. voice_mode/frontend/.next/standalone/.next/BUILD_ID +1 -1
  28. voice_mode/frontend/.next/standalone/.next/app-build-manifest.json +5 -5
  29. voice_mode/frontend/.next/standalone/.next/build-manifest.json +3 -3
  30. voice_mode/frontend/.next/standalone/.next/prerender-manifest.json +1 -1
  31. voice_mode/frontend/.next/standalone/.next/required-server-files.json +1 -1
  32. voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page.js +1 -1
  33. voice_mode/frontend/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  34. voice_mode/frontend/.next/standalone/.next/server/app/_not-found.html +1 -1
  35. voice_mode/frontend/.next/standalone/.next/server/app/_not-found.rsc +1 -1
  36. voice_mode/frontend/.next/standalone/.next/server/app/api/connection-details/route.js +2 -2
  37. voice_mode/frontend/.next/standalone/.next/server/app/favicon.ico/route.js +2 -2
  38. voice_mode/frontend/.next/standalone/.next/server/app/index.html +1 -1
  39. voice_mode/frontend/.next/standalone/.next/server/app/index.rsc +2 -2
  40. voice_mode/frontend/.next/standalone/.next/server/app/page.js +3 -3
  41. voice_mode/frontend/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  42. voice_mode/frontend/.next/standalone/.next/server/chunks/994.js +1 -1
  43. voice_mode/frontend/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
  44. voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.js +1 -1
  45. voice_mode/frontend/.next/standalone/.next/server/next-font-manifest.json +1 -1
  46. voice_mode/frontend/.next/standalone/.next/server/pages/404.html +1 -1
  47. voice_mode/frontend/.next/standalone/.next/server/pages/500.html +1 -1
  48. voice_mode/frontend/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  49. voice_mode/frontend/.next/standalone/server.js +1 -1
  50. voice_mode/frontend/.next/static/chunks/app/{layout-53ce2eb95847e7bc.js → layout-f2d1799422b78b51.js} +1 -1
  51. voice_mode/frontend/.next/static/chunks/app/{page-9c7f7be4378697be.js → page-49b84bcf9a91501f.js} +1 -1
  52. voice_mode/frontend/.next/static/chunks/{main-app-2233caae67bf73bd.js → main-app-b61926fb6b68dca5.js} +1 -1
  53. voice_mode/frontend/.next/trace +43 -43
  54. voice_mode/frontend/.next/types/app/api/connection-details/route.ts +1 -1
  55. voice_mode/frontend/.next/types/app/layout.ts +1 -1
  56. voice_mode/frontend/.next/types/app/page.ts +1 -1
  57. voice_mode/tools/services/whisper/install.py +1 -1
  58. {voice_mode-2.29.0.dist-info → voice_mode-2.31.0.dist-info}/METADATA +1 -1
  59. {voice_mode-2.29.0.dist-info → voice_mode-2.31.0.dist-info}/RECORD +63 -63
  60. /voice_mode/frontend/.next/static/{998kY2RB1umOQfi6UBnLh → QYw7BshkzP8SKP3LsaTD0}/_buildManifest.js +0 -0
  61. /voice_mode/frontend/.next/static/{998kY2RB1umOQfi6UBnLh → QYw7BshkzP8SKP3LsaTD0}/_ssgManifest.js +0 -0
  62. {voice_mode-2.29.0.dist-info → voice_mode-2.31.0.dist-info}/WHEEL +0 -0
  63. {voice_mode-2.29.0.dist-info → voice_mode-2.31.0.dist-info}/entry_points.txt +0 -0
voice_mode/__version__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # This file is automatically updated by 'make release'
2
2
  # Do not edit manually
3
- __version__ = "2.29.0"
3
+ __version__ = "2.31.0"
voice_mode/cli.py CHANGED
@@ -473,7 +473,7 @@ def whisper_model_active(model_name):
473
473
  # Check if model is installed
474
474
  if not is_whisper_model_installed(model_name):
475
475
  click.echo(f"Error: Model '{model_name}' is not installed.", err=True)
476
- click.echo(f"Install it with: voice-mode whisper model install {model_name}", err=True)
476
+ click.echo(f"Install it with: voicemode whisper model install {model_name}", err=True)
477
477
  raise click.Abort()
478
478
 
479
479
  # Get previous model
@@ -492,14 +492,14 @@ def whisper_model_active(model_name):
492
492
  if result.returncode == 0:
493
493
  # Service is running
494
494
  click.echo(f"\n⚠️ Please restart the whisper service for changes to take effect:")
495
- click.echo(f" {click.style('voice-mode whisper restart', fg='yellow', bold=True)}")
495
+ click.echo(f" {click.style('voicemode whisper restart', fg='yellow', bold=True)}")
496
496
  else:
497
497
  click.echo(f"\nWhisper service is not running. Start it with:")
498
- click.echo(f" voice-mode whisper start")
498
+ click.echo(f" voicemode whisper start")
499
499
  click.echo(f"(or restart the whisper service if it's managed by systemd/launchd)")
500
500
  except:
501
501
  click.echo(f"\nPlease restart the whisper service for changes to take effect:")
502
- click.echo(f" voice-mode whisper restart")
502
+ click.echo(f" voicemode whisper restart")
503
503
 
504
504
  else:
505
505
  # Show current model
@@ -527,8 +527,8 @@ def whisper_model_active(model_name):
527
527
  except:
528
528
  pass
529
529
 
530
- click.echo(f"\nTo change: voice-mode whisper model active <model-name>")
531
- click.echo(f"To list all models: voice-mode whisper models")
530
+ click.echo(f"\nTo change: voicemode whisper model active <model-name>")
531
+ click.echo(f"To list all models: voicemode whisper models")
532
532
 
533
533
 
534
534
  @whisper.command("models")
@@ -604,8 +604,8 @@ def whisper_models():
604
604
  click.echo(f"Models directory: {model_dir}")
605
605
  click.echo(f"Total size: {format_size(total_installed_size)} installed / {format_size(total_available_size)} available")
606
606
  click.echo("")
607
- click.echo("To download a model: voice-mode whisper model install <model-name>")
608
- click.echo("To set default model: voice-mode whisper model <model-name>")
607
+ click.echo("To download a model: voicemode whisper model install <model-name>")
608
+ click.echo("To set default model: voicemode whisper model <model-name>")
609
609
 
610
610
 
611
611
  @whisper_model.command("install")
@@ -613,7 +613,8 @@ def whisper_models():
613
613
  @click.argument('model', default='large-v2')
614
614
  @click.option('--force', '-f', is_flag=True, help='Re-download even if model exists')
615
615
  @click.option('--skip-core-ml', is_flag=True, help='Skip Core ML conversion on Apple Silicon')
616
- def whisper_model_install(model, force, skip_core_ml):
616
+ @click.option('--install-torch', is_flag=True, help='Install PyTorch for Core ML conversion (~2.5GB)')
617
+ def whisper_model_install(model, force, skip_core_ml, install_torch):
617
618
  """Install Whisper model(s) with optional Core ML conversion.
618
619
 
619
620
  MODEL can be a model name (e.g., 'large-v2'), 'all' to download all models,
@@ -627,15 +628,36 @@ def whisper_model_install(model, force, skip_core_ml):
627
628
  # Get the actual function from the MCP tool wrapper
628
629
  tool = install_module.whisper_model_install
629
630
  install_func = tool.fn if hasattr(tool, 'fn') else tool
631
+
632
+ # First attempt without install_torch to check if it's needed
630
633
  result = asyncio.run(install_func(
631
634
  model=model,
632
635
  force_download=force,
633
- skip_core_ml=skip_core_ml
636
+ skip_core_ml=skip_core_ml,
637
+ install_torch=install_torch,
638
+ auto_confirm=install_torch # If user passed --install-torch, skip confirmation
634
639
  ))
635
640
 
636
641
  try:
637
642
  # Parse JSON response
638
643
  data = json.loads(result)
644
+
645
+ # Check if PyTorch installation is required for Core ML
646
+ if data.get('requires_confirmation') and not install_torch and not skip_core_ml:
647
+ click.echo("\n" + data.get('message', 'Core ML requires PyTorch (~2.5GB)'))
648
+ if data.get('recommendation'):
649
+ click.echo(f"💡 {data['recommendation']}")
650
+
651
+ if click.confirm("\nWould you like to install PyTorch for Core ML acceleration?"):
652
+ # Retry with install_torch=True
653
+ result = asyncio.run(install_func(
654
+ model=model,
655
+ force_download=force,
656
+ skip_core_ml=skip_core_ml,
657
+ install_torch=True,
658
+ auto_confirm=True
659
+ ))
660
+ data = json.loads(result)
639
661
  if data.get('success'):
640
662
  click.echo("✅ Model download completed!")
641
663
 
@@ -984,7 +1006,7 @@ def frontend_install(auto_enable):
984
1006
  if result.get('service_enabled'):
985
1007
  click.echo("\n💡 Frontend service is enabled and will start automatically at boot/login")
986
1008
  else:
987
- click.echo("\n💡 Run 'voice-mode livekit frontend enable' to start automatically at boot/login")
1009
+ click.echo("\n💡 Run 'voicemode livekit frontend enable' to start automatically at boot/login")
988
1010
  else:
989
1011
  click.echo(f"❌ Frontend installation failed: {result.get('error', 'Unknown error')}")
990
1012
 
@@ -1185,7 +1207,7 @@ def frontend_build(force):
1185
1207
  @voice_mode_main_cli.group()
1186
1208
  @click.help_option('-h', '--help', help='Show this message and exit')
1187
1209
  def config():
1188
- """Manage voice-mode configuration."""
1210
+ """Manage voicemode configuration."""
1189
1211
  pass
1190
1212
 
1191
1213
 
@@ -1232,7 +1254,7 @@ def config_get(key):
1232
1254
  click.echo(f"{key}={env_value} (from environment)")
1233
1255
  else:
1234
1256
  click.echo(f"❌ Configuration key not found: {key}")
1235
- click.echo("Run 'voice-mode config list' to see available keys")
1257
+ click.echo("Run 'voicemode config list' to see available keys")
1236
1258
 
1237
1259
 
1238
1260
  @config.command("set")
@@ -1246,163 +1268,17 @@ def config_set(key, value):
1246
1268
  click.echo(result)
1247
1269
 
1248
1270
 
1249
- # Shell completion group
1250
- @voice_mode_main_cli.group()
1251
- @click.help_option('-h', '--help', help='Show this message and exit')
1252
- def completion():
1253
- """Generate shell completion scripts for voice-mode."""
1254
- pass
1255
-
1256
-
1257
- @completion.command("bash")
1258
- def completion_bash():
1259
- """Generate bash completion script.
1260
-
1261
- Add this to your ~/.bashrc:
1262
-
1263
- eval "$(_VOICE_MODE_COMPLETE=bash_source voice-mode)"
1264
-
1265
- Or for better performance, generate the script once:
1266
-
1267
- _VOICE_MODE_COMPLETE=bash_source voice-mode > ~/.voice-mode-complete.bash
1268
- echo '. ~/.voice-mode-complete.bash' >> ~/.bashrc
1269
- """
1270
- # Output the instructions directly since the environment variable method
1271
- # needs to be run from the shell itself
1272
- click.echo("# Bash completion for voice-mode")
1273
- click.echo("# Add this to your ~/.bashrc:")
1274
- click.echo("")
1275
- click.echo('eval "$(_VOICE_MODE_COMPLETE=bash_source voice-mode)"')
1276
- click.echo("")
1277
- click.echo("# Or for better performance, generate and save the script:")
1278
- click.echo("# _VOICE_MODE_COMPLETE=bash_source voice-mode > ~/.voice-mode-complete.bash")
1279
- click.echo("# Then add to ~/.bashrc:")
1280
- click.echo("# . ~/.voice-mode-complete.bash")
1281
-
1282
-
1283
- @completion.command("zsh")
1284
- def completion_zsh():
1285
- """Generate zsh completion script.
1286
-
1287
- Add this to your ~/.zshrc:
1288
-
1289
- eval "$(_VOICE_MODE_COMPLETE=zsh_source voice-mode)"
1290
-
1291
- Or for better performance, generate the script once:
1292
-
1293
- _VOICE_MODE_COMPLETE=zsh_source voice-mode > ~/.voice-mode-complete.zsh
1294
- echo '. ~/.voice-mode-complete.zsh' >> ~/.zshrc
1295
- """
1296
- # Output the instructions directly
1297
- click.echo("# Zsh completion for voice-mode")
1298
- click.echo("# Add this to your ~/.zshrc:")
1299
- click.echo("")
1300
- click.echo('eval "$(_VOICE_MODE_COMPLETE=zsh_source voice-mode)"')
1301
- click.echo("")
1302
- click.echo("# Or for better performance, generate and save the script:")
1303
- click.echo("# _VOICE_MODE_COMPLETE=zsh_source voice-mode > ~/.voice-mode-complete.zsh")
1304
- click.echo("# Then add to ~/.zshrc:")
1305
- click.echo("# . ~/.voice-mode-complete.zsh")
1306
-
1307
-
1308
- @completion.command("fish")
1309
- def completion_fish():
1310
- """Generate fish completion script.
1311
-
1312
- Add this to ~/.config/fish/completions/voice-mode.fish:
1313
-
1314
- _VOICE_MODE_COMPLETE=fish_source voice-mode | source
1315
-
1316
- Or save it to a file:
1317
-
1318
- _VOICE_MODE_COMPLETE=fish_source voice-mode > ~/.config/fish/completions/voice-mode.fish
1319
- """
1320
- # Output the instructions directly
1321
- click.echo("# Fish completion for voice-mode")
1322
- click.echo("# Save this to ~/.config/fish/completions/voice-mode.fish:")
1323
- click.echo("")
1324
- click.echo("_VOICE_MODE_COMPLETE=fish_source voice-mode | source")
1325
- click.echo("")
1326
- click.echo("# Or run this command to save it:")
1327
- click.echo("# _VOICE_MODE_COMPLETE=fish_source voice-mode > ~/.config/fish/completions/voice-mode.fish")
1328
-
1329
-
1330
- @completion.command("install")
1331
- @click.help_option('-h', '--help')
1332
- @click.option('--shell', type=click.Choice(['bash', 'zsh', 'fish', 'auto']), default='auto', help='Shell type to install for')
1333
- def completion_install(shell):
1334
- """Show installation instructions for shell completion.
1335
-
1336
- This command displays the steps to enable tab completion
1337
- for voice-mode in your shell.
1338
- """
1339
- # Detect shell if auto
1340
- if shell == 'auto':
1341
- shell_env = os.environ.get('SHELL', '')
1342
- if 'bash' in shell_env:
1343
- shell = 'bash'
1344
- elif 'zsh' in shell_env:
1345
- shell = 'zsh'
1346
- elif 'fish' in shell_env:
1347
- shell = 'fish'
1348
- else:
1349
- click.echo("Could not detect shell type. Showing instructions for all shells.")
1350
- click.echo("")
1351
-
1352
- # Show all instructions
1353
- click.echo("=== Bash ===")
1354
- click.echo("Add to ~/.bashrc:")
1355
- click.echo(' eval "$(_VOICE_MODE_COMPLETE=bash_source voice-mode)"')
1356
- click.echo("")
1357
-
1358
- click.echo("=== Zsh ===")
1359
- click.echo("Add to ~/.zshrc:")
1360
- click.echo(' eval "$(_VOICE_MODE_COMPLETE=zsh_source voice-mode)"')
1361
- click.echo("")
1362
-
1363
- click.echo("=== Fish ===")
1364
- click.echo("Run this command:")
1365
- click.echo(' _VOICE_MODE_COMPLETE=fish_source voice-mode > ~/.config/fish/completions/voice-mode.fish')
1366
- return
1367
-
1368
- click.echo(f"To enable tab completion for voice-mode in {shell}:")
1369
- click.echo("")
1370
-
1371
- if shell == 'bash':
1372
- click.echo("Add this line to your ~/.bashrc:")
1373
- click.echo(' eval "$(_VOICE_MODE_COMPLETE=bash_source voice-mode)"')
1374
- click.echo("")
1375
- click.echo("Or for better performance, generate the script once:")
1376
- click.echo(" _VOICE_MODE_COMPLETE=bash_source voice-mode > ~/.voice-mode-complete.bash")
1377
- click.echo(" echo '. ~/.voice-mode-complete.bash' >> ~/.bashrc")
1378
- elif shell == 'zsh':
1379
- click.echo("Add this line to your ~/.zshrc:")
1380
- click.echo(' eval "$(_VOICE_MODE_COMPLETE=zsh_source voice-mode)"')
1381
- click.echo("")
1382
- click.echo("Or for better performance, generate the script once:")
1383
- click.echo(" _VOICE_MODE_COMPLETE=zsh_source voice-mode > ~/.voice-mode-complete.zsh")
1384
- click.echo(" echo '. ~/.voice-mode-complete.zsh' >> ~/.zshrc")
1385
- elif shell == 'fish':
1386
- click.echo("Run this command:")
1387
- click.echo(' _VOICE_MODE_COMPLETE=fish_source voice-mode > ~/.config/fish/completions/voice-mode.fish')
1388
- click.echo("")
1389
- click.echo("Fish will automatically load the completion from this location.")
1390
-
1391
- click.echo("")
1392
- click.echo("After making these changes, restart your shell or source the config file.")
1393
-
1394
-
1395
1271
  # Diagnostics group
1396
1272
  @voice_mode_main_cli.group()
1397
1273
  @click.help_option('-h', '--help', help='Show this message and exit')
1398
1274
  def diag():
1399
- """Diagnostic tools for voice-mode."""
1275
+ """Diagnostic tools for voicemode."""
1400
1276
  pass
1401
1277
 
1402
1278
 
1403
1279
  @diag.command()
1404
1280
  def info():
1405
- """Show voice-mode installation information."""
1281
+ """Show voicemode installation information."""
1406
1282
  from voice_mode.tools.diagnostics import voice_mode_info
1407
1283
  result = asyncio.run(voice_mode_info.fn())
1408
1284
  click.echo(result)
@@ -1471,7 +1347,7 @@ def dependencies():
1471
1347
  click.echo(str(result))
1472
1348
 
1473
1349
 
1474
- # Legacy CLI for voice-mode-cli command
1350
+ # Legacy CLI for voicemode-cli command
1475
1351
  @click.group()
1476
1352
  @click.version_option()
1477
1353
  @click.help_option('-h', '--help')
@@ -1518,16 +1394,16 @@ def converse(message, wait, duration, min_duration, transport, room_name, voice,
1518
1394
  Examples:
1519
1395
 
1520
1396
  # Simple conversation
1521
- voice-mode converse
1397
+ voicemode converse
1522
1398
 
1523
1399
  # Speak a message without waiting
1524
- voice-mode converse -m "Hello there!" --no-wait
1400
+ voicemode converse -m "Hello there!" --no-wait
1525
1401
 
1526
1402
  # Continuous conversation mode
1527
- voice-mode converse --continuous
1403
+ voicemode converse --continuous
1528
1404
 
1529
1405
  # Use specific voice
1530
- voice-mode converse --voice nova
1406
+ voicemode converse --voice nova
1531
1407
  """
1532
1408
  from voice_mode.tools.converse import converse as converse_fn
1533
1409
 
@@ -1694,18 +1570,89 @@ def version():
1694
1570
  @click.help_option('-h', '--help')
1695
1571
  @click.option('--force', is_flag=True, help='Force reinstall even if already up to date')
1696
1572
  def update(force):
1697
- """Update Voice Mode to the latest version."""
1573
+ """Update Voice Mode to the latest version.
1574
+
1575
+ Automatically detects installation method (UV tool, UV pip, or regular pip)
1576
+ and uses the appropriate update command.
1577
+ """
1698
1578
  import subprocess
1699
1579
  import requests
1580
+ from pathlib import Path
1700
1581
  from importlib.metadata import version as get_version, PackageNotFoundError
1701
1582
 
1583
+ def detect_uv_tool_installation():
1584
+ """Detect if running from a UV tool installation."""
1585
+ prefix_path = Path(sys.prefix).resolve()
1586
+ uv_tools_base = Path.home() / ".local" / "share" / "uv" / "tools"
1587
+
1588
+ # Check if sys.prefix is within UV tools directory
1589
+ if uv_tools_base in prefix_path.parents or prefix_path.parent == uv_tools_base:
1590
+ # Find the tool directory
1591
+ tool_dir = prefix_path if prefix_path.parent == uv_tools_base else None
1592
+
1593
+ if not tool_dir:
1594
+ for parent in prefix_path.parents:
1595
+ if parent.parent == uv_tools_base:
1596
+ tool_dir = parent
1597
+ break
1598
+
1599
+ if tool_dir:
1600
+ # Verify with uv-receipt.toml
1601
+ receipt_file = tool_dir / "uv-receipt.toml"
1602
+ if receipt_file.exists():
1603
+ # Parse tool name from receipt or use directory name
1604
+ try:
1605
+ with open(receipt_file) as f:
1606
+ content = f.read()
1607
+ import re
1608
+ match = re.search(r'name = "([^"]+)"', content)
1609
+ tool_name = match.group(1) if match else tool_dir.name
1610
+ return True, tool_name
1611
+ except Exception:
1612
+ return True, tool_dir.name
1613
+
1614
+ return False, None
1615
+
1616
+ def detect_uv_venv():
1617
+ """Detect if running in a UV-managed virtual environment."""
1618
+ # Check if we're in a venv
1619
+ if sys.prefix == sys.base_prefix:
1620
+ return False
1621
+
1622
+ # Check for UV markers in pyvenv.cfg
1623
+ pyvenv_cfg = Path(sys.prefix) / "pyvenv.cfg"
1624
+ if pyvenv_cfg.exists():
1625
+ try:
1626
+ with open(pyvenv_cfg) as f:
1627
+ content = f.read()
1628
+ if "uv" in content.lower() or "managed by uv" in content:
1629
+ return True
1630
+ except Exception:
1631
+ pass
1632
+
1633
+ return False
1634
+
1635
+ def check_uv_available():
1636
+ """Check if UV is available."""
1637
+ try:
1638
+ result = subprocess.run(
1639
+ ["uv", "--version"],
1640
+ capture_output=True,
1641
+ text=True,
1642
+ timeout=2
1643
+ )
1644
+ return result.returncode == 0
1645
+ except (FileNotFoundError, subprocess.TimeoutExpired):
1646
+ return False
1647
+
1648
+ # Get current version
1702
1649
  try:
1703
1650
  current_version = get_version("voice-mode")
1704
1651
  except PackageNotFoundError:
1705
1652
  current_version = "development"
1706
1653
 
1654
+ # Check if update needed (unless forced)
1707
1655
  if not force and current_version != "development":
1708
- # Check if update is needed
1709
1656
  try:
1710
1657
  response = requests.get(
1711
1658
  "https://pypi.org/pypi/voice-mode/json",
@@ -1717,58 +1664,82 @@ def update(force):
1717
1664
  click.echo(f"Already running the latest version ({current_version})")
1718
1665
  return
1719
1666
  except (requests.RequestException, KeyError, ValueError):
1720
- # Continue with update if we can't check
1721
- pass
1667
+ pass # Continue with update if we can't check
1722
1668
 
1723
- click.echo("Updating Voice Mode to the latest version...")
1669
+ # Detect installation method
1670
+ is_uv_tool, tool_name = detect_uv_tool_installation()
1724
1671
 
1725
- # Try UV first, fall back to pip
1726
- try:
1727
- # Check if UV is available
1672
+ if is_uv_tool:
1673
+ # UV tool installation - use uv tool upgrade
1674
+ click.echo(f"Updating Voice Mode (UV tool: {tool_name})...")
1675
+
1728
1676
  result = subprocess.run(
1729
- ["uv", "--version"],
1677
+ ["uv", "tool", "upgrade", tool_name],
1730
1678
  capture_output=True,
1731
- text=True,
1732
- check=False
1679
+ text=True
1733
1680
  )
1734
1681
 
1735
1682
  if result.returncode == 0:
1736
- # Use UV for update
1683
+ try:
1684
+ new_version = get_version("voice-mode")
1685
+ click.echo(f"✅ Successfully updated to version {new_version}")
1686
+ except PackageNotFoundError:
1687
+ click.echo("✅ Successfully updated Voice Mode")
1688
+ else:
1689
+ click.echo(f"❌ Update failed: {result.stderr}")
1690
+ click.echo(f"Try running manually: uv tool upgrade {tool_name}")
1691
+
1692
+ elif detect_uv_venv():
1693
+ # UV-managed virtual environment
1694
+ click.echo("Updating Voice Mode (UV virtual environment)...")
1695
+
1696
+ result = subprocess.run(
1697
+ ["uv", "pip", "install", "--upgrade", "voice-mode"],
1698
+ capture_output=True,
1699
+ text=True
1700
+ )
1701
+
1702
+ if result.returncode == 0:
1703
+ try:
1704
+ new_version = get_version("voice-mode")
1705
+ click.echo(f"✅ Successfully updated to version {new_version}")
1706
+ except PackageNotFoundError:
1707
+ click.echo("✅ Successfully updated Voice Mode")
1708
+ else:
1709
+ click.echo(f"❌ Update failed: {result.stderr}")
1710
+ click.echo("Try running: uv pip install --upgrade voice-mode")
1711
+
1712
+ else:
1713
+ # Standard installation - try UV if available, else pip
1714
+ has_uv = check_uv_available()
1715
+
1716
+ if has_uv:
1717
+ click.echo("Updating Voice Mode (using UV)...")
1737
1718
  result = subprocess.run(
1738
1719
  ["uv", "pip", "install", "--upgrade", "voice-mode"],
1739
1720
  capture_output=True,
1740
1721
  text=True
1741
1722
  )
1742
- if result.returncode == 0:
1743
- # Get new version
1744
- try:
1745
- new_version = get_version("voice-mode")
1746
- click.echo(f"✅ Successfully updated to version {new_version}")
1747
- except PackageNotFoundError:
1748
- click.echo("✅ Successfully updated Voice Mode")
1749
- else:
1750
- click.echo(f"❌ Update failed: {result.stderr}")
1751
- click.echo("Try running: uv pip install --upgrade voice-mode")
1752
1723
  else:
1753
- # Fall back to pip
1724
+ click.echo("Updating Voice Mode (using pip)...")
1754
1725
  result = subprocess.run(
1755
1726
  [sys.executable, "-m", "pip", "install", "--upgrade", "voice-mode"],
1756
1727
  capture_output=True,
1757
1728
  text=True
1758
1729
  )
1759
- if result.returncode == 0:
1760
- try:
1761
- new_version = get_version("voice-mode")
1762
- click.echo(f"✅ Successfully updated to version {new_version}")
1763
- except PackageNotFoundError:
1764
- click.echo("✅ Successfully updated Voice Mode")
1730
+
1731
+ if result.returncode == 0:
1732
+ try:
1733
+ new_version = get_version("voice-mode")
1734
+ click.echo(f"✅ Successfully updated to version {new_version}")
1735
+ except PackageNotFoundError:
1736
+ click.echo("✅ Successfully updated Voice Mode")
1737
+ else:
1738
+ click.echo(f"❌ Update failed: {result.stderr}")
1739
+ if has_uv:
1740
+ click.echo("Try running: uv pip install --upgrade voice-mode")
1765
1741
  else:
1766
- click.echo(f"❌ Update failed: {result.stderr}")
1767
1742
  click.echo("Try running: pip install --upgrade voice-mode")
1768
-
1769
- except FileNotFoundError as e:
1770
- click.echo(f"❌ Update failed: {e}")
1771
- click.echo("Please install UV or pip and try again")
1772
1743
 
1773
1744
 
1774
1745
  # Completions command
@@ -1 +1 @@
1
- 998kY2RB1umOQfi6UBnLh
1
+ QYw7BshkzP8SKP3LsaTD0
@@ -4,25 +4,25 @@
4
4
  "static/chunks/webpack-0ea9b80f19935b70.js",
5
5
  "static/chunks/fd9d1056-af324d327b243cf1.js",
6
6
  "static/chunks/117-40bc79a2b97edb21.js",
7
- "static/chunks/main-app-2233caae67bf73bd.js",
7
+ "static/chunks/main-app-b61926fb6b68dca5.js",
8
8
  "static/chunks/app/_not-found/page-5011050e402ab9c8.js"
9
9
  ],
10
10
  "/layout": [
11
11
  "static/chunks/webpack-0ea9b80f19935b70.js",
12
12
  "static/chunks/fd9d1056-af324d327b243cf1.js",
13
13
  "static/chunks/117-40bc79a2b97edb21.js",
14
- "static/chunks/main-app-2233caae67bf73bd.js",
14
+ "static/chunks/main-app-b61926fb6b68dca5.js",
15
15
  "static/css/a2f49a47752b5010.css",
16
- "static/chunks/app/layout-53ce2eb95847e7bc.js"
16
+ "static/chunks/app/layout-f2d1799422b78b51.js"
17
17
  ],
18
18
  "/page": [
19
19
  "static/chunks/webpack-0ea9b80f19935b70.js",
20
20
  "static/chunks/fd9d1056-af324d327b243cf1.js",
21
21
  "static/chunks/117-40bc79a2b97edb21.js",
22
- "static/chunks/main-app-2233caae67bf73bd.js",
22
+ "static/chunks/main-app-b61926fb6b68dca5.js",
23
23
  "static/chunks/144d3bae-2d5f122b82426d88.js",
24
24
  "static/chunks/471-bd4b96a33883dfa2.js",
25
- "static/chunks/app/page-9c7f7be4378697be.js"
25
+ "static/chunks/app/page-49b84bcf9a91501f.js"
26
26
  ]
27
27
  }
28
28
  }
@@ -5,14 +5,14 @@
5
5
  "devFiles": [],
6
6
  "ampDevFiles": [],
7
7
  "lowPriorityFiles": [
8
- "static/998kY2RB1umOQfi6UBnLh/_buildManifest.js",
9
- "static/998kY2RB1umOQfi6UBnLh/_ssgManifest.js"
8
+ "static/QYw7BshkzP8SKP3LsaTD0/_buildManifest.js",
9
+ "static/QYw7BshkzP8SKP3LsaTD0/_ssgManifest.js"
10
10
  ],
11
11
  "rootMainFiles": [
12
12
  "static/chunks/webpack-0ea9b80f19935b70.js",
13
13
  "static/chunks/fd9d1056-af324d327b243cf1.js",
14
14
  "static/chunks/117-40bc79a2b97edb21.js",
15
- "static/chunks/main-app-2233caae67bf73bd.js"
15
+ "static/chunks/main-app-b61926fb6b68dca5.js"
16
16
  ],
17
17
  "pages": {
18
18
  "/_app": [
@@ -1 +1 @@
1
- {"version":1,"files":["../node_modules/styled-jsx/index.js","../node_modules/styled-jsx/package.json","../node_modules/styled-jsx/dist/index/index.js","../node_modules/react/package.json","../node_modules/react/index.js","../node_modules/client-only/package.json","../node_modules/react/cjs/react.production.min.js","../node_modules/client-only/index.js","../node_modules/styled-jsx/style.js","../node_modules/next/dist/compiled/next-server/server.runtime.prod.js","../node_modules/next/package.json","../node_modules/next/dist/lib/constants.js","../node_modules/next/dist/server/body-streams.js","../node_modules/next/dist/lib/picocolors.js","../node_modules/next/dist/shared/lib/constants.js","../node_modules/next/dist/server/web/utils.js","../node_modules/next/dist/client/components/app-router-headers.js","../node_modules/next/dist/server/lib/trace/tracer.js","../node_modules/next/dist/server/lib/trace/constants.js","../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../node_modules/next/dist/shared/lib/error-source.js","../node_modules/next/dist/shared/lib/modern-browserslist-target.js","../node_modules/next/dist/compiled/debug/package.json","../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../node_modules/next/dist/shared/lib/runtime-config.external.js","../node_modules/next/dist/compiled/debug/index.js","../node_modules/next/dist/compiled/ws/package.json","../node_modules/next/dist/compiled/node-html-parser/package.json","../node_modules/next/dist/compiled/lru-cache/package.json","../node_modules/@swc/helpers/_/_interop_require_default/package.json","../node_modules/next/dist/compiled/ws/index.js","../node_modules/next/dist/compiled/node-html-parser/index.js","../node_modules/next/dist/compiled/lru-cache/index.js","../node_modules/next/dist/client/components/async-local-storage.js","../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../node_modules/@swc/helpers/package.json","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/parseStack.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/nodeStackFrames.js","../node_modules/next/dist/compiled/jsonwebtoken/package.json","../node_modules/next/dist/client/components/react-dev-overlay/server/middleware.js","../node_modules/@swc/helpers/cjs/_interop_require_default.cjs","../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../node_modules/next/dist/compiled/jsonwebtoken/index.js","../node_modules/next/dist/compiled/browserslist/package.json","../node_modules/next/dist/compiled/browserslist/index.js","../node_modules/next/dist/client/components/react-dev-overlay/server/shared.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/getRawSourceMap.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/launchEditor.js","../node_modules/next/dist/compiled/babel/code-frame.js","../node_modules/next/dist/compiled/json5/package.json","../node_modules/next/dist/compiled/semver/package.json","../node_modules/next/dist/compiled/babel/package.json","../node_modules/next/dist/lib/semver-noop.js","../node_modules/next/dist/compiled/json5/index.js","../node_modules/next/dist/compiled/semver/index.js","../node_modules/next/dist/compiled/stacktrace-parser/package.json","../node_modules/next/dist/compiled/source-map08/package.json","../node_modules/caniuse-lite/dist/unpacker/agents.js","../node_modules/caniuse-lite/dist/unpacker/region.js","../node_modules/caniuse-lite/dist/unpacker/feature.js","../node_modules/next/dist/compiled/babel/bundle.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/getSourceMapUrl.js","../node_modules/next/dist/compiled/stacktrace-parser/stack-trace-parser.cjs.js","../node_modules/next/dist/compiled/source-map08/source-map.js","../node_modules/caniuse-lite/package.json","../node_modules/next/dist/compiled/babel/core.js","../node_modules/caniuse-lite/data/agents.js","../node_modules/caniuse-lite/dist/unpacker/browserVersions.js","../node_modules/caniuse-lite/dist/unpacker/browsers.js","../node_modules/caniuse-lite/dist/lib/supported.js","../node_modules/caniuse-lite/dist/lib/statuses.js","../node_modules/next/dist/compiled/data-uri-to-buffer/package.json","../node_modules/next/dist/compiled/shell-quote/package.json","../node_modules/next/dist/compiled/data-uri-to-buffer/index.js","../node_modules/next/dist/compiled/shell-quote/index.js","../node_modules/caniuse-lite/data/browserVersions.js","../node_modules/caniuse-lite/data/browsers.js","../node_modules/next/dist/compiled/babel-packages/package.json","../node_modules/next/dist/compiled/babel-packages/packages-bundle.js","../node_modules/next/dist/compiled/babel/parser.js","../node_modules/next/dist/compiled/babel/traverse.js","../node_modules/next/dist/compiled/babel/types.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/app-page/module.compiled.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/pages/module.compiled.js"]}
1
+ {"version":1,"files":["../node_modules/styled-jsx/index.js","../node_modules/styled-jsx/package.json","../node_modules/styled-jsx/dist/index/index.js","../node_modules/react/package.json","../node_modules/react/index.js","../node_modules/client-only/package.json","../node_modules/react/cjs/react.production.min.js","../node_modules/client-only/index.js","../node_modules/styled-jsx/style.js","../node_modules/next/dist/compiled/next-server/server.runtime.prod.js","../node_modules/next/package.json","../node_modules/next/dist/lib/constants.js","../node_modules/next/dist/server/body-streams.js","../node_modules/next/dist/lib/picocolors.js","../node_modules/next/dist/shared/lib/constants.js","../node_modules/next/dist/server/web/utils.js","../node_modules/next/dist/client/components/app-router-headers.js","../node_modules/next/dist/server/lib/trace/tracer.js","../node_modules/next/dist/server/lib/trace/constants.js","../node_modules/next/dist/client/components/static-generation-async-storage.external.js","../node_modules/next/dist/shared/lib/error-source.js","../node_modules/next/dist/shared/lib/modern-browserslist-target.js","../node_modules/next/dist/compiled/debug/package.json","../node_modules/next/dist/client/components/static-generation-async-storage-instance.js","../node_modules/next/dist/shared/lib/runtime-config.external.js","../node_modules/next/dist/compiled/debug/index.js","../node_modules/next/dist/compiled/ws/package.json","../node_modules/next/dist/compiled/node-html-parser/package.json","../node_modules/next/dist/compiled/lru-cache/package.json","../node_modules/@swc/helpers/_/_interop_require_default/package.json","../node_modules/next/dist/compiled/ws/index.js","../node_modules/next/dist/compiled/node-html-parser/index.js","../node_modules/next/dist/compiled/lru-cache/index.js","../node_modules/next/dist/client/components/async-local-storage.js","../node_modules/next/dist/compiled/@opentelemetry/api/package.json","../node_modules/@swc/helpers/package.json","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/parseStack.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/nodeStackFrames.js","../node_modules/next/dist/compiled/jsonwebtoken/package.json","../node_modules/next/dist/client/components/react-dev-overlay/server/middleware.js","../node_modules/@swc/helpers/cjs/_interop_require_default.cjs","../node_modules/next/dist/compiled/@opentelemetry/api/index.js","../node_modules/next/dist/compiled/jsonwebtoken/index.js","../node_modules/next/dist/compiled/browserslist/package.json","../node_modules/next/dist/compiled/browserslist/index.js","../node_modules/next/dist/client/components/react-dev-overlay/server/shared.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/getRawSourceMap.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/launchEditor.js","../node_modules/next/dist/compiled/babel/code-frame.js","../node_modules/next/dist/compiled/json5/package.json","../node_modules/next/dist/compiled/semver/package.json","../node_modules/next/dist/compiled/babel/package.json","../node_modules/next/dist/lib/semver-noop.js","../node_modules/next/dist/compiled/json5/index.js","../node_modules/next/dist/compiled/semver/index.js","../node_modules/next/dist/compiled/stacktrace-parser/package.json","../node_modules/next/dist/compiled/source-map08/package.json","../node_modules/caniuse-lite/dist/unpacker/agents.js","../node_modules/caniuse-lite/dist/unpacker/feature.js","../node_modules/caniuse-lite/dist/unpacker/region.js","../node_modules/next/dist/compiled/babel/bundle.js","../node_modules/next/dist/client/components/react-dev-overlay/internal/helpers/getSourceMapUrl.js","../node_modules/next/dist/compiled/stacktrace-parser/stack-trace-parser.cjs.js","../node_modules/next/dist/compiled/source-map08/source-map.js","../node_modules/caniuse-lite/package.json","../node_modules/next/dist/compiled/babel/core.js","../node_modules/caniuse-lite/data/agents.js","../node_modules/caniuse-lite/dist/lib/statuses.js","../node_modules/caniuse-lite/dist/lib/supported.js","../node_modules/caniuse-lite/dist/unpacker/browsers.js","../node_modules/caniuse-lite/dist/unpacker/browserVersions.js","../node_modules/next/dist/compiled/data-uri-to-buffer/package.json","../node_modules/next/dist/compiled/shell-quote/package.json","../node_modules/next/dist/compiled/data-uri-to-buffer/index.js","../node_modules/next/dist/compiled/shell-quote/index.js","../node_modules/caniuse-lite/data/browsers.js","../node_modules/caniuse-lite/data/browserVersions.js","../node_modules/next/dist/compiled/babel-packages/package.json","../node_modules/next/dist/compiled/babel-packages/packages-bundle.js","../node_modules/next/dist/compiled/babel/parser.js","../node_modules/next/dist/compiled/babel/traverse.js","../node_modules/next/dist/compiled/babel/types.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/app-page/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/app-page/module.compiled.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/amp-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/app-router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/entrypoints.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/head-manager-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/hooks-client-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/html-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/image-config-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/loadable.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/router-context.js","../node_modules/next/dist/server/future/route-modules/pages/vendored/contexts/server-inserted-html.js","../node_modules/next/dist/server/future/route-modules/pages/module.compiled.js"]}