Skip to content

Conversation

@willianpaixao
Copy link
Contributor

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 derivation
  • mnemonic check - Validate a mnemonic phrase's checksum

    • -l, --language - Wordlist language (auto-detected if not specified)
    • Accepts words as arguments or via stdin
    • Exit code 0 for valid, 1 for invalid
  • mnemonic to-seed - Derive a 64-byte seed from a mnemonic phrase

    • -p, --passphrase - Optional passphrase for seed derivation
    • Accepts words as arguments or via stdin
    • Outputs seed in hexadecimal format

Usage Examples

# Generate a new mnemonic
mnemonic create
mnemonic create -s 256 -l english -p "my passphrase"

# Validate a mnemonic
mnemonic check word1 word2 word3 ...
echo "word1 word2 ..." | mnemonic check

# Derive seed
mnemonic to-seed word1 word2 word3 ...
echo "word1 word2 ..." | mnemonic to-seed -p "passphrase"

Test Plan

  • mnemonic create generates valid mnemonic and seed
  • mnemonic check returns exit 0 for valid mnemonic
  • mnemonic check returns exit 1 for invalid mnemonic
  • mnemonic to-seed outputs correct seed in hex format
  • Stdin input works for check and to-seed
  • Language auto-detection works in check
  • All existing tests pass
  • Style checks pass (black, flake8, pyright)

…ands

- Add CLI entry point using Click
- Add Click as runtime dependency

Signed-off-by: Willian Paixao <[email protected]>
Copilot AI review requested due to automatic review settings January 2, 2026 12:30
Copy link

Copilot AI left a 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), and to-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.

Comment on lines +29 to +35
@click.option(
"-p",
"--passphrase",
default="",
type=str,
help="Optional passphrase for seed derivation.",
)
Copy link

Copilot AI Jan 2, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +22 to +28
@click.option(
"-s",
"--strength",
default=128,
type=int,
help="Entropy strength in bits (128, 160, 192, 224, or 256).",
)
Copy link

Copilot AI Jan 2, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +82 to +84
except Exception as e:
click.secho(f"Error: {e}", fg="red", err=True)
sys.exit(1)
Copy link

Copilot AI Jan 2, 2026

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.

Suggested change
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)

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +114
seed = Mnemonic.to_seed(mnemonic, passphrase)
click.echo(seed.hex())


Copy link

Copilot AI Jan 2, 2026

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.

Suggested change
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)

Copilot uses AI. Check for mistakes.
Comment on lines +88 to +94
@click.option(
"-p",
"--passphrase",
default="",
type=str,
help="Optional passphrase for seed derivation.",
)
Copy link

Copilot AI Jan 2, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +42 to +46
mnemo = Mnemonic(language)
words = mnemo.generate(strength)
seed = mnemo.to_seed(words, passphrase)
click.echo(f"Mnemonic: {words}")
click.echo(f"Seed: {seed.hex()}")
Copy link

Copilot AI Jan 2, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +46
@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()}")
Copy link

Copilot AI Jan 2, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +49 to +84
@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)
Copy link

Copilot AI Jan 2, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +87 to +112
@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())
Copy link

Copilot AI Jan 2, 2026

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant