Thanks for helping improve audioshake. Bug reports, fixes and documentation
improvements are all welcome.
- Security issues: don't open an issue or PR. Follow SECURITY.md and email security@audioshake.ai.
- Bugs: open an issue
with the version (
audioshake --version), your OS and Python version, the command or code you ran, and what happened. Redact API keys, tokens and presigned links.-vvoutput and raw--jsonoutput both contain presigned links. - New features or behavior changes: open an issue first so we can agree on
the shape before you write code. Anything that changes a CLI option, the
--jsonoutput or an exit code is part of a public contract, and we are careful about those. - API behavior: this repository is the client. A model's output, pricing, or a server error belongs with AudioShake support, not here.
You need Python 3.10 or newer and uv.
git clone https://github.com/audioshake/audioshake-python
cd audioshake-python
uv sync # environment with dev dependencies
uv tool install -e . --reinstall # optional: put this checkout's `audioshake` on PATHThe package lives under src/, so Python only sees it through the editable
install that uv sync makes. After pulling a change that moves files, run
uv sync again, and uv tool install -e . --reinstall if you use the global
command.
Run these before opening a PR. CI runs the same on Python 3.10 to 3.15.
uv run pytest # HTTP is mocked; no API key needed
uv run ruff check . && uv run ruff format --check .
uv run mypy # strict on src/audioshake/, plus tests/typing/
uv run --with pyright==1.1.414 pyright --verifytypes audioshake --ignoreexternalCI also enforces 85% coverage:
uv run pytest --cov=audioshake --cov-fail-under=85| Tests | What they cover |
|---|---|
tests/lib/ |
The library: clients, retries, downloads, run(), types, sign-in. HTTP is mocked with respx. |
tests/cli/ |
The CLI through click's test runner, with mocked HTTP or a fake client. |
tests/test_messages.py |
Fails on a user-facing message written inline instead of in a message module. |
tests/test_skill_docs.py |
Fails when a CLI command or option is missing from docs/cli.md or the agent skill. |
tests/test_import.py |
Every module imports on every supported Python version. |
The test suite never calls the real API. If you try a change against it, use
your own API key and remember that every task is billed. Never commit a key,
a token or a presigned link, including in test fixtures; fixtures use
placeholder hosts such as storage.test.
The library in src/audioshake/ is the product. The CLI in src/audioshake/cli/ is
a thin layer on the library and never calls the API directly.
docs/design.md explains the structure and the reasons
behind it. The rules that most often come up in review:
- The API is the source of truth for models. Read
GET /modelsrather than hardcoding model ids, formats or languages. - The library raises; it never prints. It raises typed exceptions from
src/audioshake/exceptions.py, logs to theaudioshakelogger, and never swallows an error to return a default. - User-facing messages live in one place. Wording goes in
src/audioshake/_messages.py, orsrc/audioshake/cli/_messages.pywhen it only makes sense at the command line. A raise site passes a built message, never a string literal, andtests/test_messages.pychecks this. - Sync and async stay in step.
client.pyandaio.pyshare request building and parsing. A change to one is a change to both, with tests for both. - The CLI's output is a contract. Logs go to stderr and results to stdout,
--jsonstays machine-readable, and the exit codes in docs/cli.md do not change meaning. - Public API only. No private hosts, internal service names or
credentials in the code.
AUDIOSHAKE_BASE_URLcovers other deployments.
plugin/skills/audioshake-cli/SKILL.md teaches AI agents to use the CLI and ships
as a Claude Code and Codex plugin from plugin/, so it must match the CLI exactly.
When your change touches a command, an option, a default, the --json shape,
the output layout, an exit code, authentication or model aliases, update in
the same PR:
plugin/skills/audioshake-cli/SKILL.md, and bumpversionin bothplugin/.claude-plugin/plugin.jsonandplugin/.codex-plugin/plugin.jsonwhen its guidance changes.docs/cli.md, anddocs/library.mdfor library changes.CHANGELOG.md: add a line to the top section, written as what the release does for a user rather than how the code changed.
To try your changes to the skill, start Claude Code from the repository root with the plugin loaded from your checkout:
claude --plugin-dir ./pluginRuff formats the Python code blocks inside Markdown, so keep doc examples under 100 columns.
ruffhandles formatting and linting;mypy --strictcoverssrc/audioshake/.- Google-style docstrings on public functions and classes.
- A new public name goes in
__all__insrc/audioshake/__init__.py, and indocs/library.md. - Comments explain why the code is the way it is, not the history of how it got there.
- Branch from
mainand targetmain. - Keep a PR to one change. Separate refactors from behavior changes.
- Say what changed and why, and how you tested it. Link the issue if there is one.
- Add a test that fails without your change.
- CI must pass before review.
Releases are cut by maintainers from v* tags; merging a PR does not publish
anything.
By contributing, you agree that your contributions are licensed under the MIT License.