diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 78f6c1f..612a3ed 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,16 +4,41 @@ Thank you for your interest in contributing to Device Connect! This guide will h
## Prerequisites
-- Python 3.10+
+- Python 3.11+
- Docker & Docker Compose v2
- Git
+- [GitHub CLI](https://cli.github.com/) (`gh`) — optional but recommended
## Development Setup
+### 1. Fork & clone (first time only)
+
+The easiest path uses the [GitHub CLI](https://cli.github.com/):
+
```bash
-git clone https://github.com/arm/device-connect.git
+gh repo fork arm/device-connect --clone
cd device-connect
+```
+
+This forks the repo under your account and clones it with `origin` pointing to your fork and `upstream` pointing to `arm/device-connect`.
+
+Manual setup (without gh)
+
+1. Fork via the GitHub UI
+2. Clone your fork and add the upstream remote:
+
+```bash
+git clone https://github.com//device-connect.git
+cd device-connect
+git remote add upstream https://github.com/arm/device-connect.git
+```
+
+
+
+### 2. Install dependencies
+
+```bash
python3 -m venv .venv
source .venv/bin/activate
@@ -39,9 +64,9 @@ See the [README](README.md) for the full architecture overview.
### 1. Create a branch
```bash
-git checkout main && git pull origin main
-git checkout -b feature/your-feature-name
-# or: git checkout -b fix/issue-number-description
+git fetch upstream
+git checkout -b feature/your-feature-name upstream/main
+# or: git checkout -b fix/issue-number-description upstream/main
```
### 2. Make changes
@@ -50,7 +75,24 @@ git checkout -b feature/your-feature-name
- Add tests for new functionality
- Update documentation if needed
-### 3. Commit
+### 3. Lint & test
+
+```bash
+# Lint (must pass before opening a PR)
+ruff check packages/ tests/
+
+# Unit tests — run the package(s) you changed
+pytest packages/device-connect-edge/tests/ -v
+pytest packages/device-connect-server/tests/ -v
+pytest packages/device-connect-agent-tools/tests/test_connection_unit.py \
+ packages/device-connect-agent-tools/tests/test_tools_unit.py \
+ packages/device-connect-agent-tools/tests/test_langchain_adapter.py \
+ packages/device-connect-agent-tools/tests/test_strands_adapter.py -v
+```
+
+See [Running Tests](#running-tests) below for integration tests.
+
+### 4. Commit
```bash
git add
@@ -64,27 +106,43 @@ Closes #123"
Commit message prefixes: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`
-### 4. Open a pull request
+### 5. Open a pull request
+
+```bash
+gh pr create --fill
+```
+
+This pushes your branch to your fork and opens a PR against `arm/device-connect:main` in one step.
+
+
+Manual setup (without gh)
```bash
git push origin feature/your-feature-name
```
-Open a PR on GitHub targeting `main`. Include a description of what changed, why, and how to test it.
+Then open a PR on GitHub from your fork targeting `arm/device-connect:main`.
+
+
## Running Tests
### Unit tests (no Docker needed)
+Run from the repository root:
+
```bash
-# SDK
-cd packages/device-connect-edge && python3 -m pytest tests/ -v
+# Edge SDK
+pytest packages/device-connect-edge/tests/ -v
# Server
-cd packages/device-connect-server && python3 -m pytest tests/ -v
+pytest packages/device-connect-server/tests/ -v
# Agent tools
-cd packages/device-connect-agent-tools && python3 -m pytest tests/test_connection_unit.py tests/test_tools_unit.py -v
+pytest packages/device-connect-agent-tools/tests/test_connection_unit.py \
+ packages/device-connect-agent-tools/tests/test_tools_unit.py \
+ packages/device-connect-agent-tools/tests/test_langchain_adapter.py \
+ packages/device-connect-agent-tools/tests/test_strands_adapter.py -v
```
### Integration tests (require Docker)
@@ -92,7 +150,7 @@ cd packages/device-connect-agent-tools && python3 -m pytest tests/test_connectio
```bash
cd tests
docker compose -f docker-compose-itest.yml up -d
-DEVICE_CONNECT_ALLOW_INSECURE=true python3 -m pytest tests/ -v -m "not llm"
+DEVICE_CONNECT_ALLOW_INSECURE=true pytest -v -m "not llm"
docker compose -f docker-compose-itest.yml down -v
```
@@ -100,15 +158,16 @@ See [tests/README.md](tests/README.md) for the full test matrix.
## Coding Standards
-- **Style**: PEP 8, enforced with `ruff`
-- **Line length**: 120 characters
+- **Style**: PEP 8, enforced with [`ruff`](ruff.toml)
+- **Line length**: 120 characters (configured in [`ruff.toml`](ruff.toml))
- **Type hints**: Use throughout public APIs
- **Naming**: `snake_case` for functions/variables, `PascalCase` for classes, `UPPER_CASE` for constants, `camelCase` for event names (e.g., `taskComplete`, `plateGrasped`)
- **Docstrings**: Google-style
-- **Tests**: pytest-asyncio with `asyncio_mode = auto`, mock `@emit` methods with `AsyncMock`
+- **Tests**: pytest with pytest-asyncio; `asyncio_mode` varies by package (`auto` in server and integration tests, `strict` in agent-tools). Mock `@emit` methods with `AsyncMock`.
## Pull Request Checklist
+- [ ] `ruff check` passes with no errors
- [ ] All unit tests pass
- [ ] Integration tests pass (if applicable)
- [ ] New code has tests
diff --git a/ruff.toml b/ruff.toml
new file mode 100644
index 0000000..f11cf63
--- /dev/null
+++ b/ruff.toml
@@ -0,0 +1 @@
+line-length = 120