- ✅ ALWAYS ask before
git checkout <branch> - ✅ ALWAYS ask before
git pullorgit fetchfollowed by merge/rebase - ✅ ALWAYS ask before creating new branches
- ✅ Stay on the branch the user checked out unless they explicitly ask you to switch
- ❌ NEVER run
git checkouton your own - ❌ NEVER run
git pullon your own - ❌ NEVER create branches autonomously
- ❌ NEVER switch branches when starting a new task - the user has already set up the branch
If you need to work on a different branch, ask the user first:
- "Should I switch to branch X to work on this?"
- "Should I create a new branch for this feature?"
When the user says a PR is merged, do NOT automatically checkout main and pull. Wait for explicit instructions.
This is a personal finance application. Users may share screenshots or logs containing real financial data (account names, transaction details, merchant names, etc.) when debugging issues.
- ❌ NEVER copy personal data from screenshots/logs into code comments
- ❌ NEVER use real account names, card numbers, or transaction details as examples
- ✅ Use generic examples like "Account Name", "Example Merchant", etc.
- ✅ If you need to reference data formats, use clearly fake data
moneyflow is a terminal-based UI for power users to manage personal finance transactions efficiently. Built with Python using Textual for the UI and Polars for data processing. Supports multiple backends including Monarch Money, with more platforms planned (YNAB, Lunch Money, etc.).
IMPORTANT: This project uses uv exclusively for all development workflows. Always use uv run for executing scripts. Never use pip, pipenv, poetry, or other package managers.
CRITICAL FOR AI ASSISTANTS (Claude Code, etc.):
- ❌ NEVER run
pip installoruv pip installto modify the user's environment - ❌ NEVER run
uv tool installfor project dependencies - ✅ All dependencies MUST be declared in
pyproject.tomland installed viauv sync - ✅ Use
uv run <command>to run tools in the project's virtual environment - 💡 This ensures reproducibility - anyone can clone the repo and run
uv syncto get the exact same environment
# Install uv if not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh
# FIRST TIME SETUP: Sync dependencies (includes dev dependencies for testing)
uv sync
# This creates a virtual environment and installs all dependencies
# You MUST run this before running tests or the TUI for the first time
# After sync, run the TUI
uv run moneyflow
# Run tests (ALWAYS before committing)
uv run pytest
# Run tests with coverage
uv run pytest --cov --cov-report=html
# View coverage report
open htmlcov/index.htmlIf you get ModuleNotFoundError: Run uv sync first!
This project handles financial data. We cannot afford slip-ups.
MANDATORY WORKFLOW:
- Write tests first for any new feature or bug fix
- Run tests - verify they fail as expected
- Implement the feature/fix
- Run tests again - verify all tests pass
- Check coverage - ensure new code is tested
- Only commit when tests are green
Before EVERY commit:
# Run full test suite
uv run pytest -v
# Run type checker
uv run pyright moneyflow/
# Check coverage
uv run pytest --cov --cov-report=term-missing
# Check markdown formatting (if docs changed)
markdownlint --config .markdownlint.json README.md 'docs/**/*.md'
.github/scripts/check-arrow-lists.shAll tests must pass, type checking must be clean, and markdown must be properly formatted before committing. No exceptions.
IMPORTANT: All Python source code must be in the moneyflow/ package. No Python files should live at the top level.
moneyflow/
├── moneyflow/ # Main package (ALL code goes here)
│ ├── app.py # Main Textual application (~1750 lines)
│ ├── monarchmoney.py # GraphQL client (keep separate for upstream diffs)
│ ├── data_manager.py # Data layer with Polars
│ ├── state.py # App state management
│ ├── credentials.py # Encrypted credential storage
│ ├── duplicate_detector.py # Duplicate detection
│ ├── view_presenter.py # Presentation logic (NEW - fully typed & tested)
│ ├── time_navigator.py # Time period calculations (NEW - 100% coverage)
│ ├── commit_orchestrator.py # DataFrame update logic (NEW - critical, 100% tested)
│ ├── backends/ # Backend implementations
│ ├── screens/ # UI screens and modals
│ ├── widgets/ # Custom UI widgets
│ └── styles/ # Textual CSS
├── tests/ # Test suite (744 tests)
│ ├── conftest.py # Pytest fixtures
│ ├── mock_backend.py # Mock MonarchMoney API
│ ├── test_state.py # State management tests
│ ├── test_data_manager.py # Data operations tests
│ ├── test_view_presenter.py # Presentation logic tests (NEW - 48 tests)
│ ├── test_time_navigator.py # Time navigation tests (NEW - 52 tests)
│ ├── test_commit_orchestrator.py # DataFrame updates (NEW - 30 tests)
│ └── test_workflows.py # Edit workflow tests
├── pyproject.toml # Project metadata and dependencies
├── README.md # User documentation
└── CLAUDE.md # This file - development guide
File Organization Rules:
- ✅ All business logic in
moneyflow/package - ✅ All tests in
tests/directory - ✅ Entry point via
moneyflowcommand (configured in pyproject.toml) - ❌ No
.pyfiles at top level - ❌ No duplicate files between top-level and package
IMPORTANT: All business logic must be tested before running against real data.
-
Mock Backend:
tests/mock_backend.pyprovides aMockMonarchMoneyclass that simulates the API without making real network calls. -
Test Fixtures:
tests/conftest.pyprovides reusable test data and fixtures. -
Separation of Concerns:
state.py: Pure state management (no I/O) - easily testabledata_manager.py: Takes MonarchMoney instance via dependency injection - can use mock- UI layer: Testable with Textual pilot tests
- ✅ State management: undo/redo, change tracking
- ✅ Data operations: aggregation, filtering, search
- ✅ Edit workflows: merchant rename, category change, hide toggle
- ✅ Bulk operations: multi-select, bulk edit
- ✅ Duplicate detection: finding and handling duplicates
- ✅ Presentation logic: View formatting, flag computation (100% coverage)
- ✅ Time navigation: Date calculations, leap years, boundaries (100% coverage)
- ✅ DataFrame updates: Critical commit logic (100% coverage)
- ✅ Edge cases: empty datasets, invalid data, API failures
ALWAYS use uv run for running tests:
# Run all tests (run before EVERY commit)
uv run pytest -v
# Run with coverage report
uv run pytest --cov --cov-report=html --cov-report=term-missing
# Run specific test file
uv run pytest tests/test_state.py -v
# Run tests matching a pattern
uv run pytest -k "test_undo" -v
# Run and stop on first failure
uv run pytest -x
# Run and show local variables on failure
uv run pytest -lBusiness Logic Coverage Target: >90%
Core modules must maintain high coverage:
state.py: State management (target: 90%+, current: 85%)data_manager.py: Data operations and API integration (target: 90%+, current: 97%)duplicate_detector.py: Duplicate detection (target: 95%+, current: 84%)view_presenter.py: Presentation logic (100% - keep at 100%)time_navigator.py: Time period calculations (100% - keep at 100%)commit_orchestrator.py: DataFrame updates (100% - CRITICAL, keep at 100%)
UI layer coverage is less critical but still valuable.
View coverage report:
uv run pytest --cov --cov-report=html
open htmlcov/index.html- Write tests first for new features
- Run tests to verify they fail
- Implement the feature
- Run tests to verify they pass
- Refactor while keeping tests green
CRITICAL: All code quality checks MUST pass before committing. This ensures consistent code quality and prevents regressions.
# 1. Run full test suite
uv run pytest -v
# 2. Type checking (pyright)
uv run pyright moneyflow/
# 3. Code formatting (ruff format)
uv run ruff format --check moneyflow/ tests/
# 4. Linting (ruff check)
uv run ruff check moneyflow/ tests/
# 5. Markdown formatting (if docs changed)
markdownlint --config .markdownlint.json README.md 'docs/**/*.md'
.github/scripts/check-arrow-lists.shAll checks must pass with zero errors before creating a commit or release.
Note: Markdown checks (5) only need to run if you've modified documentation files (README.md or docs/).
# Auto-format code
uv run ruff format moneyflow/ tests/
# Auto-fix linting issues
uv run ruff check --fix moneyflow/ tests/The project uses MkDocs with Material theme for documentation.
Starting the docs server:
# IMPORTANT: Always delete site/ first to avoid stale content
rm -rf site/ && uv run mkdocs serve
# Or on a specific port
rm -rf site/ && uv run mkdocs serve -a 127.0.0.1:8002Generating/regenerating screenshots:
# Generate all screenshots
uv run python scripts/generate_screenshots.py
# Generate only specific screenshots (by filename filter)
uv run python scripts/generate_screenshots.py --filter amazon-matching
# IMPORTANT: After regenerating, restart mkdocs for changes to appearLive Reload Configuration:
mkdocs.ymlincludes awatchsection that monitorsdocs/assets/screenshots/- Static assets (SVG, PNG) should auto-reload when changed
- If live reload stops working, check the click version (see Known Issues below)
Known Issues:
-
Click 8.3.0 breaks live reload: MkDocs file watching is broken with
click>=8.3.0. This is constrained inpyproject.toml(click>=8.1.0,<8.3.0). If the constraint is removed in the future, check mkdocs issue #4032 for status before upgrading. -
Stale screenshots/images: If docs show old images after regenerating:
- Delete
site/directory:rm -rf site/ - Restart
mkdocs serve - Hard refresh browser (
Cmd+Shift+R/Ctrl+Shift+R) - Try a different port to bypass browser cache
- Delete
-
HTML img tags need different paths: When using
<img>tags in markdown (for tables), paths resolve relative to the page URL, not the source file. Use../../assets/for pages in subdirectories likeguide/navigation.md.
pyproject.tomlcontains configuration for ruff and pyrightmonarchmoney.pyis excluded from ruff checks (external vendor code)- Line length: 100 characters
- Target Python version: 3.11
- Use type hints for all function signatures
- No inline imports: All imports must be at the top of the file, not inside functions/methods
- Inline imports are slower (import happens on every call)
- Harder to see dependencies at a glance
- Exception: Circular import issues (rare)
- Document complex logic with comments explaining "why", not "what"
- Keep functions focused - Single responsibility, easy to test
- Use meaningful variable names - Prefer clarity over brevity
The monarchmoney.py file is kept separate to make it easy to generate diffs for upstream contributions:
# Generate a diff against the original
cd moneyflow
diff monarchmoney.py /path/to/original/monarchmoney.py > my_changes.patch- Credentials are encrypted with Fernet (AES-128)
- Never commit
.mm/directory (session data) - Never commit
~/.moneyflow/directory (encrypted credentials) - Never commit test data with real credentials
- See SECURITY.md for full security documentation
- Create tests in
tests/test_*.py - Implement in appropriate module
- Update keyboard shortcuts in
keybindings.py - Update README.md with new functionality
- Run full test suite
# Enable Textual dev tools
uv run textual console
# Then in another terminal
uv run moneyflow
# View logs in the console# Add new dependency to pyproject.toml manually, then:
uv sync
# Or add directly
uv add package-name
# Update all dependencies
uv lock --upgrade
uv syncCRITICAL: Never commit without running all code quality checks first!
IMPORTANT: When working with Claude Code or AI assistants:
- ✅ AI can create commits locally
- ❌ AI must NEVER push to git without explicit user permission
- ❌ AI must NEVER create new branches unless explicitly asked by the user
- ❌ AI must NEVER amend commits unless explicitly asked by the user
- 💡 User should review commits before pushing
# MANDATORY: Run all code quality checks before committing
uv run pytest -v # All tests must pass
uv run pyright moneyflow/ # Type checking must be clean
uv run ruff format --check moneyflow/ tests/ # Code must be formatted
uv run ruff check moneyflow/ tests/ # Linting must pass
# Only if ALL checks pass, then commit
git add -A
git commit -m "Descriptive commit message"
# WAIT for user approval before pushing
# git push origin main
# Use conventional commit format
# feat: New feature
# fix: Bug fix
# test: Adding tests
# refactor: Code refactoring
# docs: Documentation updatesPre-commit Checklist (ALL must pass):
- All tests pass (
uv run pytest -v) - Type checking passes (
uv run pyright moneyflow/) - Code formatting passes (
uv run ruff format --check moneyflow/ tests/) - Linting passes (
uv run ruff check moneyflow/ tests/) - Markdown formatting passes (if docs changed):
markdownlint --config .markdownlint.json README.md 'docs/**/*.md'.github/scripts/check-arrow-lists.sh
- Coverage hasn't decreased
- No debug print statements left in code
- Updated tests for any changed behavior
- Ran with real test data if changing API logic
Pyright is integrated for static type analysis. Use comprehensive type hints for all new code.
# Type-check specific module
uv run pyright moneyflow/view_presenter.py
# Type-check all application code
uv run pyright moneyflow/
# Type checking is also run in CI on every pushType Hint Requirements:
- All function signatures must have full type hints
- Use
TypedDictfor complex dictionaries - Use
Literaltypes for string enums - Use
NamedTuplefor data transfer objects - Prefer
Callable[[Args], Return]for function types
- Bulk fetch transactions on startup (1000 per batch)
- All aggregations done locally with Polars
- Batch API updates to minimize round trips
- Cache data in AppState to avoid re-fetching
- Add transaction deletion with confirmation
- Implement time range picker UI
- Add CSV export functionality
- Improve duplicate detection algorithm
- Add split transaction support
- Implement transaction notes editing