Files
actual/AGENTS.md
2025-10-21 08:58:26 +02:00

16 KiB

AGENTS.md - Guide for AI Agents Working with Actual Budget

This guide provides comprehensive information for AI agents (like Cursor) working with the Actual Budget codebase.

Project Overview

Actual Budget is a local-first personal finance tool written in TypeScript/JavaScript. It's 100% free and open-source with synchronization capabilities across devices.

Quick Start Commands

Essential Commands (Run from Root)

# Type checking (ALWAYS run before committing)
yarn typecheck

# Linting and formatting (with auto-fix)
yarn lint:fix

# Run all tests
yarn test

# Start development server (browser)
yarn start

# Start with sync server
yarn start:server-dev

# Start desktop app development
yarn start:desktop

Important Rules

  • ALWAYS run yarn commands from the root directory - never run them in child workspaces
  • Use yarn workspace <workspace-name> run <command> for workspace-specific tasks
  • Tests run once and exit by default (using vitest --run)

Task Orchestration with Lage

The project uses lage (a task runner for JavaScript monorepos) to efficiently run tests and other tasks across multiple workspaces:

  • Parallel execution: Runs tests in parallel across workspaces for faster feedback
  • Smart caching: Caches test results to skip unchanged packages (cached in .lage/ directory)
  • Dependency awareness: Understands workspace dependencies and execution order
  • Continues on error: Uses --continue flag to run all packages even if one fails

Lage Commands:

# Run all tests across all packages
yarn test                    # Equivalent to: lage test --continue

# Run tests without cache (for debugging/CI)
yarn test:debug              # Equivalent to: lage test --no-cache --continue

Configuration is in lage.config.js at the project root.

Architecture & Package Structure

Core Packages

1. loot-core (packages/loot-core/)

The core application logic that runs on any platform.

  • Business logic, database operations, and calculations

  • Platform-agnostic code

  • Exports for both browser and node environments

  • Test commands:

    # Run all loot-core tests
    yarn workspace loot-core run test
    
    # Or run tests across all packages using lage
    yarn test
    

2. desktop-client (packages/desktop-client/ - aliased as @actual-app/web)

The React-based UI for web and desktop.

  • React components using functional programming patterns

  • E2E tests using Playwright

  • Vite for bundling

  • Commands:

    # Development
    yarn workspace @actual-app/web start:browser
    
    # Build
    yarn workspace @actual-app/web build
    
    # E2E tests
    yarn workspace @actual-app/web e2e
    
    # Visual regression tests
    yarn workspace @actual-app/web vrt
    

3. desktop-electron (packages/desktop-electron/)

Electron wrapper for the desktop application.

  • Window management and native OS integration
  • E2E tests for Electron-specific features

4. api (packages/api/ - aliased as @actual-app/api)

Public API for programmatic access to Actual.

  • Node.js API

  • Designed for integrations and automation

  • Commands:

    # Build
    yarn workspace @actual-app/api build
    
    # Run tests
    yarn workspace @actual-app/api test
    
    # Or use lage to run all tests
    yarn test
    

5. sync-server (packages/sync-server/ - aliased as @actual-app/sync-server)

Synchronization server for multi-device support.

  • Express-based server
  • Currently transitioning to TypeScript (mostly JavaScript)
  • Commands:
    yarn workspace @actual-app/sync-server start
    

6. component-library (packages/component-library/ - aliased as @actual-app/components)

Reusable React UI components.

  • Shared components like Button, Input, Menu, etc.
  • Theme system and design tokens
  • Icons (375+ icons in SVG/TSX format)

7. crdt (packages/crdt/ - aliased as @actual-app/crdt)

CRDT (Conflict-free Replicated Data Type) implementation for data synchronization.

  • Protocol buffers for serialization
  • Core sync logic

8. plugins-service (packages/plugins-service/)

Service for handling plugins/extensions.

9. eslint-plugin-actual (packages/eslint-plugin-actual/)

Custom ESLint rules specific to Actual.

  • no-untranslated-strings: Enforces i18n usage
  • prefer-trans-over-t: Prefers Trans component over t() function
  • prefer-logger-over-console: Enforces using logger instead of console
  • typography: Typography rules
  • prefer-if-statement: Prefers explicit if statements

Development Workflow

1. Making Changes

When implementing changes:

  1. Read relevant files to understand current implementation
  2. Make focused, incremental changes
  3. Run type checking: yarn typecheck
  4. Run linting: yarn lint:fix
  5. Run relevant tests
  6. Fix any linter errors that are introduced

2. Testing Strategy

Unit Tests (Vitest)

The project uses lage for running tests across all workspaces efficiently.

# Run all tests across all packages (using lage)
yarn test

# Run tests without cache (for debugging)
yarn test:debug

# Run tests for a specific package
yarn workspace loot-core run test

# Run a specific test file (watch mode)
yarn workspace loot-core run test path/to/test.test.ts

E2E Tests (Playwright)

# Run E2E tests for web
yarn e2e

# Desktop Electron E2E (includes full build)
yarn e2e:desktop

# Visual regression tests
yarn vrt

# Visual regression in Docker (consistent environment)
yarn vrt:docker

# Run E2E tests for a specific package
yarn workspace @actual-app/web e2e

Testing Best Practices:

  • Minimize mocked dependencies - prefer real implementations
  • Use descriptive test names
  • Vitest globals are available: describe, it, expect, beforeEach, etc.
  • For sync-server tests, globals are explicitly defined in config

3. Type Checking

TypeScript configuration uses:

  • Incremental compilation
  • Strict type checking with typescript-strict-plugin
  • Platform-specific exports in loot-core (node vs browser)

Always run yarn typecheck before committing.

4. Internationalization (i18n)

  • Use Trans component instead of t() function when possible
  • All user-facing strings must be translated
  • Generate i18n files: yarn generate:i18n
  • Custom ESLint rules enforce translation usage

Code Style & Conventions

TypeScript Guidelines

Type Usage:

  • Use TypeScript for all code
  • Prefer type over interface
  • Avoid enum - use objects or maps
  • Avoid any or unknown unless absolutely necessary
  • Look for existing type definitions in the codebase
  • Avoid type assertions (as, !) - prefer satisfies
  • Use inline type imports: import { type MyType } from '...'

Naming:

  • Use descriptive variable names with auxiliary verbs (e.g., isLoaded, hasError)
  • Named exports for components and utilities (avoid default exports except in specific cases)

Code Structure:

  • Functional and declarative programming patterns - avoid classes
  • Use the function keyword for pure functions
  • Prefer iteration and modularization over code duplication
  • Structure files: exported component/page, helpers, static content, types
  • Create new components in their own files

React Patterns:

  • Don't use React.FunctionComponent or React.FC - type props directly
  • Don't use React.* patterns - use named imports instead
  • Use <Link> instead of <a> tags
  • Use custom hooks from src/hooks (not react-router directly):
    • useNavigate() from src/hooks (not react-router)
    • useDispatch(), useSelector(), useStore() from src/redux (not react-redux)
  • Avoid unstable nested components
  • Use satisfies for type narrowing

JSX Style:

  • Declarative JSX, minimal and readable
  • Avoid unnecessary curly braces in conditionals
  • Use concise syntax for simple statements
  • Prefer explicit expressions (condition && <Component />)

Import Organization

Imports are automatically organized by ESLint with the following order:

  1. React imports (first)
  2. Built-in Node.js modules
  3. External packages
  4. Actual packages (loot-core, @actual-app/components - legacy pattern loot-design may appear in old code)
  5. Parent imports
  6. Sibling imports
  7. Index imports

Always maintain newlines between import groups.

Platform-Specific Code

  • Don't directly reference platform-specific imports (.api, .web, .electron)
  • Use conditional exports in loot-core for platform-specific code
  • Platform resolution happens at build time via package.json exports

Restricted Patterns

Never:

  • Use console.* (use logger instead - enforced by ESLint)
  • Import from uuid without destructuring: use import { v4 as uuidv4 } from 'uuid'
  • Import colors directly - use theme instead
  • Import @actual-app/web/* in loot-core

Git Commands:

  • Never update git config
  • Never run destructive git operations (force push, hard reset) unless explicitly requested
  • Never skip hooks (--no-verify, --no-gpg-sign)
  • Never force push to main/master
  • Never commit unless explicitly asked

File Structure Patterns

Typical Component File

import { type ComponentType } from 'react';
// ... other imports

type MyComponentProps = {
  // Props definition
};

export function MyComponent({ prop1, prop2 }: MyComponentProps) {
  // Component logic
  return (
    // JSX
  );
}

Test File

import { describe, it, expect, beforeEach } from 'vitest';
// ... imports

describe('ComponentName', () => {
  it('should behave as expected', () => {
    // Test logic
    expect(result).toBe(expected);
  });
});

Important Directories & Files

Configuration Files

  • /package.json - Root workspace configuration, scripts
  • /lage.config.js - Lage task runner configuration
  • /eslint.config.mjs - ESLint configuration (flat config format)
  • /tsconfig.json - Root TypeScript configuration
  • /.cursorignore, /.gitignore - Ignored files
  • /yarn.lock - Dependency lockfile (Yarn 4)

Documentation

  • /README.md - Project overview
  • /CONTRIBUTING.md - Points to community docs
  • /upcoming-release-notes/ - Release notes for next version
  • /CODEOWNERS - Code ownership definitions

Build Artifacts (Don't Edit)

  • packages/*/lib-dist/ - Built output
  • packages/*/dist/ - Built output
  • packages/*/build/ - Built output
  • packages/desktop-client/playwright-report/ - Test reports
  • packages/desktop-client/test-results/ - Test results
  • .lage/ - Lage task runner cache (improves test performance)

Key Source Directories

  • packages/loot-core/src/client/ - Client-side core logic
  • packages/loot-core/src/server/ - Server-side core logic
  • packages/loot-core/src/shared/ - Shared utilities
  • packages/loot-core/src/types/ - Type definitions
  • packages/desktop-client/src/components/ - React components
  • packages/desktop-client/src/hooks/ - Custom React hooks
  • packages/desktop-client/e2e/ - End-to-end tests
  • packages/component-library/src/ - Reusable components
  • packages/component-library/src/icons/ - Icon components (auto-generated, don't edit)

Common Development Tasks

Running Specific Tests

# Run all tests across all packages (recommended)
yarn test

# Unit test for a specific file in loot-core (watch mode)
yarn workspace loot-core run test src/path/to/file.test.ts

# E2E test for a specific file
yarn workspace @actual-app/web run playwright test accounts.test.ts --browser=chromium

Building for Production

# Browser build
yarn build:browser

# Desktop build
yarn build:desktop

# API build
yarn build:api

# Sync server build
yarn build:server

Type Checking Specific Packages

TypeScript uses project references. Run yarn typecheck from root to check all packages.

Debugging Tests

# Run tests in debug mode (without parallelization)
yarn test:debug

# Run specific E2E test with headed browser
yarn workspace @actual-app/web run playwright test --headed --debug accounts.test.ts

Working with Icons

Icons in packages/component-library/src/icons/ are auto-generated. Don't manually edit them.

Troubleshooting

Type Errors

  1. Run yarn typecheck to see all type errors
  2. Check if types are imported correctly
  3. Look for existing type definitions in packages/loot-core/src/types/
  4. Use satisfies instead of as for type narrowing

Linter Errors

  1. Run yarn lint:fix to auto-fix many issues
  2. Check ESLint output for specific rule violations
  3. Custom rules:
    • actual/no-untranslated-strings - Add i18n
    • actual/prefer-trans-over-t - Use Trans component
    • actual/prefer-logger-over-console - Use logger
    • Check eslint.config.mjs for complete rules

Test Failures

  1. Check if test is running in correct environment (node vs web)
  2. For Vitest: check vitest.config.ts or vitest.web.config.ts
  3. For Playwright: check playwright.config.ts
  4. Ensure mock minimization - prefer real implementations
  5. Lage cache issues: Clear cache with rm -rf .lage if tests behave unexpectedly
  6. Tests continue on error: With --continue flag, all packages run even if one fails

Import Resolution Issues

  1. Check tsconfig.json for path mappings
  2. Check package.json exports field (especially for loot-core)
  3. Verify platform-specific imports (.web, .electron, .api)
  4. Use absolute imports in desktop-client (enforced by ESLint)

Build Failures

  1. Clean build artifacts: rm -rf packages/*/dist packages/*/lib-dist packages/*/build
  2. Reinstall dependencies: yarn install
  3. Check Node.js version (requires >=20)
  4. Check Yarn version (requires ^4.9.1)

Testing Patterns

Unit Tests

  • Located alongside source files or in __tests__ directories
  • Use .test.ts, .test.tsx, .spec.js extensions
  • Vitest is the test runner
  • Minimize mocking - prefer real implementations

E2E Tests

  • Located in packages/desktop-client/e2e/
  • Use Playwright test runner
  • Visual regression snapshots in *-snapshots/ directories
  • Page models in e2e/page-models/ for reusable page interactions
  • Mobile tests have .mobile.test.ts suffix

Visual Regression Tests (VRT)

  • Snapshots stored per test file in *-snapshots/ directories
  • Use Docker for consistent environment: yarn vrt:docker

Additional Resources

Code Quality Checklist

Before committing changes, ensure:

  • yarn typecheck passes
  • yarn lint:fix has been run
  • Relevant tests pass
  • No new console.* usage (use logger)
  • User-facing strings are translated
  • Prefer type over interface
  • Named exports used (not default exports)
  • Imports are properly ordered
  • Platform-specific code uses proper exports
  • No unnecessary type assertions

Pull Request Guidelines

When creating pull requests:

  • AI-Generated PRs: If you create a PR using AI assistance, add the "AI generated" label to the pull request. This helps maintainers understand the nature of the contribution.

Performance Considerations

  • Bundle Size: Check with rollup-plugin-visualizer
  • Type Checking: Uses incremental compilation
  • Testing: Tests run in parallel by default
  • Linting: ESLint caches results for faster subsequent runs

Workspace Commands Reference

# List all workspaces
yarn workspaces list

# Run command in specific workspace
yarn workspace <workspace-name> run <command>

# Run command in all workspaces
yarn workspaces foreach --all run <command>

# Install production dependencies only (for server deployment)
yarn install:server

Environment Requirements

  • Node.js: >=20
  • Yarn: ^4.9.1 (managed by packageManager field)
  • Browser Targets: Electron >= 35.0, modern browsers (see browserslist)

Migration Notes

The codebase is actively being migrated:

  • JavaScript → TypeScript: sync-server is in progress
  • Classes → Functions: Prefer functional patterns
  • React.* → Named Imports: Legacy React.* patterns being removed

When working with older code, follow the newer patterns described in this guide.