We are unapologetically eager to merge small, well-scoped contributions.
Lighthouse normalizes merchant names by walking a hand-curated rule set first, then falling back to an LLM. The rule set lives in packages/core/src/domain/merchant_rules.ts. To add a merchant:
-
Find the right category section (or create a new one).
-
Add a row:
{ canonical: 'klaviyo', display: 'Klaviyo', category: 'developer', domains: ['klaviyo.com'], aliasPatterns: [/^klaviyo\b/i] },
-
npm run lintandnpm run testshould both pass. -
Open a PR. There's no review queue — we'll merge anything reasonable.
Naming
canonical: lowercase, kebab-case slug. Used as the primary key in the DB.display: the form a user would recognize on a card statement.category: one ofshopping | groceries | food | streaming | productivity | developer | fitness | transit | travel | payments | utilities | cloud | news | apps | other. Pick the closest match.
Domain match wins
If a rule provides a domains list, an email from any matching domain → that merchant, even if the name itself doesn't match. Aliases are a fallback for descriptors like card-statement strings.
Prompts live in:
packages/core/src/llm/extractors/classifier.tspackages/core/src/llm/extractors/receipt.tspackages/core/src/llm/extractors/subscription.tspackages/core/src/domain/normalize.ts
If you find an email type that's misclassified or extracted wrong:
- Capture a redacted copy of the email body.
- Run the pipeline against just that email (
scripts/replay-one.tsis on the roadmap; for now you can paste the body into a manualrunStructured()call in a test file). - Adjust the system prompt or schema. Prefer adding examples to changing rules.
Tests live in test/ and use Vitest. Side-effecting tests should set LIGHTHOUSE_HOME to a tmpdir so they can't clobber a real install. See test/vault.test.ts for the pattern.
- TypeScript strict mode. No
anywithout a comment. - Files under 300 lines. If you go over, split.
- Use the Edit tool / your editor's autofix to keep imports tidy.
npm run formatruns Prettier;npm run lintruns ESLint.
simple-git-hooks is wired into the repo. After npm install, two hooks run automatically:
| Hook | When | What it runs |
|---|---|---|
pre-commit |
Every git commit |
npm run check:fast (typecheck + lint) |
pre-push |
Every git push |
npm run check (typecheck + lint + test + build) |
The two manual scripts:
npm run check:fast # typecheck + lint — runs in seconds
npm run check # full CI gauntlet — typecheck + lint + test + buildIf you skip the hooks (git push --no-verify), CI will catch you. Better to run npm run check once locally than to chase a red checkmark on GitHub.
- Squash-merge style. Keep your commit history tidy on your branch.
- One logical change per PR. If you find yourself writing "and also...", split.
- Mention the issue number if there is one.
Be kind. Disagree with ideas, not with people. Assume the other contributor is acting in good faith.