Contributing
May 18, 2026 · View on GitHub
Thanks for considering a contribution. DevPinger is small enough that the right starting point is usually a thread — open an issue describing what you'd like to change before you spend a weekend on it. For paid / revenue features, see the note at the bottom.
Local setup
docs/LOCAL_SETUP.md walks through Telegram bot creation, Cloudflare Tunnel, OAuth apps, and running Postgres + Redis.
Coding style
- TypeScript + Biome. Formatting and lint rules are encoded in
biome.json—pnpm biome check --write .before committing. - Tabs for indentation, double quotes for strings, trailing commas where the parser allows them.
- One pattern, one place. If you see a helper duplicated across two
files, lift it into
@devpinger/sharedor@devpinger/coreinstead of writing a third copy. - Comments earn their keep by explaining why. Don't restate what the code already says — readers can read code.
Adding a new source
A source = OAuth + webhook + actions. The contract is in
@devpinger/core (SourceAdapter). Concrete steps:
- Create
packages/sources/<name>/withpackage.json,tsconfig.json, andsrc/. Copy the structure frompackages/sources/github. - Implement:
oauth.ts—buildAuthorizeUrl,exchangeCodeForToken,refreshAccessToken?client.ts— thin HTTP wrapper (Bearer auth, retries)normalize.ts— webhook payload →NormalizedEvent[](Test thoroughly. Normalisation bugs deliver wrong events to the wrong people.)actions.ts— provider actions called from the botadapter.ts—createXAdapter({clientId, clientSecret})returningSourceAdapterindex.ts— re-exports
- Register the adapter in
apps/server/src/registries.tsandapps/worker/src/registries.ts. - Add OAuth routes
(
apps/server/src/routes/oauth/<name>.ts) and a webhook route (apps/server/src/routes/webhooks/<name>.ts). The webhook route should be a thin delegator toservices/ingest.ts. - Add
*_OAUTH_CLIENT_ID,*_OAUTH_CLIENT_SECRET,*_OAUTH_REDIRECT_URI, and any webhook secret keys topackages/shared/src/env.ts, plus.env.example. - Add localized strings to
packages/i18n/src/messages/{en,ru}/bot.json(connect labels, action labels, etc.). - Write at least one normalize test
(
packages/sources/<name>/src/normalize.test.ts).
Adding a new destination
Similar shape, smaller surface. Implement DestinationAdapter from
@devpinger/core:
- Create
packages/destinations/<name>/. - Implement
client.ts(the SDK / HTTP wrapper),format.ts(NormalizedEvent → text + markup), andadapter.ts(factory returningDestinationAdapter). - Register in
apps/worker/src/registries.ts. The notifications worker reads fromdestinationRegistry; that's the only wiring needed.
Tests
pnpm testruns vitest across every package.- New code paths in normalize / mute / redact / OAuth state should ship with tests. Provider adapters can use HTTP fixtures or recorded responses.
- Migrations don't have tests; instead
pnpm --filter @devpinger/db exec drizzle-kit checkin CI verifies the migrations match the schema. Run that locally too after edits tosrc/schema/.
Commit messages
Conventional commits, lower-case scope: feat(server): …,
fix(db): …, chore: …. Co-author trailers are welcome.
What stays out of this repo
To keep the license line crisp, anything below ships only in the
private devpinger-cloud repo. Don't open a PR adding any of these
here:
- Recurring Stripe billing — customer/subscription/invoice tables,
invoice.*andcustomer.subscription.*webhook handlers,/billingHTTP routes,/upgradeand/billingbot commands. (One-time preorder Stripe scaffolding —preorderstable,checkout.session.completedwebhook,STRIPE_WEBHOOK_SECRETenv — is already public; it powers the $9 lifetime preorder smoke test.) - AI clients (Anthropic, OpenAI) or AI digest code
- Email transports
- Team / workspace tables
- Anything that enforces plans beyond the
noopPlanGatestub (a realstripePlanGateships in the private repo)
If you want to contribute to those, the private repo is open to maintainers — reach out.