Files
actual-actualbudget/AGENTS.md
Matiss Janis Aboltins 73fa068fe9 [AI] Establish AI agent commit and PR guidelines (#7153)
* [AI] Extract PR/commit rules into shared agent skill

Deduplicate PR and commit instructions from AGENTS.md into a standalone
skill file at .github/agents/pr-and-commit-rules.md. This single source
of truth is consumed by both Claude Code (via CLAUDE.md @-import) and
Cursor (via .cursor/rules/pr-and-commit.mdc with alwaysApply: true).

AGENTS.md now references the shared file instead of repeating the rules
in three separate sections.

https://claude.ai/code/session_01KkHg7MYXrTyDkTw6u98Vam

* Add release notes for PR #7153

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-03-08 18:16:46 +00:00

20 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)

⚠️ CRITICAL REQUIREMENT: AI-Generated Commit Messages and PR Titles

ALL commit messages and PR titles MUST be prefixed with [AI]. No exceptions.

See PR and Commit Rules for the full specification, including git safety rules, pre-commit checklist, and PR workflow.

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 in packages/loot-core/
  • typography: Typography rules
  • prefer-if-statement: Prefers explicit if statements

10. docs (packages/docs/)

Documentation website built with Docusaurus.

  • Documentation is part of the monorepo
  • Built with Docusaurus 3
  • Commands:
    yarn workspace docs start
    yarn workspace docs build
    yarn start:docs  # From root
    

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

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

5. Financial Number Typography

  • Wrap standalone financial numbers with FinancialText or apply styles.tnum directly if wrapping is not possible

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:

  • The project uses React Compiler (babel-plugin-react-compiler) in the desktop-client. The compiler auto-memoizes component bodies, so you can omit manual useCallback, useMemo, and React.memo when adding or refactoring code; prefer inline callbacks and values unless a stable identity is required by a non-compiled dependency.
  • 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:

  • 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:

See PR and Commit Rules for complete git safety rules, commit message requirements, and PR workflow.

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
  • /packages/docs/ - Documentation website (Docusaurus)

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)
  • packages/docs/docs/ - Documentation source files (Markdown)
  • packages/docs/docs/contributing/ - Developer documentation

Common Development Tasks

Running Specific Tests

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

# 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 >=22)
  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:

  • Commit and PR rules followed (see PR and Commit Rules)
  • yarn typecheck passes
  • yarn lint:fix has been run
  • Relevant tests pass
  • 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

See PR and Commit Rules for complete PR creation rules, including title prefix requirements, labeling, and PR template handling.

Code Review Guidelines

When performing code reviews (especially for LLM agents): see CODE_REVIEW_GUIDELINES.md for specific guidelines.

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: >=22
  • 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.

Cursor Cloud specific instructions

Services overview

Service Command Port Required
Web Frontend (Vite) yarn start 3001 Yes
Sync Server yarn start:server-dev 5006 Optional (sync features only)

All storage is SQLite (file-based via better-sqlite3). No external databases or services are needed.

Running the app

  • yarn start builds the plugins-service worker, loot-core browser backend, and starts the Vite dev server on port 3001.
  • yarn start:server-dev starts both the sync server (port 5006) and the web frontend together.
  • The Vite HMR dev server serves many unbundled modules. In constrained environments, the browser may hit ERR_INSUFFICIENT_RESOURCES. If that happens, use yarn build:browser followed by serving the built output from packages/desktop-client/build/ with proper COOP/COEP headers (Cross-Origin-Opener-Policy: same-origin, Cross-Origin-Embedder-Policy: require-corp).

Lint, test, typecheck

Standard commands documented in package.json scripts and the Quick Start section above:

  • yarn lint / yarn lint:fix (uses oxlint + oxfmt)
  • yarn test (lage across all workspaces)
  • yarn typecheck (tsc + lage typecheck)

Testing and previewing the app

When running the app for manual testing or demos, use "View demo" on the initial setup screen (after selecting "Don't use a server"). This creates a test budget pre-populated with realistic sample data (accounts, transactions, categories, and budgeted amounts), which is far more useful than starting with an empty budget.

Gotchas

  • The engines field requires Node.js >=22 and Yarn ^4.9.1. The .nvmrc specifies v22/*.
  • Pre-commit hook runs lint-staged (oxfmt + oxlint) via Husky. Run yarn prepare once after install to set up hooks.
  • Lage caches test results in .lage/. If tests behave unexpectedly, clear with rm -rf .lage.
  • Native modules (better-sqlite3, bcrypt) require build tools (gcc, make, python3). These are pre-installed in the Cloud VM.
  • All yarn commands must be run from the repository root, never from child workspaces.