voice-mode-install 7.4.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.
- voice_mode_install-7.4.0.dist-info/METADATA +192 -0
- voice_mode_install-7.4.0.dist-info/RECORD +12 -0
- voice_mode_install-7.4.0.dist-info/WHEEL +4 -0
- voice_mode_install-7.4.0.dist-info/entry_points.txt +2 -0
- voicemode_install/__init__.py +8 -0
- voicemode_install/checker.py +200 -0
- voicemode_install/cli.py +478 -0
- voicemode_install/dependencies.yaml +367 -0
- voicemode_install/hardware.py +92 -0
- voicemode_install/installer.py +150 -0
- voicemode_install/logger.py +90 -0
- voicemode_install/system.py +146 -0
voicemode_install/cli.py
ADDED
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
"""Main CLI for VoiceMode installer."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from . import __version__
|
|
13
|
+
from .checker import DependencyChecker
|
|
14
|
+
from .hardware import HardwareInfo
|
|
15
|
+
from .installer import PackageInstaller
|
|
16
|
+
from .logger import InstallLogger
|
|
17
|
+
from .system import detect_platform, get_system_info, check_command_exists, check_homebrew_installed
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
LOGO = """
|
|
21
|
+
╔════════════════════════════════════════════╗
|
|
22
|
+
║ ║
|
|
23
|
+
║ ██╗ ██╗ ██████╗ ██╗ ██████╗███████╗ ║
|
|
24
|
+
║ ██║ ██║██╔═══██╗██║██╔════╝██╔════╝ ║
|
|
25
|
+
║ ██║ ██║██║ ██║██║██║ █████╗ ║
|
|
26
|
+
║ ╚██╗ ██╔╝██║ ██║██║██║ ██╔══╝ ║
|
|
27
|
+
║ ╚████╔╝ ╚██████╔╝██║╚██████╗███████╗ ║
|
|
28
|
+
║ ╚═══╝ ╚═════╝ ╚═╝ ╚═════╝╚══════╝ ║
|
|
29
|
+
║ ║
|
|
30
|
+
║ ███╗ ███╗ ██████╗ ██████╗ ███████╗ ║
|
|
31
|
+
║ ████╗ ████║██╔═══██╗██╔══██╗██╔════╝ ║
|
|
32
|
+
║ ██╔████╔██║██║ ██║██║ ██║█████╗ ║
|
|
33
|
+
║ ██║╚██╔╝██║██║ ██║██║ ██║██╔══╝ ║
|
|
34
|
+
║ ██║ ╚═╝ ██║╚██████╔╝██████╔╝███████╗ ║
|
|
35
|
+
║ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝ ║
|
|
36
|
+
║ ║
|
|
37
|
+
║ VoiceMode Installer ║
|
|
38
|
+
║ ║
|
|
39
|
+
╚════════════════════════════════════════════╝
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def print_logo():
|
|
44
|
+
"""Display the VoiceMode logo in Claude Code orange."""
|
|
45
|
+
# Use ANSI 256-color code 208 (dark orange) which matches Claude Code orange (RGB 208, 128, 0)
|
|
46
|
+
# This works on xterm-256color and other 256-color terminals
|
|
47
|
+
click.echo('\033[38;5;208m' + '\033[1m' + LOGO + '\033[0m')
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def print_step(message: str):
|
|
51
|
+
"""Print a step message."""
|
|
52
|
+
click.echo(click.style(f"🔧 {message}", fg='blue'))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def print_success(message: str):
|
|
56
|
+
"""Print a success message."""
|
|
57
|
+
click.echo(click.style(f"✅ {message}", fg='green'))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def print_warning(message: str):
|
|
61
|
+
"""Print a warning message in Claude Code orange."""
|
|
62
|
+
# Use ANSI 256-color code 208 (dark orange)
|
|
63
|
+
click.echo('\033[38;5;208m' + f"⚠️ {message}" + '\033[0m')
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def print_error(message: str):
|
|
67
|
+
"""Print an error message."""
|
|
68
|
+
click.echo(click.style(f"❌ {message}", fg='red'))
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def get_installed_version() -> str | None:
|
|
72
|
+
"""Get the currently installed VoiceMode version."""
|
|
73
|
+
try:
|
|
74
|
+
result = subprocess.run(
|
|
75
|
+
['voicemode', '--version'],
|
|
76
|
+
capture_output=True,
|
|
77
|
+
text=True,
|
|
78
|
+
timeout=5
|
|
79
|
+
)
|
|
80
|
+
if result.returncode == 0:
|
|
81
|
+
# Output is like "VoiceMode version 6.0.1" or just "6.0.1"
|
|
82
|
+
version = result.stdout.strip().split()[-1]
|
|
83
|
+
return version
|
|
84
|
+
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired):
|
|
85
|
+
pass
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_latest_version() -> str | None:
|
|
90
|
+
"""Get the latest VoiceMode version from PyPI."""
|
|
91
|
+
try:
|
|
92
|
+
# Use PyPI JSON API to get latest version
|
|
93
|
+
result = subprocess.run(
|
|
94
|
+
['curl', '-s', 'https://pypi.org/pypi/voice-mode/json'],
|
|
95
|
+
capture_output=True,
|
|
96
|
+
text=True,
|
|
97
|
+
timeout=10
|
|
98
|
+
)
|
|
99
|
+
if result.returncode == 0:
|
|
100
|
+
data = json.loads(result.stdout)
|
|
101
|
+
return data['info']['version']
|
|
102
|
+
except (subprocess.CalledProcessError, FileNotFoundError, subprocess.TimeoutExpired, json.JSONDecodeError, KeyError):
|
|
103
|
+
pass
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def check_existing_installation() -> bool:
|
|
108
|
+
"""Check if VoiceMode is already installed."""
|
|
109
|
+
return check_command_exists('voicemode')
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def ensure_homebrew_on_macos(platform_info, dry_run: bool, non_interactive: bool) -> bool:
|
|
113
|
+
"""
|
|
114
|
+
Ensure Homebrew is installed on macOS before checking dependencies.
|
|
115
|
+
|
|
116
|
+
Returns True if Homebrew is available or successfully installed, False otherwise.
|
|
117
|
+
"""
|
|
118
|
+
# Only needed on macOS
|
|
119
|
+
if platform_info.distribution != 'darwin':
|
|
120
|
+
return True
|
|
121
|
+
|
|
122
|
+
# Check if already installed
|
|
123
|
+
if check_homebrew_installed():
|
|
124
|
+
return True
|
|
125
|
+
|
|
126
|
+
# Not installed
|
|
127
|
+
print_warning("Homebrew is not installed")
|
|
128
|
+
click.echo("Homebrew is the package manager required to install system dependencies on macOS.")
|
|
129
|
+
click.echo("Visit: https://brew.sh")
|
|
130
|
+
click.echo()
|
|
131
|
+
|
|
132
|
+
if dry_run:
|
|
133
|
+
print_step("[DRY RUN] Would install Homebrew (macOS package manager)")
|
|
134
|
+
return True
|
|
135
|
+
|
|
136
|
+
if non_interactive:
|
|
137
|
+
# Auto-install Homebrew in non-interactive mode using NONINTERACTIVE=1
|
|
138
|
+
print_step("Installing Homebrew (non-interactive)...")
|
|
139
|
+
else:
|
|
140
|
+
# Prompt user
|
|
141
|
+
if not click.confirm("Install Homebrew now?", default=True):
|
|
142
|
+
print_error("Homebrew installation declined")
|
|
143
|
+
click.echo("Please install Homebrew manually and run the installer again.")
|
|
144
|
+
return False
|
|
145
|
+
print_step("Installing Homebrew...")
|
|
146
|
+
click.echo("This may take a few minutes and will require your password.")
|
|
147
|
+
|
|
148
|
+
click.echo()
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
# Use NONINTERACTIVE=1 for unattended installation
|
|
152
|
+
env = os.environ.copy()
|
|
153
|
+
if non_interactive:
|
|
154
|
+
env['NONINTERACTIVE'] = '1'
|
|
155
|
+
install_script = '/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"'
|
|
156
|
+
result = subprocess.run(install_script, shell=True, check=True, env=env)
|
|
157
|
+
|
|
158
|
+
if result.returncode == 0:
|
|
159
|
+
print_success("Homebrew installed successfully")
|
|
160
|
+
|
|
161
|
+
# Verify
|
|
162
|
+
if check_homebrew_installed():
|
|
163
|
+
return True
|
|
164
|
+
else:
|
|
165
|
+
print_warning("Homebrew was installed but 'brew' command not found in PATH")
|
|
166
|
+
click.echo("You may need to add Homebrew to your PATH. Check the installation output above.")
|
|
167
|
+
return False
|
|
168
|
+
else:
|
|
169
|
+
print_error("Homebrew installation returned non-zero exit code")
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
except subprocess.CalledProcessError as e:
|
|
173
|
+
print_error(f"Error installing Homebrew: {e}")
|
|
174
|
+
return False
|
|
175
|
+
except Exception as e:
|
|
176
|
+
print_error(f"Unexpected error installing Homebrew: {e}")
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
EPILOG = """
|
|
181
|
+
\b
|
|
182
|
+
Examples:
|
|
183
|
+
# Normal installation
|
|
184
|
+
voice-mode-install
|
|
185
|
+
|
|
186
|
+
# Non-interactive installation (auto-accept all prompts)
|
|
187
|
+
voice-mode-install --yes
|
|
188
|
+
voice-mode-install -y
|
|
189
|
+
|
|
190
|
+
# Dry run (see what would be installed)
|
|
191
|
+
voice-mode-install --dry-run
|
|
192
|
+
|
|
193
|
+
# Install specific version
|
|
194
|
+
voice-mode-install --voice-mode-version=5.1.3
|
|
195
|
+
|
|
196
|
+
# Skip service installation
|
|
197
|
+
voice-mode-install --skip-services
|
|
198
|
+
|
|
199
|
+
# Install with specific Whisper model
|
|
200
|
+
voice-mode-install --yes --model large-v2
|
|
201
|
+
"""
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
@click.command(epilog=EPILOG, context_settings={'help_option_names': ['-h', '--help']})
|
|
205
|
+
@click.option('-d', '--dry-run', is_flag=True, help='Show what would be installed without installing')
|
|
206
|
+
@click.option('-v', '--voice-mode-version', default=None, help='Specific VoiceMode version to install')
|
|
207
|
+
@click.option('-s', '--skip-services', is_flag=True, help='Skip local service installation')
|
|
208
|
+
@click.option('-y', '--yes', 'non_interactive', is_flag=True, help='Run without prompts (auto-accept all)')
|
|
209
|
+
@click.option('-n', '--non-interactive', is_flag=True, help='Run without prompts (deprecated: use --yes/-y)')
|
|
210
|
+
@click.option('-m', '--model', default='base', help='Whisper model to use (base, small, medium, large-v2)')
|
|
211
|
+
@click.version_option(__version__, '-V', '--version')
|
|
212
|
+
def main(dry_run, voice_mode_version, skip_services, non_interactive, model):
|
|
213
|
+
"""VoiceMode Installer - Install VoiceMode and its system dependencies.
|
|
214
|
+
|
|
215
|
+
This installer will:
|
|
216
|
+
|
|
217
|
+
1. Detect your operating system and architecture
|
|
218
|
+
|
|
219
|
+
2. Check for missing system dependencies
|
|
220
|
+
|
|
221
|
+
3. Install required packages (with your permission)
|
|
222
|
+
|
|
223
|
+
4. Install VoiceMode using uv
|
|
224
|
+
|
|
225
|
+
5. Optionally install local voice services
|
|
226
|
+
|
|
227
|
+
6. Configure shell completions
|
|
228
|
+
|
|
229
|
+
7. Verify the installation
|
|
230
|
+
"""
|
|
231
|
+
# Detect non-interactive environment (no TTY)
|
|
232
|
+
if not sys.stdin.isatty() and not non_interactive and not dry_run:
|
|
233
|
+
click.echo("Error: Running in non-interactive environment without --yes flag", err=True)
|
|
234
|
+
click.echo("Use --yes or -y to enable automatic installation", err=True)
|
|
235
|
+
click.echo("Example: uvx voice-mode-install --yes", err=True)
|
|
236
|
+
sys.exit(1)
|
|
237
|
+
|
|
238
|
+
# Initialize logger
|
|
239
|
+
logger = InstallLogger()
|
|
240
|
+
|
|
241
|
+
try:
|
|
242
|
+
# Clear screen and show logo
|
|
243
|
+
if not dry_run:
|
|
244
|
+
click.clear()
|
|
245
|
+
print_logo()
|
|
246
|
+
click.echo()
|
|
247
|
+
|
|
248
|
+
if dry_run:
|
|
249
|
+
click.echo(click.style("DRY RUN MODE - No changes will be made", fg='yellow', bold=True))
|
|
250
|
+
click.echo()
|
|
251
|
+
|
|
252
|
+
# Detect platform
|
|
253
|
+
print_step("Detecting platform...")
|
|
254
|
+
platform_info = detect_platform()
|
|
255
|
+
system_info = get_system_info()
|
|
256
|
+
|
|
257
|
+
logger.log_start(system_info)
|
|
258
|
+
|
|
259
|
+
click.echo(f"Detected: {platform_info.os_name} ({platform_info.architecture})")
|
|
260
|
+
if platform_info.is_wsl:
|
|
261
|
+
print_warning("WSL detected - additional audio configuration may be needed")
|
|
262
|
+
click.echo()
|
|
263
|
+
|
|
264
|
+
# Ensure Homebrew is installed on macOS (before checking dependencies)
|
|
265
|
+
if not ensure_homebrew_on_macos(platform_info, dry_run, non_interactive):
|
|
266
|
+
logger.log_error("Homebrew installation required but not available")
|
|
267
|
+
sys.exit(1)
|
|
268
|
+
|
|
269
|
+
# Check for existing installation
|
|
270
|
+
if check_existing_installation():
|
|
271
|
+
installed_version = get_installed_version()
|
|
272
|
+
latest_version = get_latest_version()
|
|
273
|
+
|
|
274
|
+
click.echo(click.style("✓ VoiceMode is currently installed", fg='green'))
|
|
275
|
+
|
|
276
|
+
if installed_version:
|
|
277
|
+
click.echo(f" Installed version: {installed_version}")
|
|
278
|
+
else:
|
|
279
|
+
click.echo(" Installed version: (unable to detect)")
|
|
280
|
+
|
|
281
|
+
if latest_version:
|
|
282
|
+
click.echo(f" Latest version: {latest_version}")
|
|
283
|
+
|
|
284
|
+
# Check if update is available
|
|
285
|
+
if installed_version and latest_version and installed_version != latest_version:
|
|
286
|
+
click.echo()
|
|
287
|
+
if non_interactive:
|
|
288
|
+
print_step("Upgrading VoiceMode...")
|
|
289
|
+
elif not click.confirm(f"Upgrade to version {latest_version}?", default=True):
|
|
290
|
+
click.echo("\nTo upgrade manually later, run: uv tool install --upgrade voice-mode")
|
|
291
|
+
sys.exit(0)
|
|
292
|
+
elif installed_version and latest_version and installed_version == latest_version:
|
|
293
|
+
click.echo()
|
|
294
|
+
click.echo(click.style("✓ VoiceMode is up-to-date", fg='green'))
|
|
295
|
+
if non_interactive:
|
|
296
|
+
click.echo("Reinstalling...")
|
|
297
|
+
elif not click.confirm("Reinstall anyway?", default=False):
|
|
298
|
+
click.echo("\nInstallation cancelled.")
|
|
299
|
+
sys.exit(0)
|
|
300
|
+
else:
|
|
301
|
+
click.echo()
|
|
302
|
+
if not non_interactive:
|
|
303
|
+
if not click.confirm("Reinstall VoiceMode?", default=False):
|
|
304
|
+
click.echo("\nTo upgrade manually, run: uv tool install --upgrade voice-mode")
|
|
305
|
+
sys.exit(0)
|
|
306
|
+
else:
|
|
307
|
+
click.echo(" Latest version: (unable to check)")
|
|
308
|
+
click.echo()
|
|
309
|
+
if not non_interactive:
|
|
310
|
+
if not click.confirm("Reinstall/upgrade VoiceMode?", default=False):
|
|
311
|
+
click.echo("\nTo upgrade manually, run: uv tool install --upgrade voice-mode")
|
|
312
|
+
sys.exit(0)
|
|
313
|
+
|
|
314
|
+
click.echo()
|
|
315
|
+
|
|
316
|
+
# Check dependencies
|
|
317
|
+
print_step("Checking system dependencies...")
|
|
318
|
+
checker = DependencyChecker(platform_info)
|
|
319
|
+
core_deps = checker.check_core_dependencies()
|
|
320
|
+
|
|
321
|
+
missing_deps = checker.get_missing_packages(core_deps)
|
|
322
|
+
summary = checker.get_summary(core_deps)
|
|
323
|
+
|
|
324
|
+
logger.log_check('core', summary['installed'], summary['missing_required'])
|
|
325
|
+
|
|
326
|
+
# Display summary
|
|
327
|
+
click.echo()
|
|
328
|
+
click.echo("System Dependencies:")
|
|
329
|
+
for pkg in core_deps:
|
|
330
|
+
if pkg.required:
|
|
331
|
+
status = "✓" if pkg.installed else "✗"
|
|
332
|
+
color = "green" if pkg.installed else "red"
|
|
333
|
+
click.echo(f" {click.style(status, fg=color)} {pkg.name} - {pkg.description}")
|
|
334
|
+
|
|
335
|
+
click.echo()
|
|
336
|
+
|
|
337
|
+
# Install missing dependencies
|
|
338
|
+
if missing_deps:
|
|
339
|
+
print_warning(f"Missing {len(missing_deps)} required package(s)")
|
|
340
|
+
|
|
341
|
+
missing_names = [pkg.name for pkg in missing_deps]
|
|
342
|
+
click.echo(f"\nPackages to install: {', '.join(missing_names)}")
|
|
343
|
+
|
|
344
|
+
if not non_interactive and not dry_run:
|
|
345
|
+
if not click.confirm("\nInstall missing dependencies?", default=True):
|
|
346
|
+
print_error("Cannot proceed without required dependencies")
|
|
347
|
+
sys.exit(1)
|
|
348
|
+
|
|
349
|
+
installer = PackageInstaller(platform_info, dry_run=dry_run, non_interactive=non_interactive)
|
|
350
|
+
if installer.install_packages(missing_deps):
|
|
351
|
+
print_success("System dependencies installed")
|
|
352
|
+
logger.log_install('system', missing_names, True)
|
|
353
|
+
else:
|
|
354
|
+
print_error("Failed to install some dependencies")
|
|
355
|
+
logger.log_install('system', missing_names, False)
|
|
356
|
+
if not dry_run:
|
|
357
|
+
sys.exit(1)
|
|
358
|
+
else:
|
|
359
|
+
print_success("All required dependencies are already installed")
|
|
360
|
+
|
|
361
|
+
click.echo()
|
|
362
|
+
|
|
363
|
+
# Install VoiceMode
|
|
364
|
+
print_step("Installing VoiceMode...")
|
|
365
|
+
installer = PackageInstaller(platform_info, dry_run=dry_run, non_interactive=non_interactive)
|
|
366
|
+
|
|
367
|
+
if installer.install_voicemode(version=voice_mode_version):
|
|
368
|
+
print_success("VoiceMode installed successfully")
|
|
369
|
+
logger.log_install('voicemode', ['voice-mode'], True)
|
|
370
|
+
else:
|
|
371
|
+
print_error("Failed to install VoiceMode")
|
|
372
|
+
logger.log_install('voicemode', ['voice-mode'], False)
|
|
373
|
+
if not dry_run:
|
|
374
|
+
sys.exit(1)
|
|
375
|
+
|
|
376
|
+
click.echo()
|
|
377
|
+
|
|
378
|
+
# Health check
|
|
379
|
+
if not dry_run:
|
|
380
|
+
print_step("Verifying installation...")
|
|
381
|
+
voicemode_path = shutil.which('voicemode')
|
|
382
|
+
if voicemode_path:
|
|
383
|
+
print_success(f"VoiceMode command found: {voicemode_path}")
|
|
384
|
+
|
|
385
|
+
# Test that it works
|
|
386
|
+
try:
|
|
387
|
+
result = subprocess.run(
|
|
388
|
+
['voicemode', '--version'],
|
|
389
|
+
capture_output=True,
|
|
390
|
+
text=True,
|
|
391
|
+
timeout=5
|
|
392
|
+
)
|
|
393
|
+
if result.returncode == 0:
|
|
394
|
+
print_success(f"VoiceMode version: {result.stdout.strip()}")
|
|
395
|
+
else:
|
|
396
|
+
print_warning("VoiceMode command exists but may not be working correctly")
|
|
397
|
+
except Exception as e:
|
|
398
|
+
print_warning(f"Could not verify VoiceMode: {e}")
|
|
399
|
+
else:
|
|
400
|
+
print_warning("VoiceMode command not immediately available in PATH")
|
|
401
|
+
click.echo("You may need to restart your shell or run:")
|
|
402
|
+
click.echo(" source ~/.bashrc # or your shell's rc file")
|
|
403
|
+
|
|
404
|
+
# Shell completion setup
|
|
405
|
+
if not dry_run:
|
|
406
|
+
print_step("Setting up shell completion...")
|
|
407
|
+
shell = Path.home() / '.bashrc' # Simplified for now
|
|
408
|
+
if shell.exists():
|
|
409
|
+
print_success("Shell completion configured")
|
|
410
|
+
else:
|
|
411
|
+
print_warning("Could not configure shell completion automatically")
|
|
412
|
+
|
|
413
|
+
# Hardware recommendations for services
|
|
414
|
+
if not skip_services and not dry_run:
|
|
415
|
+
click.echo()
|
|
416
|
+
click.echo("━" * 70)
|
|
417
|
+
click.echo(click.style("Local Voice Services", fg='blue', bold=True))
|
|
418
|
+
click.echo("━" * 70)
|
|
419
|
+
click.echo()
|
|
420
|
+
|
|
421
|
+
hardware = HardwareInfo(platform_info)
|
|
422
|
+
click.echo(hardware.get_recommendation_message())
|
|
423
|
+
click.echo()
|
|
424
|
+
click.echo(f"Estimated download size: {hardware.get_download_estimate()}")
|
|
425
|
+
click.echo()
|
|
426
|
+
|
|
427
|
+
if hardware.should_recommend_local_services():
|
|
428
|
+
if non_interactive or click.confirm("Install local voice services now?", default=True):
|
|
429
|
+
model_flag = f" --model {model}" if model != 'base' else ''
|
|
430
|
+
click.echo("\nLocal services can be installed with:")
|
|
431
|
+
click.echo(f" voicemode whisper install{model_flag}")
|
|
432
|
+
click.echo(" voicemode kokoro install")
|
|
433
|
+
click.echo("\nRun these commands after the installer completes.")
|
|
434
|
+
if non_interactive:
|
|
435
|
+
click.echo(f"\nNote: Whisper model '{model}' will be used (set via --model flag)")
|
|
436
|
+
else:
|
|
437
|
+
click.echo("Cloud services recommended for your system configuration.")
|
|
438
|
+
click.echo("Local services can still be installed if desired:")
|
|
439
|
+
model_flag = f" --model {model}" if model != 'base' else ''
|
|
440
|
+
click.echo(f" voicemode whisper install{model_flag}")
|
|
441
|
+
click.echo(" voicemode kokoro install")
|
|
442
|
+
|
|
443
|
+
# Completion summary
|
|
444
|
+
click.echo()
|
|
445
|
+
click.echo("━" * 70)
|
|
446
|
+
click.echo(click.style("Installation Complete!", fg='green', bold=True))
|
|
447
|
+
click.echo("━" * 70)
|
|
448
|
+
click.echo()
|
|
449
|
+
|
|
450
|
+
logger.log_complete(success=True, voicemode_installed=True)
|
|
451
|
+
|
|
452
|
+
if dry_run:
|
|
453
|
+
click.echo("DRY RUN: No changes were made to your system")
|
|
454
|
+
else:
|
|
455
|
+
click.echo("VoiceMode has been successfully installed!")
|
|
456
|
+
click.echo()
|
|
457
|
+
click.echo("Next steps:")
|
|
458
|
+
click.echo(" 1. Restart your terminal (or source your shell rc file)")
|
|
459
|
+
click.echo(" 2. Run: voicemode --help")
|
|
460
|
+
click.echo(" 3. Configure with Claude Code:")
|
|
461
|
+
click.echo(" claude mcp add --scope user voicemode -- uvx voice-mode")
|
|
462
|
+
click.echo()
|
|
463
|
+
click.echo(f"Installation log: {logger.get_log_path()}")
|
|
464
|
+
|
|
465
|
+
except KeyboardInterrupt:
|
|
466
|
+
click.echo("\n\nInstallation cancelled by user")
|
|
467
|
+
logger.log_error("Installation cancelled by user")
|
|
468
|
+
sys.exit(130)
|
|
469
|
+
except Exception as e:
|
|
470
|
+
print_error(f"Installation failed: {e}")
|
|
471
|
+
logger.log_error("Installation failed", e)
|
|
472
|
+
if not dry_run:
|
|
473
|
+
click.echo(f"\nFor troubleshooting, see: {logger.get_log_path()}")
|
|
474
|
+
sys.exit(1)
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
if __name__ == '__main__':
|
|
478
|
+
main()
|