Welcome, and thank you for your interest in contributing to Claudius. Named after the Roman Emperor Claudius, known for infrastructure, administration, and expanding connectivity across the empire, this project strives to bring those same qualities to modern app development. Claudius is an embeddable AI chat widget built by PMDevSolutions, and contributions from the community are what make it better.
Whether you are fixing a bug, adding a feature, improving documentation, or suggesting an idea, we appreciate your time and effort.
-
Fork and clone the repository:
git clone https://github.com/<your-username>/claudius.git cd claudius
-
Install pnpm if you do not already have it. This project uses pnpm exclusively.
corepack enable corepack prepare pnpm@latest --activate -
Install dependencies:
cd widget && pnpm install cd ../worker && pnpm install
-
Set up local development:
cp worker/.dev.vars.example worker/.dev.vars # Add your Anthropic API key to .dev.vars
Run the widget and worker in separate terminals:
Terminal 1 - Worker:
cd worker
pnpm devTerminal 2 - Widget:
cd widget
pnpm devOpen http://localhost:5173 to test.
# Widget tests
cd widget && pnpm test
# Worker tests
cd worker && pnpm testAll tests must pass before a pull request will be reviewed.
Claudius ships first-party translations for English, Spanish, French, and German. Every UI string lives in widget/src/locales/, and en.ts is the single source of truth for the set of keys. Adding a language is welcome and self-contained.
-
Copy the English file. Use
widget/src/locales/en.tsas your starting point so you have the complete, correctly typed key set:cp widget/src/locales/en.ts widget/src/locales/pt.ts
-
Translate every value. Rename the exported constant (e.g.
export const pt) and translate each string. Keep the: ClaudiusTranslationstype annotation, it makes any missing key a compile error. -
Register the locale in
widget/src/locales/index.ts:- Add the code to the
LocaleCodeunion (e.g."pt"). - Add the imported constant to the
localesregistry.
Detection matches on the primary subtag, so registering
ptautomatically coverspt-BR,pt-PT, and so on from<html lang>ornavigator.language. - Add the code to the
-
Add it to the Storybook switcher in
widget/.storybook/preview.tsby appending an entry toLOCALE_ITEMS. This lets reviewers eyeball your translation across every component via the Locale toolbar:cd widget && pnpm storybook
-
Verify from the
widget/directory:pnpm test # the key-parity test fails if any key is missing or extra pnpm typecheck
Translate these for tone and register, not word-for-word. A literal translation often reads as cold or robotic, and formality conventions differ by language:
welcomeMessageandsubtitle— the greeting sets the bot's warmth. Choose the register your audience expects: informal (Spanish tú, German du) reads friendlier, while formal (Spanish usted, French vous, German Sie) reads more professional. The shipped locales use informal Spanish and formal French/German; stay internally consistent with whichever you pick.error*strings — error tone varies by culture between apologetic and matter-of-fact. Keep them reassuring and actionable rather than blunt, and match the formality you chose for the greetings.
If you are unsure about register, note it in your pull request so a native speaker can weigh in during review.
Use the following prefixes when creating branches:
| Prefix | Use Case | Example |
|---|---|---|
feat/ |
New features or capabilities | feat/message-reactions |
fix/ |
Bug fixes | fix/cors-preflight |
docs/ |
Documentation updates | docs/deployment-guide |
chore/ |
Maintenance, refactoring, tooling | chore/upgrade-vitest |
Branch names should be lowercase, use hyphens as separators, and be descriptive.
-
Create a focused branch from
mainusing the naming conventions above. -
Make your changes. Write tests for any new functionality and ensure existing tests continue to pass.
-
Run tests locally before pushing:
cd widget && pnpm test cd ../worker && pnpm test
-
Push your branch and open a pull request against
main. -
Write a clear pull request title and description:
- The title should be concise and under 70 characters.
- The description should explain what changed and why.
- Reference any related issues (e.g.,
Closes #42).
-
Use conventional commit messages. Examples:
feat: add typing indicator animation fix: resolve CORS preflight issue docs: add deployment instructions chore: update dependencies -
All CI checks must pass. Pull requests with failing tests will not be merged until resolved.
-
Be responsive to review feedback. Reviewers may request changes.
Releases are fully automated with release-please:
merging to main accumulates conventional commits into a release PR, and
merging that PR cuts the tag, GitHub Release, and CHANGELOG.md entry.
feat: bumps the minor version, fix: the patch, and a !/BREAKING CHANGE
commit the major. Version numbers are therefore outputs of the commit
history, not plans.
The same release also publishes the widget to npm as
claudius-chat-widget. The
publish step in .github/workflows/release-please.yml runs only when a release is
cut, builds the dual ESM/CJS package, and authenticates with an NPM_TOKEN
repository secret (publishing with provenance):
-
Create an Automation access token — or a granular token scoped to publish
claudius-chat-widget— at npmjs.com → Access Tokens. -
Store it as a repository Actions secret:
gh secret set NPM_TOKEN --repo PMDevSolutions/Claudius
The very first version must be published manually once, because release-please only triggers the publish step on a new release after the workflow is wired up:
npm login
cd widget && pnpm build && npm publish --access public(--provenance is omitted for the local manual publish; it requires the OIDC
token only available in CI.) Every release after that publishes automatically.
Two conventions follow from that:
- Milestones are themes, not version numbers. Issues are grouped into
thematic milestones (e.g. Developer Experience & Distribution,
Conversation Intelligence, Quality & Hardening) with due dates. Don't
create milestones named after unreleased minor versions — release-please
will assign whatever number the commits dictate, and the milestone name
will drift. The one exception is a deliberately gated major (e.g.
v2.0 - Multi-Channel & Enterprise): breaking changes are a real product
boundary because the CDN
@1channel never auto-serves a new major. - Issues associate with releases through commits, not milestones. Put
Closes #Nin the PR description and reference the issue in commit bodies where useful; the changelog and GitHub Release then record which version actually shipped the work.
claudius/
├── widget/ # React chat widget (Vite + TypeScript + Tailwind)
│ ├── src/
│ │ ├── components/ # React components
│ │ └── hooks/ # Custom hooks (useChat)
│ └── package.json
├── worker/ # Cloudflare Workers backend (Hono + Anthropic SDK)
│ ├── src/
│ │ ├── index.ts # API routes
│ │ ├── chat.ts # Claude integration
│ │ └── system-prompt.ts # Bot personality
│ └── package.json
└── package.json # Root package
We follow standard open source etiquette. All contributors are expected to:
- Be respectful and constructive in all interactions.
- Provide clear, actionable feedback in code reviews.
- Assume good intent from other contributors.
- Keep discussions focused on the project and its goals.
Harassment, discrimination, and disruptive behavior will not be tolerated.
Thank you for contributing to Claudius. Your work helps make AI-powered chat widgets more accessible, reliable, and efficient for everyone.