Thank you for considering contributing to APM! This document outlines the process for contributing to the project.
By participating in this project, you agree to abide by our Code of Conduct. Please read it before contributing.
Before submitting a bug report:
- Check the GitHub Issues to see if the bug has already been reported.
- Update your copy of the code to the latest version to ensure the issue hasn't been fixed.
When submitting a bug report:
- Use our bug report template.
- Include detailed steps to reproduce the bug.
- Describe the expected behavior and what actually happened.
- Include any relevant logs or error messages.
Enhancement suggestions are welcome! Please:
- Use our feature request template.
- Clearly describe the enhancement and its benefits.
- Provide examples of how the enhancement would work.
- Fork the repository.
- Create a new branch for your feature/fix:
git checkout -b feature/your-feature-nameorgit checkout -b fix/issue-description. - Make your changes.
- Run tests:
uv run pytest tests/unit tests/test_console.py -x - Ensure your code follows our coding style (we use Black and isort).
- Commit your changes with a descriptive message.
- Push to your fork.
- Submit a pull request.
- Fill out the PR template - describe what changed, why, and link the issue.
- Ensure your PR addresses only one concern (one feature, one bug fix).
- Include tests for new functionality.
- Update documentation if needed.
- PRs must pass all CI checks before they can be merged.
This repo uses GitHub's native merge queue. Once your PR is approved, a maintainer adds it to the queue. The queue then:
- Builds a tentative merge of your PR against the latest
main- no manual "Update branch" needed. - Runs the integration suite against that tentative merge.
- Auto-merges if checks pass; ejects from the queue if they fail.
What this means for contributors:
- You don't need to keep your branch up to date with
mainmanually. - The fast unit + build checks (Tier 1) run on every push to your PR.
- The full integration suite (Tier 2) only runs once your PR is in the queue, not on every WIP push.
If your PR is ejected from the queue because of a real failure, push a fix and ask a maintainer to re-queue.
Every new issue is automatically labeled needs-triage. Maintainers review incoming issues and:
- Accept - remove
needs-triage, addaccepted, and assign a milestone. - Prioritize - optionally add
priority/highorpriority/low. - Close - if it's a duplicate (
duplicate) or out of scope, close with a comment explaining why.
Labels used for triage: needs-triage, accepted, needs-design, priority/high, priority/low.
This project uses uv to manage Python environments and dependencies:
# Clone the repository
git clone <this-repo-url>
cd apm
# Install all dependencies (creates .venv automatically)
uv sync --extra devWe use pytest for testing with pytest-xdist for parallel execution. After completing the setup above:
# Run the unit test suite (recommended - matches CI, fast)
uv run pytest tests/unit tests/test_console.py -x
# Run a specific test file (fastest, use during development)
uv run pytest tests/unit/path/to/relevant_test.py -x
# Run the full test suite (includes integration & acceptance tests)
uv run pytest
# Run with verbose output
uv run pytest tests/unit -x -vTests run in parallel automatically (-n auto is configured in pyproject.toml). To force serial execution, add -n0.
If you don't have uv available, you can use a standard Python venv and pip:
# create and activate a venv (POSIX / WSL)
python -m venv .venv
source .venv/bin/activate
# install this package in editable mode and test deps
pip install -U pip
pip install -e .[dev]
# run unit tests
pytest tests/unit tests/test_console.py -xThis project follows:
- PEP 8 for Python style guidelines
- We use Black for code formatting and isort for import sorting
You can run these tools with:
uv run black .
uv run isort .If your changes affect how users interact with the project, update the documentation accordingly.
Use an experimental flag to de-risk rollout of a user-visible behavioural change that may need early adopter feedback. Do not add a flag for a bug fix, internal refactor, or any change that should simply ship as the default behaviour.
Experimental flags MUST NOT gate security-critical behaviour (content scanning, path validation, lockfile integrity, token handling, MCP trust, collision detection). Flags are ergonomic/UX toggles only.
When adding a new experimental flag:
- Register it in
src/apm_cli/core/experimental.pyin theFLAGSdict with a frozenExperimentalFlag(name=..., description=..., default=False, hint=...). - Gate the code path with a function-scope import (avoids import cycles):
def my_function(): from apm_cli.core.experimental import is_enabled if is_enabled("my_flag"): ...
- Add tests that cover both the enabled and disabled code paths.
- Update the experimental command reference page at
docs/src/content/docs/reference/experimental.md.
Naming rules:
- Use
snake_casein the registry and config. - Use
kebab-casefor display and other user-facing strings. - The CLI accepts both forms on input.
Graduation and retirement:
- When a flag becomes the default, remove the gate and remove the matching
FLAGSentry in the same PR. - Add a
CHANGELOG.mdentry underChangedwith a migration note if the previous default differed.
Avoid these anti-patterns:
- Do not gate security-critical behaviour behind an experimental flag.
- Do not read
is_enabled()at module import time. - Do not persist flag state anywhere other than
~/.apm/config.jsonviaupdate_config.
By contributing to this project, you agree that your contributions will be licensed under the project's MIT License.
If you have any questions, feel free to open an issue or reach out to the maintainers.
Thank you for your contributions!