This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
YNAB CLI is a command-line interface for You Need a Budget (YNAB) designed for LLMs and developers to interface with YNAB budgets. The CLI outputs JSON by default and provides built-in filtering capabilities to reduce dependency on external tools.
bun run dev # Run CLI in development mode (no build required)
bun run build # Build for production using tsup
bun run link # Build and link globally (makes `ynab` available system-wide)
bun run start # Run built CLI from dist/
# Testing the CLI locally
bun run src/cli.ts <command> # Run directly without building
bun dist/cli.js <command> # Run after buildingbun run typecheck # Type check without emitting files
bun run lint # Lint TypeScript files in src/
bun test # Run vitest testsThe CLI follows a command-based architecture built on Commander.js:
- src/cli.ts: Entry point that registers all commands and handles global flags (
--compact,--output,--budget) - src/commands/: Each file exports a
create*Command()function that returns a Commander Command- Commands: auth, user, budgets, accounts, categories, transactions, scheduled, payees, months, api
- src/lib/: Shared utilities and core functionality
- api-client.ts: Main YNAB API wrapper (
YnabClientclass) - single source of truth for API calls - auth.ts: OS keychain integration via @napi-rs/keyring for secure token storage
- config.ts: Application config management via
confpackage (stores default budget ID) - output.ts: JSON output formatting with automatic milliunit conversion
- utils.ts: Currency conversion, date formatting, filtering, field selection
- errors.ts: Centralized error handling for YNAB API errors
- command-utils.ts: Shared command helpers
- prompts.ts: Interactive prompts using inquirer
- api-client.ts: Main YNAB API wrapper (
- src/types/: TypeScript type definitions
- Priority order: CLI keychain →
YNAB_API_KEYenv var - Default budget:
--budgetflag → config file →YNAB_BUDGET_IDenv var - Tokens stored in OS keychain via @napi-rs/keyring (service: 'ynab-cli', account: 'access-token')
Critical: All amounts are automatically converted between YNAB's milliunit format (1000 = $1.00) and dollars:
- Input: User provides amounts in dollars (e.g.,
--min-amount 100) - Internal: Code uses
amountToMilliunits()to convert dollars to milliunits for API calls - Output:
convertMilliunitsToAmounts()recursively converts all amount fields in JSON output to dollars - Fields converted:
amount,balance,*_balance,budgeted,activity,available,goal_target,*_amount
All commands use outputJson() from src/lib/output.ts which:
- Recursively converts milliunits to dollar amounts
- Formats as JSON (pretty or compact based on
--compactflag) - Writes to file or stdout based on
--outputflag
List commands return arrays directly (not wrapped in objects) for easy piping to other tools.
All API calls go through YnabClient.withErrorHandling() which catches errors and passes them to handleYnabError() in src/lib/errors.ts. Errors are output as JSON:
{
"error": {
"name": "error_name",
"detail": "Error detail",
"statusCode": 400
}
}- Create or modify file in src/commands/
- Export a
create*Command()function that returns a Commander Command - Register in src/cli.ts
- Use
clientfrom src/lib/api-client.ts for API calls - Use
outputJson()oroutputSuccess()for JSON output - Handle milliunits conversion:
- Input: Use
amountToMilliunits()for user-provided amounts - Output: Automatic via
outputJson()- don't manually convert
- Input: Use
YNAB API does not support:
- Creating categories or category groups
- Creating payees
- Creating or updating accounts (beyond initial creation)
These operations must be done through YNAB's web/mobile apps.
Tests use Vitest. Run with bun test.
- tsup.config.ts: Bundles src/cli.ts → dist/cli.js as ESM
- package.json: Type is "module" (ESM), main is dist/cli.js, bin is "ynab"
- Shebang
#!/usr/bin/env bunin src/cli.ts makes dist/cli.js directly executable