Thanks for your interest in improving @devicecloud.dev/dcd! This guide covers
everything you need to land a change: local setup, our commit/PR conventions, and
how releases work.
By participating you agree to abide by our Code of Conduct.
Before your first contribution can be merged, you must sign our Contributor License Agreement. When you open your first pull request, the CLA Assistant bot will comment with a link and instructions — signing takes under a minute and is a one-time step. PRs cannot be merged until the CLA check is green.
- Individuals: sign the Individual CLA.
- Contributing on behalf of an employer? Have an authorised signatory complete the Corporate CLA.
You need Node.js 22+ and pnpm (packageManager pins
the exact version; Corepack will pick it
up automatically).
$ git clone https://github.com/devicecloud-dev/dcd-cli.git
$ cd dcd-cli
$ pnpm install # installs deps, builds, and sets up git hooks
$ pnpm dcd <args> # run the CLI from sourceUseful scripts:
| Command | What it does |
|---|---|
pnpm lint |
ESLint over src/ and test/ |
pnpm typecheck |
Strict tsc --noEmit over src/ and test/ |
pnpm build |
Compile to dist/ |
pnpm test:unit |
Run the unit suite — no backend needed. This is what CI runs. |
pnpm test |
The same, plus the integration suite if MOCK_API_DIR points at a mock API |
Before pushing, make sure pnpm lint, pnpm typecheck, pnpm test:unit, and
pnpm build pass. These run for every PR and are required to merge.
Tests split in two. test/unit/* is pure — no network, no backend — and runs
everywhere, in CI and locally.
test/integration/* drives the built CLI against a Prism mock of the dcd API on
port 3001. CI does not run it, on any PR, from a fork or otherwise: this repo
is public and deliberately holds no credentials for, and makes no requests to,
our private infrastructure. There is no default mock API — set
MOCK_API_DIR=/path/to/mock-api (a package exposing a start:auth script on
port 3001) and pnpm test picks the integration suite up. Without it the runner
prints a notice and runs the unit suite alone.
So every contributor, maintainers included, gets the same CI signal, and you don't need backend access to contribute. The flip side is worth knowing: a green PR says nothing about the integration suite, so if your change touches the API surface, say so in the PR and a maintainer will exercise it before merge.
A gitleaks scan runs as a pre-commit hook
and in CI (sharing the allowlist in .gitleaks.toml). Installing the binary
locally (brew install gitleaks) catches secrets before you commit; without it
the hook skips with a warning and CI remains the backstop. Never commit real
credentials.
- Branch off
dev(the default branch). Name it descriptively, e.g.fix/upload-retryorfeat/json-output. - Open your pull request against
dev. (Theproductionbranch is the stable release track and is maintainer-only — don't target it.) - Keep PRs focused. Smaller, single-purpose PRs are reviewed and merged faster.
- Fill in the PR template, including the checklist.
- PRs are merged via squash merge, so your PR ends up as a single commit on
devwhose message is your PR title — which is why the title must follow the Conventional Commits format below.
We use Conventional Commits. Because we
squash-merge, only your PR title needs to follow the format — individual
commit messages on your branch are squashed away, so commit however you like
while developing. A CI check (PR Title) validates the title and must pass to
merge.
Format:
<type>(<optional scope>): <description>
Allowed types and how they affect the next release:
| Type | Use for | Changelog | Version bump |
|---|---|---|---|
feat |
A new feature | Features | minor |
fix |
A bug fix | Bug Fixes | patch |
perf |
A performance improvement | Performance | patch |
deps |
Dependency updates | Dependencies | patch |
revert |
Reverting a previous change | Reverts | patch |
refactor |
Code change that neither fixes a bug nor adds a feature | Code Refactoring | patch |
docs |
Documentation only | hidden | none |
chore |
Tooling/maintenance | hidden | none |
test |
Adding or fixing tests | hidden | none |
ci |
CI configuration | hidden | none |
build |
Build system | hidden | none |
style |
Formatting, whitespace | hidden | none |
Breaking changes: append ! after the type (e.g. feat!: drop Node 20) or
add a BREAKING CHANGE: footer in the PR description. While the CLI is pre-1.0,
feat bumps the minor version and breaking changes bump the minor too.
Examples:
feat(cloud): add --json output for run results
fix: retry binary upload on transient 5xx
docs: clarify dcd login flow in README
deps: bump @modelcontextprotocol/sdk to 1.x
- TypeScript, strict mode. Run
pnpm lintandpnpm typecheckbefore pushing. - Formatting is handled by Prettier (config in
.prettierrc); an.editorconfigkeeps editors consistent. - All human-facing CLI output goes through the rendering layer described in
STYLE_GUIDE.md— please read it before adding output. Don't hand-roll layouts or callconsole.logdirectly.
You don't need to do anything for releases — do not bump the version in
package.json or edit CHANGELOG.md in your PR.
Releases are automated by release-please:
- Merges to
devaccumulate into a beta release (published to npm under thebetatag). - Maintainers promote
dev→productionfor stable releases (npmlatest).
release-please reads the Conventional Commit titles of merged PRs to compute the next version and generate the changelog — which is exactly why the PR title convention matters.
- General questions and help: Discord.
- Security vulnerabilities: do not open an issue — see SECURITY.md.
Thanks for contributing! 🎉