Skip to content
Back to skills

2043 Download Progress Spec 63bb20a0

ASecurity

Standardize download progress indicators across VoiceMode CLI for consistent UX when downloading models, services, or other resources.

  • 9 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 11, 2026
toolspythongobashtesting

Works with

  • terminal
  • cli

Security analysis

A100/100

Scanned October 11, 2026

npx -y skills add tools-only/X-Skills --skill 2043-download-progress-spec_63bb20a0 --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 2043 Download Progress Spec 63bb20a0?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 2043 Download Progress Spec 63bb20a0
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tools-only-2043-download-progress-spec-63bb20a0/badge)](https://www.skillsdirectory.com/skills/tools-only-2043-download-progress-spec-63bb20a0)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
# Download Progress Indicator Specification

## Overview

Standardize download progress indicators across VoiceMode CLI for consistent UX when downloading models, services, or other resources.

## Design Principles

1. **Informative**: Show progress, speed, time remaining
2. **Consistent**: Same style across all downloads
3. **Responsive**: Update smoothly without flickering
4. **Accessible**: Work in different terminal environments
5. **Graceful**: Handle unknown file sizes elegantly

## Progress Bar Options

### Option 1: Click Progress Bar (Recommended)
Using Click's built-in ProgressBar - integrates well with our CLI framework.

```
Downloading ggml-large-v3.bin (3.1 GB)
[████████████████████░░░░░░░░░░░░░░░░]  45% (1.4GB/3.1GB) • 12.5 MB/s • ETA: 02:16
```

**Pros:**
- Built into Click (no new dependencies)
- Automatic terminal width detection
- Handles ANSI colors well
- Can customize format easily

**Code example:**
```python
import click

with click.progressbar(
    length=total_size,
    label='Downloading ggml-large-v3.bin',
    show_eta=True,
    show_percent=True,
    show_pos=True,
    width=40,
    fill_char='█',
    empty_char='░'
) as bar:
    # Update during download
    bar.update(chunk_size)
```

### Option 2: Rich Progress Bar
Using the Rich library for beautiful terminal output.

```
Downloading Core ML model
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
ggml-large-v3-encoder.mlmodelc.zip
1.2 GB / 1.2 GB • 15.3 MB/s • ✓ Complete
```

**Pros:**
- Beautiful, modern appearance
- Multiple progress bars simultaneously
- Rich formatting options
- Spinners for indeterminate progress

**Cons:**
- Adds Rich as dependency (~500KB)

### Option 3: Custom Minimal Progress
Simple, dependency-free implementation.

```
Downloading: ggml-base.bin
[===========         ] 55% | 78.1 MB / 142 MB | 8.2 MB/s | ~8s remaining
```

**Code example:**
```python
def print_progress(downloaded, total, speed):
    percent = (downloaded / total) * 100
    bar_length = 20
    filled = int(bar_length * downloaded / total)
    bar = '=' * filled + ' ' * (bar_length - filled)

    print(f'\r[{bar}] {percent:.0f}% | {downloaded/1024/1024:.1f} MB / {total/1024/1024:.1f} MB | {speed/1024/1024:.1f} MB/s', end='', flush=True)
```

### Option 4: Spinner for Unknown Size
When Content-Length header is missing:

```
Downloading model ⠼ 45.2 MB downloaded (5.3 MB/s)
```

Spinner frames: `⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏`

### Option 5: Block Progress
Visual blocks that fill up (good for accessibility):

```
Downloading ggml-small.bin (466 MB)
Progress: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░ 45%
Speed: 10.2 MB/s | Downloaded: 210 MB | Remaining: ~25s
```

### Option 6: Verbose Mode (Debug/CI)
For CI environments or debug mode:

```
[2024-10-01 13:45:23] Starting download: ggml-large-v3.bin
[2024-10-01 13:45:24] Progress: 5% (155 MB / 3100 MB) - 12.3 MB/s
[2024-10-01 13:45:25] Progress: 10% (310 MB / 3100 MB) - 13.1 MB/s
[2024-10-01 13:45:26] Progress: 15% (465 MB / 3100 MB) - 12.8 MB/s
```

## Implementation Strategy

### 1. Create Unified Download Function
```python
# voice_mode/utils/download.py
async def download_with_progress(
    url: str,
    destination: Path,
    description: str = None,
    style: str = "auto",  # auto, bar, spinner, blocks, verbose
    quiet: bool = False
) -> bool:
    """
    Download file with progress indicator.

    Args:
        url: URL to download from
        destination: Where to save the file
        description: Label for the progress bar
        style: Progress indicator style
        quiet: Suppress all output

    Returns:
        True if successful, False otherwise
    """
```

### 2. Environment Detection
```python
def detect_progress_style():
    """Auto-detect best progress style based on environment."""
    if os.environ.get('CI'):
        return 'verbose'  # CI environment
    if not sys.stdout.isatty():
        return 'verbose'  # Not a terminal
    if os.environ.get('VOICEMODE_PROGRESS_STYLE'):
        return os.environ['VOICEMODE_PROGRESS_STYLE']
    return 'bar'  # Default to progress bar
```

### 3. Integration Points

**Whisper Models:**
```python
# voice_mode/utils/services/whisper_helpers.py
async def download_whisper_model(...):
    await download_with_progress(
        url=model_url,
        destination=model_path,
        description=f"Downloading {model_name}"
    )
```

**Core ML Models:**
```python
# voice_mode/utils/services/whisper_helpers.py
async def download_coreml_model(...):
    await download_with_progress(
        url=coreml_url,
        destination=coreml_zip,
        description=f"Downloading Core ML model for {model}"
    )
```

**Service Installations:**
```python
# When downloading Whisper.cpp releases, Kokoro, etc.
```

## User Configuration

### Environment Variables
```bash
# Progress bar style
export VOICEMODE_PROGRESS_STYLE=bar  # bar, spinner, blocks, verbose, quiet

# Colors
export VOICEMODE_PROGRESS_COLOR=true  # Enable/disable colors

# Update frequency
export VOICEMODE_PROGRESS_RATE=10  # Updates per second
```

### CLI Flags
```bash
# Global flags
voicemode --progress=verbose whisper model install large-v3
voicemode --quiet whisper model install base

# Command-specific
voicemode whisper model install large-v3 --no-progress
voicemode whisper model install large-v3 --progress-style=blocks
```

## Error Handling

### Network Interruption
```
Downloading ggml-large-v3.bin (3.1 GB)
[████████████░░░░░░░░░░░░░░░░░░░░░░]  35% (1.1GB/3.1GB)
❌ Download interrupted: Connection reset by peer
💡 Resume with: voicemode whisper model install large-v3 --resume
```

### Disk Space
```
Downloading ggml-large-v3.bin (3.1 GB)
[██████░░░░░░░░░░░░░░░░░░░░░░░░░░░░]  18% (558MB/3.1GB)
❌ Insufficient disk space (need 3.1 GB, have 2.1 GB available)
```

## Testing Considerations

1. **Terminal Widths**: Test with narrow (80 cols) and wide terminals
2. **Color Support**: Test with NO_COLOR environment variable
3. **CI Environment**: Test with CI=true
4. **Network Speeds**: Test progress updates with slow/fast connections
5. **File Sizes**: Test with small (KB), medium (MB), and large (GB) files

## Recommendation

**Use Click's ProgressBar (Option 1)** because:
- No new dependencies (already using Click)
- Good terminal compatibility
- Easy to customize
- Consistent with CLI framework
- Sufficient features for our needs

Fallback to spinner (Option 4) when file size is unknown.

## Example Implementation

```python
import click
import urllib.request
from pathlib import Path

def download_with_click_progress(url: str, dest: Path, label: str = None):
    """Download with Click progress bar."""

    response = urllib.request.urlopen(url)
    total_size = int(response.headers.get('Content-Length', 0))

    if not label:
        label = f"Downloading {dest.name}"

    if total_size == 0:
        # Unknown size - use spinner
        with click.progressbar(
            label=label,
            length=None,
            show_percent=False,
            show_pos=True
        ) as bar:
            downloaded = 0
            while chunk := response.read(8192):
                dest.write(chunk)
                downloaded += len(chunk)
                bar.update(len(chunk))
    else:
        # Known size - use progress bar
        with click.progressbar(
            length=total_size,
            label=label,
            show_eta=True,
            show_percent=True,
            width=40,
            fill_char='█',
            empty_char='░'
        ) as bar:
            with open(dest, 'wb') as f:
                while chunk := response.read(8192):
                    f.write(chunk)
                    bar.update(len(chunk))
```

## Next Steps

1. Implement download_with_progress utility function
2. Update whisper_helpers.py to use new function
3. Update CLI to show progress
4. Add --quiet and --verbose flags
5. Test across different environments
6. Document in user guide

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.