AgentSkillsCN

gilt

本地专用、隐私优先的个人理财 CLI 工具。所有变更默认采用干运行模式;若需持久化存储,请添加 --write 参数。适用于导入、分类、预算编制、重复记录处理以及报表生成等场景。

SKILL.md
--- frontmatter
name: gilt
description: Local-only, privacy-first personal finance CLI. All mutations dry-run by default; pass --write to persist. Use for import, categorization, budgeting, duplicates, and reporting.

Gilt CLI

Local-only CLI for managing personal finance ledgers. Run with uv run gilt <command>.

Safety Model

Every mutation is dry-run by default. The CLI previews what would change; nothing is written until you add --write.

Workflow: Always run without --write first, review the preview, then re-run with --write.

Write vs Read-Only Commands

Write commands (need --write)Read-only commands
ingestaccounts
categorizecategories
recategorizeytd
auto-categorizeuncategorized
categorybudget
notediagnose-categories
reportduplicates
mark-duplicateaudit-ml
backfill-eventsprompt-stats
migrate-to-events
rebuild-projections (always writes)
init (always writes)

Quick Command Reference

View

CommandPurpose
accountsList account IDs and descriptions
categoriesList categories with usage stats
ytdYear-to-date transactions for one account
uncategorizedTransactions missing categories
budgetBudget vs actual spending summary

Setup

CommandPurpose
initInitialize a new workspace with directories and starter config

Import

CommandPurpose
ingestNormalize raw bank CSVs into per-account ledgers

Categorize

CommandPurpose
categorizeAssign category to transactions (single or batch)
recategorizeRename a category across all ledgers
auto-categorizeML-based auto-categorization
categoryAdd/remove categories, set budgets
diagnose-categoriesFind categories in transactions not in config

Annotate

CommandPurpose
noteAttach notes to transactions

Report

CommandPurpose
budgetTerminal budget summary
reportGenerate .md and .docx budget reports

Duplicates

CommandPurpose
duplicatesScan for duplicates (ML or LLM)
mark-duplicateManually mark a transaction pair as duplicates

ML / Debug

CommandPurpose
audit-mlInspect ML training data and decisions
prompt-statsLLM prompt learning statistics

Maintenance

CommandPurpose
rebuild-projectionsRebuild projections from event store
backfill-eventsBackfill events from CSVs (advanced)
migrate-to-eventsOne-command migration to event sourcing

Account IDs

IDInstitutionProductNature
MYBANK_CHQMyBankChequingasset
BANK2_BIZSecondBankBusiness Chequingasset
BANK2_CHQSecondBankPersonal Chequingasset
BANK2_LOCSecondBankLine of Creditliability
MYBANK_CCMyBankCredit Cardliability

Category Syntax

Categories use colon notation: "TopLevel:Subcategory".

code
gilt categorize --txid abc12345 --category "Housing:Utilities" --write

Alternative: separate flags --category Housing --subcategory Utilities.

To add a new top-level category:

code
gilt category --add "NewCategory" --description "..." --write

To add a subcategory:

code
gilt category --add "Existing:NewSub" --write

Categories must exist in config/categories.yml before use. Use gilt categories to see all defined categories.

Transaction Matching

Commands like categorize and note support 4 matching modes:

ModeFlagBehavior
Single--txid / -tMatch one transaction by ID prefix (8+ chars)
Exact--description / -dMatch all with exact description
Prefix--desc-prefix / -pCase-insensitive prefix match
Regex--patternCase-insensitive regex on description

Combine with --amount / -m to narrow batch matches to a specific dollar amount.

Use only one matching mode per invocation. Do not combine --txid with --description, etc.

In batch mode, add --yes / -y to skip per-transaction confirmations.

Common Workflows

Set up a new workspace

bash
# Initialize workspace structure with starter config files
uv run gilt --data-dir ~/finances init

# Then edit the generated config files:
#   ~/finances/config/accounts.yml   — define your bank accounts
#   ~/finances/config/categories.yml — define spending categories

# Import your first data
uv run gilt --data-dir ~/finances ingest --write
uv run gilt --data-dir ~/finances migrate-to-events --write

The init command creates all required directories (config/, data/accounts/, ingest/, reports/) and writes starter accounts.yml and categories.yml with commented examples. It is idempotent — safe to run on an existing workspace (skips anything that already exists, never overwrites files).

Import new bank data

bash
# Drop CSV files into ingest/, then:
uv run gilt ingest                  # Preview
uv run gilt ingest --write          # Persist
uv run gilt rebuild-projections     # Update projections

Categorize transactions

bash
# Find uncategorized
uv run gilt uncategorized --account MYBANK_CHQ --year 2025

# Single transaction
uv run gilt categorize -a MYBANK_CHQ --txid abc12345 -c "Groceries" --write

# Batch by description prefix
uv run gilt categorize --desc-prefix "SPOTIFY" -c "Entertainment:Subscriptions" --yes --write

# ML auto-categorize
uv run gilt auto-categorize --confidence 0.8 --write

Budget review

bash
uv run gilt budget                              # Current year
uv run gilt budget --year 2025 --month 10       # Specific month
uv run gilt report --year 2025 --write          # Generate .docx

Handle duplicates

bash
uv run gilt duplicates                          # ML-based scan
uv run gilt duplicates --interactive            # Train ML with feedback
uv run gilt mark-duplicate -p abc12345 -d def67890 --write

Manage categories

bash
uv run gilt categories                          # View all
uv run gilt category --add "Travel:Flights" --write
uv run gilt category --set-budget "Dining Out" --amount 500 --write
uv run gilt recategorize --from "OldName" --to "NewName" --write
uv run gilt diagnose-categories                 # Find orphaned categories

Common Mistakes

MistakeFix
Forgetting --writeNothing persists without it. Re-run with --write.
--data-dir after command--data-dir is a top-level option: gilt --data-dir PATH budget, not gilt budget --data-dir PATH.
Category doesn't existAdd it first: gilt category --add "Cat:Sub" --write
Wrong amount signExpenses are negative, income is positive in ledgers. Match accordingly with --amount.
Combining match modesUse only one of --txid, --description, --desc-prefix, --pattern per call.
Workspace not initializedRun gilt --data-dir PATH init to create directories and starter config.
Missing projections DBRun gilt migrate-to-events --write or gilt rebuild-projections.
Missing event storeRun gilt migrate-to-events --write first.
Batch without --yesWithout --yes, each match prompts interactively (won't work in non-interactive shells).

Workspace and Data Paths

All paths are resolved from a single workspace root directory. The CLI determines the workspace root using this priority:

  1. --data-dir PATH (top-level CLI option, applies to all commands)
  2. GILT_DATA environment variable
  3. Current working directory (default)
bash
# Use current directory as workspace (default)
uv run gilt budget

# Explicit workspace root
uv run gilt --data-dir /path/to/my/finances budget

# Via environment variable
GILT_DATA=/path/to/my/finances uv run gilt budget

--data-dir is a top-level option, not a per-command option. It must appear before the command name.

Workspace Layout

All paths below are relative to the workspace root:

PathContentsWorkspace Property
config/accounts.ymlAccount definitionsaccounts_config
config/categories.ymlCategory tree and budgetscategories_config
data/accounts/Per-account ledger CSVsledger_data_dir
data/events.dbImmutable event storeevent_store_path
data/projections.dbMaterialized transaction viewprojections_path
data/budget_projections.dbMaterialized budget viewbudget_projections_path
ingest/Drop raw bank CSVs hereingest_dir
reports/Generated report outputreports_dir

Workspace in Code

Path resolution is centralized in gilt.workspace.Workspace. All command modules and services accept a workspace: Workspace parameter instead of individual path arguments.

python
from gilt.workspace import Workspace

# Resolve from env/CWD (used by CLI callback)
workspace = Workspace.resolve()

# Explicit root (used in tests)
workspace = Workspace(root=Path("/tmp/test"))

# Access paths as properties
workspace.event_store_path      # root / "data" / "events.db"
workspace.projections_path      # root / "data" / "projections.db"
workspace.ledger_data_dir       # root / "data" / "accounts"
workspace.categories_config     # root / "config" / "categories.yml"

The EventSourcingService also accepts workspace= to derive its paths:

python
es_service = EventSourcingService(workspace=workspace)

Testing with Workspace

Tests create a Workspace pointing at a temp directory. Use the init command's run() to scaffold the workspace, or create directories manually if you only need a subset:

python
from gilt.workspace import Workspace
from gilt.cli.command.init import run as init_workspace

def test_with_full_workspace():
    with TemporaryDirectory() as tmpdir:
        workspace = Workspace(root=Path(tmpdir))
        init_workspace(workspace=workspace)  # creates all dirs + starter config
        rc = run(workspace=workspace, ...)

def test_with_minimal_dirs():
    with TemporaryDirectory() as tmpdir:
        workspace = Workspace(root=Path(tmpdir))
        (Path(tmpdir) / "data" / "accounts").mkdir(parents=True)
        (Path(tmpdir) / "config").mkdir(parents=True)
        rc = run(workspace=workspace, ...)

Full Command Reference

For complete option listings and examples for all 20 commands, see references/command-reference.md.