Local-only, privacy-first personal finance CLI
Project description
Gilt
A local-only, privacy-first command-line tool for managing personal gilt data from multiple bank accounts.
Core Principles
- Local-only: All processing happens on your machine. No network I/O, no cloud services.
- Privacy-first: Raw transaction data never leaves your machine. Only you have access to your financial records.
- Deterministic: Re-running commands with the same inputs produces identical results.
- Safe by default: All commands that modify files are dry-run by default. Use
--writeto persist changes. - Minimal dependencies: Standard Python tooling with a small set of trusted libraries.
Features
Transaction Management
- Normalize raw bank CSV exports into a standardized format
- Track transactions across multiple accounts (checking, savings, credit cards, lines of credit)
- Link inter-account transfers automatically with intelligent matching
- Detect duplicates using local LLM inference (privacy-preserving)
- Annotate transactions with custom notes and categories
- View year-to-date transactions in rich formatted tables
Budgeting & Analysis
- Categorize transactions with hierarchical categories and subcategories
- Auto-categorize new transactions using machine learning (learns from your categorization history)
- Track budgets by category with monthly or yearly periods
- Monitor spending against budgets with visual indicators
- Generate reports for budget analysis and spending patterns
- Diagnose issues with orphaned categories and uncategorized transactions
Event Sourcing Architecture
- Complete audit trail of all transactions and categorizations
- Time-travel queries to view historical budget states
- ML-powered learning from user feedback to improve duplicate detection
- Immutable event log for data integrity and analysis
- Projection rebuilding for flexible reporting and experimentation
Installation
Prerequisites
- Python 3.13 or higher
- Virtual environment (recommended)
Setup
- Clone or navigate to the repository:
cd /path/to/gilt
- Create and activate a virtual environment:
python3.13 -m venv .venv
source .venv/bin/activate
- Install the package in editable mode with development tools:
pip install -e .[dev]
- Verify installation:
gilt --help
Directory Structure
gilt/
├── config/
│ └── accounts.yml # Account configuration (optional but recommended)
├── ingest/ # Raw bank CSV exports (immutable inputs)
│ ├── 2025-10-13-mybank-chequing.csv
│ ├── 2025-10-13-mybank-cc.csv
│ └── ...
├── data/
│ └── accounts/ # Normalized per-account ledgers (outputs)
│ ├── MYBANK_CHQ.csv
│ ├── MYBANK_CC.csv
│ ├── BANK2_BIZ.csv
│ └── BANK2_LOC.csv
└── reports/ # Generated reports/plots (optional)
File Conventions
- ingest/: Place raw CSV files exported from your bank here. These files are never modified by the tool.
- data/accounts/: Standardized ledger files, one per account. Safe to regenerate via the
ingestcommand. - config/accounts.yml: Maps raw CSV filenames to account IDs and provides parsing hints.
Configuration
Create config/accounts.yml to define your accounts:
accounts:
- account_id: MYBANK_CHQ
description: "MyBank Chequing"
source_patterns:
- "*mybank*chequing*.csv"
- "*mybank*chq*.csv"
import_hints:
header_row: 0
date_format: "%m/%d/%Y"
- account_id: MYBANK_CC
description: "MyBank Credit Card"
source_patterns:
- "*mybank*cc*.csv"
- "*mybank*creditcard*.csv"
- account_id: BANK2_BIZ
description: "SecondBank Business Chequing"
source_patterns:
- "*bank2*business*.csv"
- account_id: BANK2_LOC
description: "SecondBank Line of Credit"
source_patterns:
- "*bank2*line*.csv"
- "*bank2*loc*.csv"
Commands
All commands are local-only and respect your privacy. Commands that modify files default to dry-run mode—use --write to persist changes.
accounts - List Available Accounts
Display all configured accounts and their descriptions.
# List accounts from config
gilt accounts
# Use custom config location
gilt accounts --config /path/to/accounts.yml
# Use custom data directory
gilt accounts --data-dir /path/to/ledgers
Options:
--config PATH: Path to accounts.yml (default:config/accounts.yml)--data-dir PATH: Directory containing ledger CSVs (default:data/accounts)
ingest - Normalize Raw Bank CSVs
Convert raw bank CSV exports into standardized per-account ledger files. This command:
- Discovers CSV files in
ingest/directory - Maps them to accounts using patterns from
config/accounts.yml - Normalizes column names and formats to a standard schema
- Generates deterministic transaction IDs
- Links inter-account transfers automatically
# Dry-run (preview what would happen)
gilt ingest
# Actually write the normalized ledgers
gilt ingest --write
# Use custom paths
gilt ingest --config config/accounts.yml \
--ingest-dir ingest \
--output-dir data/accounts \
--write
Options:
--config PATH: Path to accounts.yml (default:config/accounts.yml)--ingest-dir PATH: Directory with raw bank CSVs (default:ingest)--output-dir PATH: Directory to write ledgers (default:data/accounts)--write: Persist changes (default: dry-run)
Workflow:
- Export CSVs from your bank's website
- Place them in
ingest/directory - Run
gilt ingestto preview - Run
gilt ingest --writeto process and save
Transfer Linking: After ingestion, the tool automatically identifies transfers between your accounts (e.g., moving money from checking to credit card) and annotates both sides of the transfer with metadata.
ytd - View Year-to-Date Transactions
Display transactions for a specific account in a rich formatted table.
# Show current year's transactions for MYBANK_CHQ
gilt ytd --account MYBANK_CHQ
# Show transactions for a specific year
gilt ytd --account MYBANK_CC --year 2024
# Limit to last 20 transactions
gilt ytd --account BANK2_BIZ --limit 20
# Specify default currency for legacy rows
gilt ytd --account MYBANK_CHQ --default-currency CAD
Options:
--account, -a ACCOUNT: Account ID to display (required)--year, -y YEAR: Year to filter (default: current year)--limit, -n N: Maximum number of rows to show--default-currency CURR: Fallback currency for legacy rows (e.g., CAD)--data-dir PATH: Directory containing ledgers (default:data/accounts)
Output: A rich formatted table showing:
- Transaction ID (first 8 characters)
- Date
- Description
- Amount (with currency)
- Category/Subcategory
- Notes
note - Annotate Transactions
Attach or update notes on one or more transactions. Supports both single-transaction and batch modes.
Single Transaction Mode
Use --txid to target one specific transaction by its ID prefix (as shown in ytd output):
# Dry-run: preview the change
gilt note --account MYBANK_CHQ --txid a1b2c3d4 --note "Reimbursable expense"
# Write the note to disk
gilt note --account MYBANK_CHQ --txid a1b2c3d4 --note "Reimbursable expense" --write
Batch Mode
Use --description, --desc-prefix, or --pattern with optional --amount to target multiple recurring transactions:
# Match exact description
gilt note --account MYBANK_CC \
--description "SPOTIFY" \
--note "Music subscription" \
--write
# Match by description prefix (case-insensitive)
gilt note --account MYBANK_CHQ \
--desc-prefix "TRANSFER FROM" \
--note "Inter-account transfer" \
--write
# Match by regex pattern (case-insensitive)
gilt note --account MYBANK_CHQ \
--pattern "Payment - WWW Payment - \d+ EXAMPLE UTILITY" \
--note "Hydro bill" \
--write
# Match description and specific amount
gilt note --account BANK2_BIZ \
--description "Monthly Fee" \
--amount -12.95 \
--note "Account maintenance" \
--write
# Batch mode with auto-confirm (skip prompts)
gilt note --account MYBANK_CC \
--desc-prefix "AMAZON" \
--note "Online purchases" \
--yes \
--write
Options:
--account, -a ACCOUNT: Account ID containing the transaction (required)--txid, -t ID: Transaction ID prefix (single mode)--description, -d TEXT: Exact description match (batch mode)--desc-prefix, -p TEXT: Description prefix match, case-insensitive (batch mode)--pattern TEXT: Regex pattern to match description, case-insensitive (batch mode)--amount, -m AMOUNT: Exact amount match (batch mode, optional)--note, -n TEXT: Note text to set (required)--yes, -y, -r: Skip confirmations in batch mode--data-dir PATH: Directory containing ledgers (default:data/accounts)--write: Persist changes (default: dry-run)
Budgeting & Categorization
The gilt tool includes powerful budgeting features for tracking and categorizing spending across all your accounts.
categories - List Categories
Display all defined categories with usage statistics from your transactions.
# List all categories with usage counts and totals
gilt categories
# Use custom config location
gilt categories --config /path/to/categories.yml
Options:
--config PATH: Path to categories.yml (default:config/categories.yml)--data-dir PATH: Directory containing ledgers (default:data/accounts)
Output: Shows a table with:
- Category and subcategory names
- Descriptions
- Budget amounts (if set)
- Transaction count per category
- Total spending per category
category - Manage Categories
Add, remove, or set budgets for categories.
# Add a new category
gilt category --add "Housing" --description "Housing and utilities" --write
# Add a subcategory
gilt category --add "Housing:Utilities" --description "Electric, gas, water" --write
# Set a budget
gilt category --set-budget "Dining Out" --amount 400 --period monthly --write
# Remove a category (requires confirmation if used)
gilt category --remove "Old Category" --write
# Force remove without confirmation
gilt category --remove "Housing:Utilities" --force --write
Options:
--add CATEGORY: Add a new category (supports "Category:Subcategory" syntax)--remove CATEGORY: Remove a category--set-budget CATEGORY: Set budget amount for a category--description TEXT: Description for new category--amount FLOAT: Budget amount (required with --set-budget)--period monthly|yearly: Budget period (default: monthly)--force: Skip confirmations when removing used categories--config PATH: Path to categories.yml (default:config/categories.yml)--data-dir PATH: Directory containing ledgers (default:data/accounts)--write: Persist changes (default: dry-run)
categorize - Categorize Transactions
Assign categories to one or many transactions.
Single Transaction Mode
# Categorize one transaction by ID
gilt categorize --account MYBANK_CHQ --txid a1b2c3d4 --category "Housing:Utilities" --write
Batch Mode
# Categorize all matching transactions by description prefix
gilt categorize --desc-prefix "SPOTIFY" --category "Entertainment:Music" --yes --write
# Categorize exact description match
gilt categorize --account MYBANK_CC --description "Monthly Fee" --category "Banking:Fees" --write
# Categorize by regex pattern
gilt categorize --pattern "Payment.*EXAMPLE UTILITY" --category "Housing:Utilities" --yes --write
# Categorize across all accounts
gilt categorize --desc-prefix "AMAZON" --category "Shopping:Online" --yes --write
# With amount filter
gilt categorize --account MYBANK_CHQ --description "Service Fee" --amount -12.95 --category "Banking:Fees" --write
Options:
--account, -a ACCOUNT: Account ID (omit to categorize across all accounts)--txid, -t ID: Transaction ID prefix (single mode)--description, -d TEXT: Exact description match (batch mode)--desc-prefix, -p TEXT: Description prefix match, case-insensitive (batch mode)--pattern TEXT: Regex pattern to match description, case-insensitive (batch mode)--amount, -m AMOUNT: Exact amount match (batch mode, optional)--category, -c CATEGORY: Category name (supports "Category:Subcategory" syntax)--subcategory, -s SUBCAT: Subcategory name (alternative to colon syntax)--yes, -y: Skip confirmations in batch mode--config PATH: Path to categories.yml (default:config/categories.yml)--data-dir PATH: Directory containing ledgers (default:data/accounts)--write: Persist changes (default: dry-run)
Category Syntax:
--category "Housing"- Category only--category "Housing:Utilities"- Category with subcategory (shorthand)--category "Housing" --subcategory "Utilities"- Alternative syntax
auto-categorize - Auto-Categorize Transactions with ML
Uses a machine learning classifier trained on your categorization history (from event store) to automatically categorize uncategorized transactions. Supports interactive review mode for approving predictions.
# Dry-run to see what would be auto-categorized
gilt auto-categorize
# Interactive mode - review and approve each prediction
gilt auto-categorize --interactive
# Apply auto-categorizations with default confidence threshold
gilt auto-categorize --write
# Use stricter confidence threshold (0.0-1.0, default 0.6)
gilt auto-categorize --confidence 0.8 --write
# Force retraining the model before predictions
gilt auto-categorize --retrain --write
Options:
--data-dir PATH: Directory containing ledger files (default:data/accounts)--confidence FLOAT: Minimum confidence threshold (0.0-1.0, default: 0.6)--interactive: Review predictions interactively before applying--retrain: Force retraining the model from event store--write: Persist approved categorizations (default: dry-run)
How it works:
- Loads the trained classifier (or trains from event store if
--retrainspecified) - Scans all ledger files for transactions without categories
- Predicts categories based on description and amount patterns
- In interactive mode, shows each prediction with confidence score and prompts for approval
- With
--write, persists approved categorizations to ledger files
Interactive commands:
a- Approve predictionr- Reject predictionm- Modify category manuallyq- Quit and save approved categorizations
Use case:
After manually categorizing some transactions, train the classifier to automatically categorize new transactions. Review predictions interactively to ensure accuracy, then apply with --write.
recategorize - Rename Categories in Ledgers
Rename a category across all ledger files. Useful when renaming categories in categories.yml to update existing transaction categorizations.
# Rename a category
gilt recategorize --from "Business" --to "Work" --write
# Rename a category with subcategory
gilt recategorize --from "Business:Subscriptions" --to "Work:Subscriptions" --write
# Preview changes without writing (dry-run)
gilt recategorize --from "Old Category" --to "New Category"
Options:
--from TEXT: Original category name (supports "Category:Subcategory" syntax) (required)--to TEXT: New category name (supports "Category:Subcategory" syntax) (required)--data-dir PATH: Directory containing ledgers (default:data/accounts)--write: Persist changes (default: dry-run)
How it works:
- Searches all ledger files for transactions with the specified category
- Shows a preview table of all matching transactions
- In dry-run mode (default), displays what would be changed
- With
--write, prompts for confirmation and updates all matching transactions
Use case:
When you rename a category in config/categories.yml, existing transactions in ledger files still reference the old category name. Use this command to bulk-update all historical transactions to use the new category name.
diagnose-categories - Find Orphaned Categories
Diagnose category issues by finding categories used in transactions that aren't defined in categories.yml. Helps identify orphaned, misspelled, or forgotten categories.
# Check all ledgers for orphaned categories
gilt diagnose-categories
# Use custom config location
gilt diagnose-categories --config /path/to/categories.yml
Options:
--config PATH: Path to categories.yml (default:config/categories.yml)--data-dir PATH: Directory containing ledgers (default:data/accounts)
Output:
- Exit code 0: All categories in transactions are defined in config (no issues)
- Exit code 1: Found orphaned categories (issues require attention)
Displays a table showing:
- Category and subcategory names found in transactions but not in config
- Transaction count for each orphaned category
- Suggested actions (add to config or fix typo)
- Similar category names if potential typos detected
Use case: After categorizing many transactions, this command helps you discover:
- Categories that need to be added to
categories.yml - Typos in category names that should be fixed with
recategorize - Subcategories that exist in transactions but not in the parent category definition
Example workflow:
# 1. Run diagnostic
gilt diagnose-categories
# 2a. If legitimate category, add it to config
gilt category --add "NewCategory" --write
# 2b. If typo, fix all transactions
gilt recategorize --from "Transporation" --to "Transportation" --write
# 3. Re-run diagnostic to verify
gilt diagnose-categories
uncategorized - Find Uncategorized Transactions
Display transactions that haven't been categorized yet.
# Show all uncategorized transactions
gilt uncategorized
# Filter by account
gilt uncategorized --account MYBANK_CHQ
# Filter by year
gilt uncategorized --year 2025
# Show only large transactions
gilt uncategorized --min-amount 100
# Limit results
gilt uncategorized --limit 50
Options:
--account, -a ACCOUNT: Account ID to filter (omit for all accounts)--year, -y YEAR: Year to filter--limit, -n N: Max number of transactions to show--min-amount AMOUNT: Minimum absolute amount to include--data-dir PATH: Directory containing ledgers (default:data/accounts)
Output: Transactions are sorted by description (to group similar ones), then date. This makes it easy to identify patterns and batch categorize recurring expenses.
budget - Budget Reporting
View budget vs actual spending by category.
# Current year summary
gilt budget
# Specific year
gilt budget --year 2025
# Specific month
gilt budget --year 2025 --month 10
# Single category detail
gilt budget --category "Dining Out" --year 2025
Options:
--year, -y YEAR: Year to report (default: current year)--month, -m MONTH: Month to report (1-12, requires --year)--category, -c CATEGORY: Filter to specific category--config PATH: Path to categories.yml (default:config/categories.yml)--data-dir PATH: Directory containing ledgers (default:data/accounts)
Output: Shows a table with:
- Category and subcategory breakdowns
- Budgeted amount (automatically prorated for monthly/yearly reports)
- Actual spending
- Remaining budget (or over-budget amount in red)
- Percentage of budget used (color-coded: green < 90%, yellow 90-100%, red > 100%)
Budget Proration:
- Monthly reports use monthly budgets directly, or divide yearly budgets by 12
- Yearly reports use yearly budgets directly, or multiply monthly budgets by 12
backfill-events - Migrate Existing Data to Event Store
Generate historical events from existing CSV ledgers and YAML configuration, enabling the full event-sourced architecture with complete audit trail.
# Preview migration (dry-run)
gilt backfill-events
# Execute migration with validation
gilt backfill-events --write
# Skip validation (faster, use with caution)
gilt backfill-events --write --no-validate
# Custom paths
gilt backfill-events \
--data-dir custom/data \
--categories-config custom/categories.yml \
--event-store custom/events.db \
--write
Options:
--data-dir PATH: Ledger CSV directory (default:data/accounts)--categories-config PATH: Categories configuration (default:config/categories.yml)--event-store PATH: Event store database (default:data/events.db)--projections-db PATH: Transaction projections (default:data/projections.db)--budget-projections-db PATH: Budget projections (default:data/budget_projections.db)--write: Execute migration (default: dry-run)--validate: Validate projections after backfill (default: true)
What it does:
- Generates
TransactionImportedandTransactionCategorizedevents from ledger CSVs - Generates
BudgetCreatedevents from categories.yml - Infers import timestamps from source filenames
- Validates that rebuilt projections match original data exactly
- Provides detailed migration statistics
Migration Statistics Example:
TransactionImported events: 1016
TransactionCategorized events: 446
BudgetCreated events: 6
Total events: 1468
✓ Transaction count matches: 1016
✓ Budget count matches: 6
✓ Sample transaction validation passed
✓ All validations passed
Use case: One-time migration to convert existing CSV/YAML data into the event-sourced architecture. Enables complete audit trail, time-travel queries, and ML-powered learning from user feedback.
rebuild-projections - Rebuild Transaction Projections from Events
Rebuild the transaction projection database from the event store. Useful after event store corruption or to experiment with new projection logic.
# Rebuild from scratch
gilt rebuild-projections --from-scratch
# Incremental rebuild (only new events)
gilt rebuild-projections
# Custom paths
gilt rebuild-projections \
--event-store custom/events.db \
--projections-db custom/projections.db \
--from-scratch
Options:
--event-store PATH: Event store database (default:data/events.db)--projections-db PATH: Projections database (default:data/projections.db)--from-scratch: Full rebuild instead of incremental
Use case: Recovery from projection database issues or testing new projection logic.
prompt-stats - View ML Learning Statistics
Display statistics about the duplicate detection learning system, including accuracy metrics and description preferences learned from user feedback.
# Show learning statistics
gilt prompt-stats
# Use custom event store
gilt prompt-stats --event-store custom/events.db
Options:
--event-store PATH: Event store database (default:data/events.db)
Output:
- Current prompt version and update count
- Accuracy metrics (TP/FP/TN/FN, precision, recall, F1 score)
- Description preference patterns
- Learned duplicate detection heuristics
Use case: Monitor how well the duplicate detection system is learning from your feedback.
Standard Ledger Schema
All normalized ledger files follow this schema (column order is fixed):
| Column | Type | Description |
|---|---|---|
transaction_id |
string | Deterministic hash (first 16 chars of SHA-256) |
date |
date | Transaction date (YYYY-MM-DD) |
description |
string | Transaction description from bank |
amount |
float | Signed amount (debits negative, credits positive) |
currency |
string | Currency code (e.g., CAD, USD) |
account_id |
string | Account identifier (e.g., MYBANK_CHQ) |
counterparty |
string | Initially same as description; can be refined |
category |
string | Transaction category |
subcategory |
string | Transaction subcategory |
notes |
string | User-added notes |
source_file |
string | Original CSV filename |
Additional metadata (like transfer links) is stored in a JSON metadata field when present.
Transaction IDs
Transaction IDs are deterministic and generated from:
SHA-256(account_id|date|amount|description)
The first 16 characters of the hex digest are used as the ID. This ensures:
- Same transaction always gets the same ID
- Idempotent ingestion (re-running won't create duplicates)
- Easy referencing in commands (8-char prefix is usually unique)
Privacy & Security
- No network access: This tool never connects to the internet or external services. Even LLM inference runs locally via Ollama.
- Local storage only: All data remains on your machine in plain CSV files and SQLite databases.
- No tracking: No telemetry, analytics, or usage reporting.
- You control the data: Backup, encrypt, or delete your data as you see fit.
- Privacy-preserving ML: Duplicate detection uses local LLM models (via
mojenticand Ollama) — your data never leaves your machine.
Local LLM Setup (Optional)
For duplicate detection features, install Ollama and a compatible model:
# Install Ollama (see https://ollama.ai)
brew install ollama # macOS
# Pull a recommended model
ollama pull qwen3:30b
# Verify it's working
ollama list
Note: Duplicate detection is optional. All core features work without LLM setup.
Best Practices
- Keep
ingest/directory private (do not commit to public repositories) - Keep
data/accounts/directory private - Use
.gitignoreto exclude sensitive directories if version-controlling your workflow - Consider encrypting your file system or using encrypted volumes for additional security
Development
Running Tests
# Activate virtual environment
source .venv/bin/activate
# Run all tests
pytest
# Run with verbose output
pytest -v
# Run with coverage
pytest --cov=gilt --cov-report=term-missing
Tests are discovered automatically from *_spec.py files under src/.
Test Statistics:
- 196 tests (all passing)
- Covers: Events, projections, CLI commands, services, duplicate detection, budgets
- Test pattern: BDD-style with
it_should_*naming
Linting
# Check code style
ruff check .
# Auto-fix issues
ruff check --fix .
Project Guidelines
For detailed development conventions, architecture decisions, and contribution guidelines, see the .github/copilot-instructions.md file and the event sourcing architecture documented in EVENT_SOURCING.md.
Architecture Highlights:
- Event Sourcing: Immutable audit log with materialized projections
- 11 Event Types: Transaction imports, categorizations, duplicates, budgets, prompts
- SQLite Storage: Event store + projection databases
- Local LLM: Privacy-preserving duplicate detection via Ollama
- Functional Core, Imperative Shell: Pure business logic isolated from I/O
Architecture Overview
Gilt uses an event-sourced architecture for transaction management:
CSV Exports → Events (Immutable) → Projections (Derived) → Views (Queries)
Key Components
Event Store (data/events.db):
- Append-only log of all transaction and categorization events
- 11 event types: TransactionImported, TransactionCategorized, DuplicateSuggested, BudgetCreated, etc.
- Complete audit trail with timestamps
Projections (data/projections.db, data/budget_projections.db):
- Materialized views rebuilt from events
- Transaction ledgers with current state
- Budget tracking with time-travel capability
- Can be rebuilt at any time from events
Learning System:
- Captures user feedback on duplicate detection
- Adapts prompts based on decision patterns
- Tracks accuracy metrics (precision, recall, F1)
- All processing happens locally (privacy-first)
Benefits
- Auditability: Every change is recorded with who/what/when
- Time-travel: Query historical state at any point
- Flexibility: Experiment with new categorization logic without data loss
- Learning: ML training from user decisions (local-only)
- Recovery: Rebuild projections if corrupted
Common Workflows
Initial Setup
- Configure accounts in
config/accounts.yml - Configure categories in
config/categories.yml - Export CSVs from your banks
- Place CSVs in
ingest/directory - Run
gilt ingest --write - Backfill events:
gilt backfill-events --write - Verify with
gilt accountsandgilt ytd --account <ACCOUNT_ID>
Regular Updates
- Download new transactions as CSV exports
- Place new CSVs in
ingest/directory (can overwrite old ones or use new filenames) - Run
gilt ingest --write - Review recent transactions with
gilt ytd --account <ACCOUNT_ID> --limit 50 - Categorize new transactions (see below)
Categorizing Transactions
Find What Needs Categorizing
# Show all uncategorized transactions
gilt uncategorized
# Filter by account
gilt uncategorized --account MYBANK_CHQ
# Show only large amounts
gilt uncategorized --min-amount 100
Categorize in Batch
# Recurring subscriptions
gilt categorize --desc-prefix "SPOTIFY" --category "Entertainment:Music" --yes --write
gilt categorize --desc-prefix "NETFLIX" --category "Entertainment:Streaming" --yes --write
# Utilities
gilt categorize --pattern "Payment.*EXAMPLE UTILITY" --category "Housing:Utilities" --yes --write
# Banking fees
gilt categorize --description "Monthly Fee" --category "Banking:Fees" --write
# Transit
gilt categorize --desc-prefix "PRESTO" --category "Transportation:Transit" --yes --write
Auto-Categorize with ML (After Manual Training)
# 1. First, manually categorize some transactions to build training data
gilt categorize --desc-prefix "SPOTIFY" --category "Entertainment:Music" --yes --write
gilt categorize --desc-prefix "NETFLIX" --category "Entertainment:Streaming" --yes --write
# ... continue for various patterns
# 2. Preview what the ML classifier would auto-categorize
gilt auto-categorize
# 3. Review predictions interactively
gilt auto-categorize --interactive
# (Approve, reject, or modify each prediction)
# 4. Apply approved auto-categorizations
gilt auto-categorize --write
# 5. Use stricter confidence threshold for higher precision
gilt auto-categorize --confidence 0.8 --write
Tip: Combine manual batch categorization for clear patterns (subscriptions, utilities) with auto-categorization for transactions that need contextual understanding.
Categorize Individual Transactions
# 1. View transactions
gilt ytd --account MYBANK_CHQ --limit 50
# 2. Note transaction ID (first 8 chars)
# 3. Categorize
gilt categorize --account MYBANK_CHQ --txid a1b2c3d4 --category "Housing:Rent" --write
Annotating Transactions
Single Transaction Notes
# View transactions
gilt ytd --account <ACCOUNT_ID>
# Add note using transaction ID
gilt note --account <ACCOUNT_ID> --txid <TXID> --note "Your note" --write
Batch Annotation for Recurring Expenses
# Add notes to all Spotify charges
gilt note --account MYBANK_CC --desc-prefix "SPOTIFY" --note "Music subscription" --yes --write
# Mark all gym membership fees
gilt note --account MYBANK_CHQ --description "GoodLife Fitness" --note "Gym membership" --yes --write
# Annotate transfer payments
gilt note --account MYBANK_CHQ --desc-prefix "TRANSFER FROM" --note "Inter-account transfer" --yes --write
Monthly Budget Review
-
Import new transactions
gilt ingest --write
-
Check categorization status
# How many need categorization? gilt uncategorized | head
-
Categorize uncategorized spending
# Focus on larger amounts first gilt uncategorized --min-amount 50 # Categorize patterns gilt categorize --desc-prefix "AMAZON" --category "Shopping:Online" --yes --write
-
View budget status
# Current month gilt budget --year 2025 --month 11 # Specific category detail gilt budget --category "Dining Out" --year 2025 --month 11
-
Generate monthly report (optional)
# Create markdown report gilt budget --year 2025 --month 11 > reports/budget_2025_11.md
-
Review over-budget categories
- Look for red indicators in budget output
- Check if over-budget is recurring pattern
- Adjust spending or increase budget as needed
-
Add notes to significant transactions
gilt note --account MYBANK_CHQ --txid <large-txid> --note "Reason for expense" --write
Setting Up Budget Tracking
-
Review existing categories
gilt categories -
Add custom categories if needed
gilt category --add "Custom Category" --description "Category description" --write gilt category --add "Custom:Subcategory" --description "Subcategory description" --write
-
Set budgets for each category
gilt category --set-budget "Dining Out" --amount 400 --period monthly --write gilt category --set-budget "Groceries" --amount 600 --period monthly --write gilt category --set-budget "Entertainment" --amount 200 --period monthly --write
-
Categorize existing transactions (see Categorizing Transactions above)
-
Monitor spending
# Current month gilt budget # Year-to-date gilt budget --year 2025
Duplicate Detection Workflow
Gilt provides two ways to handle duplicate transactions:
1. Automatic Scanning (find duplicates you don't know about)
Use gilt duplicates to scan all transactions for potential duplicates:
# ML-based detection (fast, learns from feedback)
gilt duplicates
# Interactive mode - review each match
gilt duplicates --interactive
# High-confidence only
gilt duplicates -i --min-confidence 0.8
# Force LLM analysis (slower, no training needed)
gilt duplicates --llm --interactive
What it detects:
- Same account + same amount + within 1 day
- Different descriptions (bank variations)
- ML model trained from your previous feedback
2. Manual Marking (when you spot a specific duplicate)
Use gilt mark-duplicate when you discover a duplicate in reports or listings:
# Preview marking a duplicate
gilt mark-duplicate --primary a1b2c3d4 --duplicate e5f6g7h8
# Confirm and persist (you'll choose which description to keep)
gilt mark-duplicate -p a1b2c3d4 -d e5f6g7h8 --write
When to use this:
- You found a duplicate while reviewing transactions
- Don't want to scan through 100 candidates
- Want to directly mark a specific pair
View transaction IDs:
gilt ytd --account MYBANK_CHQ --limit 50
View learning statistics:
gilt prompt-stats
Category Management
Add New Categories
# Category with budget
gilt category --add "Housing" --description "Housing expenses" --write
gilt category --set-budget "Housing" --amount 2500 --period monthly --write
# Add subcategories
gilt category --add "Housing:Utilities" --description "Electric, gas, water" --write
gilt category --add "Housing:Rent" --description "Monthly rent" --write
Rename Categories
# Update category definition in config/categories.yml
# Then update all existing transactions:
gilt recategorize --from "Old Category" --to "New Category" --write
gilt recategorize --from "Business:Subscriptions" --to "Work:Subscriptions" --write
Diagnose Category Issues
# Find orphaned categories (used but not defined)
gilt diagnose-categories
# Add missing categories or fix typos based on output
gilt category --add "MissingCategory" --write
# or
gilt recategorize --from "Typo" --to "Correct" --write
Remove Unused Categories
# Safe removal (prompts if used)
gilt category --remove "Old Category" --write
# Force removal (skip confirmation)
gilt category --remove "Unused Category" --force --write
Troubleshooting
Command not found: gilt
Make sure you've activated your virtual environment and installed the package:
source .venv/bin/activate
pip install -e .
Ingestion fails to match files
Check your config/accounts.yml and ensure source_patterns match your CSV filenames. You can use wildcards:
source_patterns:
- "*mybank*chequing*.csv"
- "*MYBANK*CHQ*.csv"
Transfer links not detected
The transfer linker uses specific parameters (date window, amount tolerance). If legitimate transfers aren't linked, you may need to review the parameters in gilt/cli/command/ingest.py and adjust epsilon_direct, epsilon_interac, or window_days.
Encoding issues with CSV files
The tool expects UTF-8 encoded CSVs. If your bank exports in a different encoding, convert them first:
iconv -f ISO-8859-1 -t UTF-8 input.csv > output.csv
License
This is a private, local-only tool. No license is provided for public distribution.
Support
This tool is designed for personal use. For questions or issues, consult the project guidelines and source code documentation.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gilt_cli-0.3.4.tar.gz.
File metadata
- Download URL: gilt_cli-0.3.4.tar.gz
- Upload date:
- Size: 335.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cec329201e4a5e5ee8831cacaf6033726e3e2bf1038f24f85a6cce8bc0969dce
|
|
| MD5 |
b8858345da7a90ca6da01b21c1e2b35c
|
|
| BLAKE2b-256 |
93a7b02432088cd2db3f477fd0b3de8bb907457d8e23fb983c5999fe15c9f589
|
File details
Details for the file gilt_cli-0.3.4-py3-none-any.whl.
File metadata
- Download URL: gilt_cli-0.3.4-py3-none-any.whl
- Upload date:
- Size: 231.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
24f18bc23b72d6d41ebdccd07202ce4c8f9a4f740e1794c182a7617e2503e202
|
|
| MD5 |
321bfefa33f4dd8c737db93159468f8b
|
|
| BLAKE2b-256 |
799713fd46a1a467211729c1ef3b363a7767921ea73330bbfae5e3c2fd43ae13
|