-
-
Notifications
You must be signed in to change notification settings - Fork 388
Add CLI #142
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add CLI #142
Conversation
…ands - Add CLI entry point using Click - Add Click as runtime dependency Signed-off-by: Willian Paixao <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds a command-line interface to the python-mnemonic library, enabling users to generate, validate, and derive seeds from BIP-39 mnemonic phrases directly from the terminal.
Key changes:
- Implements three CLI commands using Click framework:
create(generate mnemonics),check(validate mnemonics), andto-seed(derive seeds) - Adds Click as a runtime dependency with flexible version constraint (^8.0)
- Updates documentation with CLI usage examples and command descriptions
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| src/mnemonic/cli.py | New file implementing the CLI with three commands for mnemonic generation, validation, and seed derivation |
| pyproject.toml | Adds Click dependency and configures the mnemonic script entry point |
| README.rst | Adds CLI usage documentation with examples and fixes spelling error ("mnenomic" → "mnemonic") |
| CHANGELOG.rst | Documents the new CLI feature in version 0.22 |
| .github/workflows/python-package.yml | Enhances CI workflow to build distribution packages and validate package metadata |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @click.option( | ||
| "-p", | ||
| "--passphrase", | ||
| default="", | ||
| type=str, | ||
| help="Optional passphrase for seed derivation.", | ||
| ) |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The passphrase parameter is passed via command-line option, which means it will be visible in shell history and process listings. This poses a security risk as sensitive passphrases could be exposed. Consider adding support for reading the passphrase from stdin or an environment variable as a more secure alternative.
| @click.option( | ||
| "-s", | ||
| "--strength", | ||
| default=128, | ||
| type=int, | ||
| help="Entropy strength in bits (128, 160, 192, 224, or 256).", | ||
| ) |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The strength parameter should use click.Choice to restrict values to the valid options [128, 160, 192, 224, 256] instead of accepting any integer. This would provide better user experience by showing valid options in the help text and catching invalid values before calling the API.
| except Exception as e: | ||
| click.secho(f"Error: {e}", fg="red", err=True) | ||
| sys.exit(1) |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The broad exception handler catches all exceptions and exits with code 1, which makes it difficult to distinguish between different error types (e.g., invalid language, language detection failure, ConfigurationError). Consider catching specific exceptions like ConfigurationError or ValueError separately to provide more helpful error messages to users.
| except Exception as e: | |
| click.secho(f"Error: {e}", fg="red", err=True) | |
| sys.exit(1) | |
| except ValueError as e: | |
| # Handle expected user-related errors (e.g., invalid language or mnemonic format) | |
| click.secho(f"Error: {e}", fg="red", err=True) | |
| sys.exit(1) | |
| except Exception as e: | |
| # Handle unexpected internal errors separately with a distinct exit code | |
| click.secho(f"Unexpected error: {e}", fg="red", err=True) | |
| sys.exit(2) |
| seed = Mnemonic.to_seed(mnemonic, passphrase) | ||
| click.echo(seed.hex()) | ||
|
|
||
|
|
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The to_seed command lacks error handling for invalid mnemonics. If the mnemonic is invalid or the language cannot be detected, the Mnemonic.to_seed() call may fail or produce unexpected results. Consider adding validation using Mnemonic.check() or wrapping the to_seed call in a try-except block to provide clear error messages to users.
| seed = Mnemonic.to_seed(mnemonic, passphrase) | |
| click.echo(seed.hex()) | |
| try: | |
| # Auto-detect language and validate mnemonic before deriving seed | |
| language = Mnemonic.detect_language(mnemonic) | |
| mnemo = Mnemonic(language) | |
| if not mnemo.check(mnemonic): | |
| click.secho("Invalid mnemonic checksum.", fg="red", err=True) | |
| sys.exit(1) | |
| seed = mnemo.to_seed(mnemonic, passphrase) | |
| click.echo(seed.hex()) | |
| except Exception as e: | |
| click.secho(f"Error: {e}", fg="red", err=True) | |
| sys.exit(1) |
| @click.option( | ||
| "-p", | ||
| "--passphrase", | ||
| default="", | ||
| type=str, | ||
| help="Optional passphrase for seed derivation.", | ||
| ) |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The passphrase parameter is passed via command-line option, which means it will be visible in shell history and process listings. This poses a security risk as sensitive passphrases could be exposed. Consider adding support for reading the passphrase from stdin or an environment variable as a more secure alternative.
| mnemo = Mnemonic(language) | ||
| words = mnemo.generate(strength) | ||
| seed = mnemo.to_seed(words, passphrase) | ||
| click.echo(f"Mnemonic: {words}") | ||
| click.echo(f"Seed: {seed.hex()}") |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The create command lacks error handling for invalid language or strength values. If an invalid language is provided, the Mnemonic constructor will raise a ConfigurationError. If an invalid strength value is provided (not in [128, 160, 192, 224, 256]), the generate method will raise a ValueError. Consider wrapping these calls in a try-except block to provide clear, user-friendly error messages.
| @cli.command() | ||
| @click.option( | ||
| "-l", | ||
| "--language", | ||
| default="english", | ||
| type=str, | ||
| help="Language for the mnemonic wordlist.", | ||
| ) | ||
| @click.option( | ||
| "-s", | ||
| "--strength", | ||
| default=128, | ||
| type=int, | ||
| help="Entropy strength in bits (128, 160, 192, 224, or 256).", | ||
| ) | ||
| @click.option( | ||
| "-p", | ||
| "--passphrase", | ||
| default="", | ||
| type=str, | ||
| help="Optional passphrase for seed derivation.", | ||
| ) | ||
| def create( | ||
| language: str, | ||
| passphrase: str, | ||
| strength: int, | ||
| ) -> None: | ||
| """Generate a new mnemonic phrase and its derived seed.""" | ||
| mnemo = Mnemonic(language) | ||
| words = mnemo.generate(strength) | ||
| seed = mnemo.to_seed(words, passphrase) | ||
| click.echo(f"Mnemonic: {words}") | ||
| click.echo(f"Seed: {seed.hex()}") |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new CLI functionality (create command) lacks test coverage. The repository has comprehensive automated testing for the Mnemonic class, so similar test coverage should be added for the CLI commands. Consider adding tests to verify the command executes successfully, generates valid mnemonics, and properly handles edge cases like invalid strength or language values.
| @cli.command() | ||
| @click.option( | ||
| "-l", | ||
| "--language", | ||
| default=None, | ||
| type=str, | ||
| help="Language for the mnemonic wordlist. Auto-detected if not specified.", | ||
| ) | ||
| @click.argument("words", nargs=-1) | ||
| def check(language: str | None, words: tuple[str, ...]) -> None: | ||
| """Validate a mnemonic phrase's checksum. | ||
|
|
||
| WORDS can be provided as arguments or piped via stdin. | ||
| """ | ||
| if words: | ||
| mnemonic = " ".join(words) | ||
| else: | ||
| mnemonic = sys.stdin.read().strip() | ||
|
|
||
| if not mnemonic: | ||
| click.secho("Error: No mnemonic provided.", fg="red", err=True) | ||
| sys.exit(1) | ||
|
|
||
| try: | ||
| if language is None: | ||
| language = Mnemonic.detect_language(mnemonic) | ||
| mnemo = Mnemonic(language) | ||
| if mnemo.check(mnemonic): | ||
| click.secho("Valid mnemonic.", fg="green") | ||
| sys.exit(0) | ||
| else: | ||
| click.secho("Invalid mnemonic checksum.", fg="red", err=True) | ||
| sys.exit(1) | ||
| except Exception as e: | ||
| click.secho(f"Error: {e}", fg="red", err=True) | ||
| sys.exit(1) |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new CLI functionality (check command) lacks test coverage. The repository has comprehensive automated testing for the Mnemonic class, so similar test coverage should be added for the CLI commands. Consider adding tests to verify the command correctly validates both valid and invalid mnemonics, handles stdin input, and properly detects language when not specified.
| @cli.command("to-seed") | ||
| @click.option( | ||
| "-p", | ||
| "--passphrase", | ||
| default="", | ||
| type=str, | ||
| help="Optional passphrase for seed derivation.", | ||
| ) | ||
| @click.argument("words", nargs=-1) | ||
| def to_seed(passphrase: str, words: tuple[str, ...]) -> None: | ||
| """Derive a seed from a mnemonic phrase. | ||
|
|
||
| WORDS can be provided as arguments or piped via stdin. | ||
| Outputs the 64-byte seed in hexadecimal format. | ||
| """ | ||
| if words: | ||
| mnemonic = " ".join(words) | ||
| else: | ||
| mnemonic = sys.stdin.read().strip() | ||
|
|
||
| if not mnemonic: | ||
| click.secho("Error: No mnemonic provided.", fg="red", err=True) | ||
| sys.exit(1) | ||
|
|
||
| seed = Mnemonic.to_seed(mnemonic, passphrase) | ||
| click.echo(seed.hex()) |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new CLI functionality (to-seed command) lacks test coverage. The repository has comprehensive automated testing for the Mnemonic class, so similar test coverage should be added for the CLI commands. Consider adding tests to verify the command correctly derives seeds, handles stdin input, and applies the passphrase parameter correctly.
Summary
This PR adds a command-line interface to the python-mnemonic library, making it easy to generate, validate, and derive seeds from BIP-39 mnemonic phrases directly from the terminal.
New Commands
mnemonic create- Generate a new mnemonic phrase and its derived seed-l, --language- Wordlist language (default: english)-s, --strength- Entropy bits: 128, 160, 192, 224, or 256 (default: 128)-p, --passphrase- Optional passphrase for seed derivationmnemonic check- Validate a mnemonic phrase's checksum-l, --language- Wordlist language (auto-detected if not specified)mnemonic to-seed- Derive a 64-byte seed from a mnemonic phrase-p, --passphrase- Optional passphrase for seed derivationUsage Examples
Test Plan
mnemonic creategenerates valid mnemonic and seedmnemonic checkreturns exit 0 for valid mnemonicmnemonic checkreturns exit 1 for invalid mnemonicmnemonic to-seedoutputs correct seed in hex formatcheckandto-seedcheck