/\_/\
( ^.^ ) *excited chittering*
> ^ <
Want to help rummage through finances? Here's how to get your paws dirty.
# Clone and enter the den
git clone https://github.com/aviraccoon/rummage
cd rummage
# Option 1: Nix (recommended) - provides bun, beancount, fava
nix develop
# Or with direnv: direnv allow
# Option 2: Manual setup
mise install # bun
uv tool install beancount fava # beancount tooling
# Install dependencies
bun install
# Make sure everything works
bun run check# Run the build pipeline
# Always specify env explicitly (don't rely on .env which may have different settings)
RUMMAGE_DATA_SOURCE=examples bun run build
# Run tests
bun test
# Run everything (typecheck + lint + test)
bun run check
# Fix linting issues
bun run lint:fix
# Typecheck only
bun run typecheckWe use Biome for linting and formatting:
- Tabs for indentation
- Double quotes for strings
- No trailing semicolons required (Biome handles it)
Run bun run lint:fix before committing. The raccoon will be sad if CI fails.
src/
├── importers/ # Bank and format importers
├── output/ # Output generators (beancount, etc.)
├── types.ts # Core data structures
├── config.ts # Environment configuration
├── registry.ts # Importer registry
├── build.ts # Pipeline orchestration
└── *.test.ts # Tests live next to source files
This is the most common contribution! To add support for a new bank:
- Create the bank directory
src/importers/your-bank/with:transform.ts- the importer logictransform.test.ts- tests
// src/importers/your-bank/transform.ts
import type { ImportResult, Transaction } from "../../types.ts";
export function importYourBankFile(filePath: string): ImportResult {
// Parse CSV/OFX/whatever format your bank uses
// Return { transactions, errors }
}
export function importYourBankDirectory(dirPath: string): ImportResult {
// Import all files from a directory
}- Add tests in
src/importers/your-bank/transform.test.ts:
import { describe, expect, test } from "bun:test";
import { importYourBankFile } from "./transform.ts";
describe("importYourBankFile", () => {
test("parses transactions correctly", () => {
// Add sample file to examples/raw/your-bank/
const result = importYourBankFile("examples/raw/your-bank/sample.csv");
expect(result.transactions).toHaveLength(/* expected */);
});
});-
Register the importer in
src/registry.ts(follow the pattern forfioandrevolut) -
Add example data in
examples/raw/your-bank/with sanitized sample exports
- Generate stable, unique IDs (for override matching and deduplication)
- Preserve rawName and rawMemo for rule matching
- Use ISO dates:
YYYY-MM-DD - Positive amounts = inflow, negative = outflow
- Set
sourceto the file path
Tests use Bun's built-in test runner:
# Run all tests
bun test
# Run specific test file
bun test src/importers/ofx.test.ts
# Watch mode
bun test --watchTests run against examples/ data by default. Add sample files there for new features.
We use strict TypeScript:
strict: truenoUncheckedIndexedAccess: true(array access returnsT | undefined)- Use
assertAt()andassertDefined()fromtest-utils.tsin tests
- Fix warnings, don't suppress them - no
biome-ignoreor lint disables to sweep issues under the rug - Refactoring for testability is encouraged - extracting pure functions, breaking up complexity - just don't over-engineer
- Fork and create a branch
- Make your changes
- Run
bun run check(must pass!) - Write/update tests
- Submit PR with clear description
-
bun run checkpasses - Tests added/updated
- Example data added (if new transform)
-
CHANGELOG.mdupdated (for notable changes) - Version bumped in
package.json(for releases)
Like a raccoon looking for treasures:
- New bank transforms - support more banks/formats
- Bug fixes - especially in parsing edge cases
- Output formats - beyond Beancount
- Scripts - useful financial analysis tools
Open an issue! We don't bite (unless you're a garbage bag full of transactions).
/\_/\
( o.o ) Happy rummaging!
> ^ <
/| |\