@fuzzle/opencode-accountant 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,10 +93,10 @@ The `classify-statements` tool classifies bank statement CSV files by provider a
93
93
 
94
94
  ```yaml
95
95
  paths:
96
- import: statements/import
97
- pending: doc/agent/todo/import
98
- done: doc/agent/done/import
99
- unrecognized: statements/import/unrecognized
96
+ import: import/incoming
97
+ pending: import/pending
98
+ done: import/done
99
+ unrecognized: import/unrecognized
100
100
  rules: ledger/rules
101
101
 
102
102
  providers:
@@ -176,35 +176,31 @@ providers:
176
176
  your-project/
177
177
  ├── config/
178
178
  │ └── import/
179
- │ └── providers.yaml
179
+ │ └── providers.yaml # Configures all paths below
180
180
  ├── ledger/
181
- │ └── rules/ # hledger rules files
182
- │ └── <provider>-{account-number}.rules # {account-number} from metadata extraction
183
- ├── statements/
184
- └── import/ # Drop CSV files here
185
- └── unrecognized/ # Unclassified files moved here
186
- └── doc/
187
- └── agent/
188
- ├── todo/
189
- │ └── import/
190
- │ └── <provider>/ # e.g. revolut
191
- └── <currency>/ # e.g. chf, eur, usd, btc
192
- └── done/
193
- └── import/
194
- └── <provider>/
195
- └── <currency>/
181
+ │ └── rules/ # {paths.rules}
182
+ │ └── <provider>-{account-number}.rules
183
+ ├── import/
184
+ ├── incoming/ # {paths.import} - Drop CSV files here
185
+ ├── pending/ # {paths.pending} - Classified files
186
+ │ │ └── <provider>/ # e.g., revolut, ubs
187
+ │ │ └── <currency>/ # e.g., chf, eur, usd, btc
188
+ ├── done/ # {paths.done} - Processed files
189
+ └── <provider>/
190
+ └── <currency>/
191
+ └── unrecognized/ # {paths.unrecognized} - Unknown files
196
192
  ```
197
193
 
198
194
  #### Workflow
199
195
 
200
- 1. Drop CSV files into `statements/import/`
196
+ 1. Drop CSV files into `{paths.import}` (default: `import/incoming/`)
201
197
  2. Run `classify-statements` tool
202
- 3. Files are moved to `doc/agent/todo/import/<provider>/<currency>/`
203
- 4. Unrecognized files are moved to `statements/import/unrecognized/`
198
+ 3. Files are moved to `{paths.pending}/<provider>/<currency>/` (default: `import/pending/`)
199
+ 4. Unrecognized files are moved to `{paths.unrecognized}/` (default: `import/unrecognized/`)
204
200
  5. Run `import-statements` with `checkOnly: true` to validate transactions
205
201
  6. If unknown postings found: Add rules to the `.rules` file, repeat step 5
206
202
  7. Once all transactions match: Run `import-statements` with `checkOnly: false`
207
- 8. Transactions are imported to journal, CSV files moved to `doc/agent/done/import/`
203
+ 8. Transactions are imported to journal, CSV files moved to `{paths.done}/<provider>/<currency>/` (default: `import/done/`)
208
204
 
209
205
  ### Statement Import
210
206
 
@@ -223,11 +219,13 @@ The `import-statements` tool imports classified CSV statements into hledger usin
223
219
  The tool matches CSV files to their rules files by parsing the `source` directive in each `.rules` file. For example, if `ubs-account.rules` contains:
224
220
 
225
221
  ```
226
- source ../../doc/agent/todo/import/ubs/chf/transactions.csv
222
+ source ../../import/pending/ubs/chf/transactions.csv
227
223
  ```
228
224
 
229
225
  The tool will use that rules file when processing `transactions.csv`.
230
226
 
227
+ **Note:** The `source` path should match your configured `{paths.pending}` directory structure.
228
+
231
229
  See the hledger documentation for details on rules file format and syntax.
232
230
 
233
231
  #### Unknown Postings
@@ -8,6 +8,7 @@ tools:
8
8
  bash: true
9
9
  edit: true
10
10
  write: true
11
+ # MCP tools available: classify-statements, import-statements, update-prices
11
12
  permission:
12
13
  bash: allow
13
14
  edit: allow
@@ -49,15 +50,42 @@ When working with accounting tasks:
49
50
  1. **File organization** - Keep transactions in appropriate year journals
50
51
  1. **Duplicate checking** - Take extra care to avoid duplicate transactions
51
52
  1. **Unintended edits** - If a balance is off, check the journal for unintended edits
52
- 1. **Statement tracking** - Move processed statements to `statements/{provider}/YYYY`
53
53
  1. **Consistency** - Maintain consistent formatting and naming conventions across all files
54
54
 
55
+ ## Required Tools
56
+
57
+ You have access to specialized MCP tools that MUST be used for their designated tasks. Do NOT attempt to replicate their functionality with bash commands, direct hledger CLI calls, or manual file edits.
58
+
59
+ | Tool | Use For | NEVER Do Instead |
60
+ | --------------------- | ---------------------------------- | ------------------------------------------ |
61
+ | `classify-statements` | Organizing incoming CSV files | Manual file moves or bash `mv` commands |
62
+ | `import-statements` | Importing transactions to journals | `hledger import`, manual journal edits |
63
+ | `update-prices` | Fetching exchange rates | `curl` to price APIs, manual price entries |
64
+
65
+ These tools handle validation, deduplication, error checking, and file organization automatically. Bypassing them risks data corruption, duplicate transactions, and inconsistent state.
66
+
67
+ ## Bash Usage Policy
68
+
69
+ Bash is allowed ONLY for:
70
+
71
+ - Validation commands: `hledger check`, `hledger-fmt`, `hledger bal`
72
+ - Read-only queries: `hledger print`, `hledger reg`, `hledger accounts`
73
+ - File inspection: `cat`, `head`, `tail` (read-only)
74
+
75
+ Bash is FORBIDDEN for:
76
+
77
+ - `hledger import` - use `import-statements` tool instead
78
+ - Moving/copying CSV files - use `classify-statements` tool instead
79
+ - Editing journal files directly - use `edit` tool only for rules files
80
+ - Fetching prices - use `update-prices` tool instead
81
+
55
82
  ## Statement Import Workflow
56
83
 
57
- Use the `import-statements` tool to import bank statements. Do not edit the ledger manually! The workflow:
84
+ **IMPORTANT:** You MUST use the MCP tools below for statement imports. Do NOT edit journals manually, run `hledger import` directly, or move files with bash commands. The workflow:
58
85
 
59
- 1. **Prepare**: Drop CSV files into the incoming import folder configured in `config/import/providers.yaml`
60
- 2. **Classify**: Run `classify-statements` tool to move files to the configured import pending folder
86
+ 1. **Prepare**: Drop CSV files into `{paths.import}` (configured in `config/import/providers.yaml`, default: `import/incoming`)
87
+ 2. **Classify**: Run `classify-statements` tool to organize files by provider/currency
88
+ - Files moved to `{paths.pending}/<provider>/<currency>/`
61
89
  3. **Validate (check mode)**: Run `import-statements(checkOnly: true)` to validate transactions
62
90
  4. **Handle unknowns**: If unknown postings found:
63
91
  - Tool returns full CSV row data for each unknown posting
@@ -65,7 +93,7 @@ Use the `import-statements` tool to import bank statements. Do not edit the ledg
65
93
  - Create or update rules file with `if` directives to match the transaction
66
94
  - Repeat step 3 until all postings are matched
67
95
  5. **Import**: Once all transactions have matching rules, run `import-statements(checkOnly: false)`
68
- 6. **Complete**: Transactions imported to journal, CSVs moved to `doc/agent/done/import/`
96
+ 6. **Complete**: Transactions imported to journal, CSVs moved to `{paths.done}/<provider>/<currency>/`
69
97
 
70
98
  ### Rules Files
71
99
 
@@ -73,3 +101,75 @@ Use the `import-statements` tool to import bank statements. Do not edit the ledg
73
101
  - Match CSV to rules file via the `source` directive in each `.rules` file
74
102
  - Use field names from the `fields` directive for matching
75
103
  - Unknown account pattern: `income:unknown` (positive amounts) / `expenses:unknown` (negative amounts)
104
+
105
+ ## Tool Usage Reference
106
+
107
+ The following are MCP tools available to you. Always call these tools directly - do not attempt to replicate their behavior with shell commands.
108
+
109
+ ### classify-statements
110
+
111
+ **Purpose:** Organizes CSV files by auto-detecting provider and currency.
112
+
113
+ **Usage:** `classify-statements()` (no arguments)
114
+
115
+ **Behavior:**
116
+
117
+ - Scans `{paths.import}` for CSV files
118
+ - Detects provider using header matching + filename patterns
119
+ - Moves classified files to `{paths.pending}/<provider>/<currency>/`
120
+ - Moves unrecognized files to `{paths.unrecognized}/`
121
+ - Aborts if any file collision detected (no partial moves)
122
+
123
+ **Output:** Returns classified/unrecognized file lists with target paths
124
+
125
+ **Common issues:**
126
+
127
+ - Unrecognized files → Add provider config to `config/import/providers.yaml`
128
+ - Collisions → Move/rename existing pending files before re-running
129
+
130
+ ---
131
+
132
+ ### import-statements
133
+
134
+ **Purpose:** Imports classified CSV transactions into hledger journals.
135
+
136
+ **Usage:**
137
+
138
+ - Check mode (default): `import-statements(checkOnly: true)` or `import-statements()`
139
+ - Import mode: `import-statements(checkOnly: false)`
140
+
141
+ **Behavior:**
142
+
143
+ - Processes CSV files in `{paths.pending}/<provider>/<currency>/`
144
+ - Matches each CSV to rules file via `source` directive
145
+ - Check mode: Validates transactions, reports unknown postings with full CSV row data
146
+ - Import mode: Only proceeds if ALL transactions have known accounts, moves CSVs to `{paths.done}/`
147
+
148
+ **Output:** Returns per-file results with transaction counts and unknown postings (if any)
149
+
150
+ **Required for import:**
151
+
152
+ - All transactions must have matching rules (no `income:unknown` or `expenses:unknown`)
153
+ - Each CSV must have a corresponding `.rules` file in `{paths.rules}`
154
+
155
+ ---
156
+
157
+ ### update-prices
158
+
159
+ **Purpose:** Fetches currency exchange rates and updates `ledger/currencies/` journals.
160
+
161
+ **Usage:**
162
+
163
+ - Daily mode (default): `update-prices()` or `update-prices(backfill: false)`
164
+ - Backfill mode: `update-prices(backfill: true)`
165
+
166
+ **Behavior:**
167
+
168
+ - Daily mode: Fetches yesterday's prices only
169
+ - Backfill mode: Fetches from `backfill_date` (or Jan 1 of current year) to yesterday
170
+ - Updates journal files in-place with deduplication (newer prices overwrite older for same date)
171
+ - Processes all currencies independently (partial failures possible)
172
+
173
+ **Output:** Returns per-currency results with latest price line or error message
174
+
175
+ **Configuration:** `config/prices.yaml` defines currencies, sources, pairs, and backfill dates
@@ -0,0 +1,404 @@
1
+ # classify-statements Tool
2
+
3
+ The `classify-statements` tool organizes bank statement CSV files by automatically detecting their provider and currency, then moves them to the appropriate directories for import processing.
4
+
5
+ This tool is **restricted to the accountant agent only**.
6
+
7
+ ## Arguments
8
+
9
+ | Argument | Type | Default | Description |
10
+ | -------- | ---- | ------- | ---------------------------- |
11
+ | (none) | - | - | This tool takes no arguments |
12
+
13
+ ## Output Format
14
+
15
+ **Note on paths:** All file paths use `{paths.*}` variables configured in `config/import/providers.yaml`. Default values:
16
+
17
+ - `{paths.import}` = `import/incoming`
18
+ - `{paths.pending}` = `import/pending`
19
+ - `{paths.unrecognized}` = `import/unrecognized`
20
+
21
+ ### Success - All Files Classified
22
+
23
+ When all CSV files are successfully classified:
24
+
25
+ ```json
26
+ {
27
+ "success": true,
28
+ "classified": [
29
+ {
30
+ "filename": "transactions-ubs-2026-02.csv",
31
+ "provider": "ubs",
32
+ "currency": "chf",
33
+ "targetPath": "{paths.pending}/ubs/chf/transactions-ubs-2026-02.csv"
34
+ },
35
+ {
36
+ "filename": "account-statement_2026-02.csv",
37
+ "provider": "revolut",
38
+ "currency": "eur",
39
+ "targetPath": "{paths.pending}/revolut/eur/account-statement_2026-02.csv"
40
+ }
41
+ ],
42
+ "unrecognized": [],
43
+ "summary": {
44
+ "total": 2,
45
+ "classified": 2,
46
+ "unrecognized": 0
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### Success - With Filename Renaming
52
+
53
+ When provider config includes `renamePattern` with metadata extraction:
54
+
55
+ ```json
56
+ {
57
+ "success": true,
58
+ "classified": [
59
+ {
60
+ "filename": "transactions-ubs-0235-90250546.csv",
61
+ "originalFilename": "export.csv",
62
+ "provider": "ubs",
63
+ "currency": "chf",
64
+ "targetPath": "{paths.pending}/ubs/chf/transactions-ubs-0235-90250546.csv"
65
+ }
66
+ ],
67
+ "unrecognized": [],
68
+ "summary": {
69
+ "total": 1,
70
+ "classified": 1,
71
+ "unrecognized": 0
72
+ }
73
+ }
74
+ ```
75
+
76
+ The `originalFilename` field appears when the file was renamed using metadata extraction.
77
+
78
+ ### Success - Some Files Unrecognized
79
+
80
+ When some files cannot be classified:
81
+
82
+ ```json
83
+ {
84
+ "success": true,
85
+ "classified": [
86
+ {
87
+ "filename": "transactions-ubs-2026-02.csv",
88
+ "provider": "ubs",
89
+ "currency": "chf",
90
+ "targetPath": "{paths.pending}/ubs/chf/transactions-ubs-2026-02.csv"
91
+ }
92
+ ],
93
+ "unrecognized": [
94
+ {
95
+ "filename": "mystery-bank.csv",
96
+ "targetPath": "{paths.unrecognized}/mystery-bank.csv"
97
+ }
98
+ ],
99
+ "summary": {
100
+ "total": 2,
101
+ "classified": 1,
102
+ "unrecognized": 1
103
+ }
104
+ }
105
+ ```
106
+
107
+ Unrecognized files are moved to `{paths.unrecognized}` for manual review.
108
+
109
+ ### Failure - File Collisions
110
+
111
+ When target files already exist (prevents overwriting):
112
+
113
+ ```json
114
+ {
115
+ "success": false,
116
+ "error": "Cannot classify: 1 file(s) would overwrite existing pending files.",
117
+ "collisions": [
118
+ {
119
+ "filename": "transactions.csv",
120
+ "existingPath": "{paths.pending}/ubs/chf/transactions.csv"
121
+ }
122
+ ],
123
+ "classified": [],
124
+ "unrecognized": []
125
+ }
126
+ ```
127
+
128
+ **Important:** The tool uses a two-pass approach (detect → check collisions → move) to prevent partial classification. If ANY collision is detected, NO files are moved.
129
+
130
+ ### Configuration Error
131
+
132
+ When `config/import/providers.yaml` is missing or invalid:
133
+
134
+ ```json
135
+ {
136
+ "success": false,
137
+ "error": "Failed to load configuration: config/import/providers.yaml not found",
138
+ "classified": [],
139
+ "unrecognized": []
140
+ }
141
+ ```
142
+
143
+ ### Agent Restriction Error
144
+
145
+ When called by the wrong agent:
146
+
147
+ ```json
148
+ {
149
+ "success": false,
150
+ "error": "This tool is restricted to the accountant agent only.",
151
+ "hint": "Use: Task(subagent_type='accountant', prompt='classify statements')",
152
+ "caller": "main assistant",
153
+ "classified": [],
154
+ "unrecognized": []
155
+ }
156
+ ```
157
+
158
+ ## Provider Detection
159
+
160
+ The tool detects providers using rules defined in `config/import/providers.yaml`:
161
+
162
+ ### Detection Methods
163
+
164
+ 1. **Filename Pattern** (optional): Regex match against filename
165
+ 2. **CSV Header** (required): Exact match of CSV header row
166
+ 3. **Currency Field** (required): Which column contains the currency
167
+
168
+ ### Detection Example
169
+
170
+ ```yaml
171
+ providers:
172
+ revolut:
173
+ detect:
174
+ - filenamePattern: '^account-statement_'
175
+ header: 'Type,Product,Started Date,Completed Date,Description,Amount,Fee,Currency,State,Balance'
176
+ currencyField: Currency
177
+ currencies:
178
+ CHF: chf
179
+ EUR: eur
180
+ ```
181
+
182
+ Detection process:
183
+
184
+ 1. Check if filename matches `filenamePattern` (if specified)
185
+ 2. Read CSV and check if header matches exactly
186
+ 3. Determine currency from `currencyField` column
187
+ 4. Map raw currency value (e.g., "EUR") to normalized folder name (e.g., "eur")
188
+
189
+ ### Filename Renaming
190
+
191
+ Providers can specify `renamePattern` with metadata extraction:
192
+
193
+ ```yaml
194
+ ubs:
195
+ detect:
196
+ - header: 'Trade date,Trade time,...'
197
+ currencyField: Currency
198
+ skipRows: 9
199
+ delimiter: ';'
200
+ renamePattern: 'transactions-ubs-{account-number}.csv'
201
+ metadata:
202
+ - field: account-number
203
+ row: 0
204
+ column: 1
205
+ normalize: spaces-to-dashes
206
+ ```
207
+
208
+ This extracts metadata from the CSV (e.g., account number from row 0, column 1) and uses it in the output filename.
209
+
210
+ ## Directory Structure
211
+
212
+ ```
213
+ your-project/
214
+ ├── config/
215
+ │ └── import/
216
+ │ └── providers.yaml # Defines all paths and detection rules
217
+ ├── {paths.import}/ # Drop CSV files here (default: import/incoming)
218
+ │ ├── bank1.csv
219
+ │ └── bank2.csv
220
+
221
+ ├── {paths.pending}/ # Classified files (default: import/pending)
222
+ │ ├── <provider>/ # e.g., revolut, ubs
223
+ │ │ └── <currency>/ # e.g., chf, eur, usd, btc
224
+ │ │ └── classified.csv
225
+ │ ├── ubs/
226
+ │ │ └── chf/
227
+ │ │ └── transactions-ubs-0235-90250546.csv
228
+ │ └── revolut/
229
+ │ └── eur/
230
+ │ └── account-statement_2026-02.csv
231
+
232
+ └── {paths.unrecognized}/ # Unclassified files (default: import/unrecognized)
233
+ └── mystery-bank.csv
234
+ ```
235
+
236
+ ## Typical Workflow
237
+
238
+ ### Scenario 1: Successful Classification
239
+
240
+ 1. Drop CSV files into `{paths.import}/` (e.g., `import/incoming/`)
241
+ 2. Run `classify-statements` tool (no arguments)
242
+ 3. Check output - all files classified successfully
243
+ 4. Files organized in `{paths.pending}/<provider>/<currency>/`
244
+ 5. Proceed to `import-statements` tool
245
+
246
+ ### Scenario 2: Handling Unrecognized Files
247
+
248
+ 1. Run `classify-statements` tool
249
+ 2. Review `unrecognized` array in output
250
+ 3. Check files in `{paths.unrecognized}/` directory
251
+ 4. Options to resolve:
252
+ - **Add provider config**: Update `config/import/providers.yaml` with detection rules
253
+ - **Manual classification**: Move file to correct `{paths.pending}/<provider>/<currency>/` directory
254
+ - **Investigate format**: Check if CSV format matches expected patterns
255
+ 5. Re-run tool after adding configuration
256
+
257
+ ### Scenario 3: Resolving Collisions
258
+
259
+ 1. Run `classify-statements` tool
260
+ 2. Tool reports collision - file would overwrite existing file
261
+ 3. Check `collisions` array for affected files
262
+ 4. Options to resolve:
263
+ - **Archive existing**: Move existing pending file to `{paths.done}/` if already processed
264
+ - **Rename**: Rename one of the conflicting files
265
+ - **Remove**: Delete duplicate file if confirmed redundant
266
+ 5. Re-run tool after resolving collision
267
+
268
+ **Important:** No files are moved until ALL collisions are resolved. This prevents partial/inconsistent state.
269
+
270
+ ## Handling Unrecognized Files
271
+
272
+ ### What "Unrecognized" Means
273
+
274
+ A file is unrecognized when:
275
+
276
+ - Filename doesn't match any `filenamePattern` (if patterns are specified)
277
+ - CSV header doesn't match any configured provider's `header`
278
+ - CSV is malformed or has unexpected structure
279
+ - Currency value doesn't map to configured currencies
280
+
281
+ ### Common Causes
282
+
283
+ | Cause | Solution |
284
+ | ------------------------- | ------------------------------------------------------------------------------- |
285
+ | New bank/provider | Add provider config to `config/import/providers.yaml` |
286
+ | Non-standard CSV format | Check CSV structure; add detection rules with correct header/skipRows/delimiter |
287
+ | Filename pattern mismatch | Update `filenamePattern` or remove it (header-only detection) |
288
+ | Unknown currency | Add currency mapping to provider's `currencies` section |
289
+ | Metadata in header rows | Use `skipRows` to skip non-CSV rows before header |
290
+ | Wrong delimiter | Specify `delimiter` (e.g., `";"` for semicolon-delimited) |
291
+
292
+ ### Adding Provider Detection
293
+
294
+ Example: Adding a new bank called "SwissBank":
295
+
296
+ ```yaml
297
+ providers:
298
+ swissbank:
299
+ detect:
300
+ - filenamePattern: '^swissbank-'
301
+ header: 'Date,Description,Amount,Balance,Currency'
302
+ currencyField: Currency
303
+ currencies:
304
+ CHF: chf
305
+ EUR: eur
306
+ ```
307
+
308
+ After updating config, re-run `classify-statements` to classify previously unrecognized files.
309
+
310
+ ## Collision Safety
311
+
312
+ The tool uses a **two-pass approach** to ensure atomic operations:
313
+
314
+ ### Two-Pass Process
315
+
316
+ **Pass 1: Detection & Planning**
317
+
318
+ - Scan all CSV files in `{paths.import}/`
319
+ - Detect provider/currency for each file
320
+ - Determine target path for each file
321
+ - Build complete move plan
322
+
323
+ **Pass 2: Collision Check**
324
+
325
+ - Check if ANY target file already exists
326
+ - If collisions found: abort with error (no files moved)
327
+ - If no collisions: proceed to Pass 3
328
+
329
+ **Pass 3: Move Files**
330
+
331
+ - Execute all planned moves atomically
332
+ - All files moved successfully or none at all
333
+
334
+ ### Why This Matters
335
+
336
+ Without collision checking, partial classification could occur:
337
+
338
+ - Some files moved, others fail mid-process
339
+ - Inconsistent state requiring manual cleanup
340
+ - Risk of lost data or confusion about what was processed
341
+
342
+ With two-pass approach:
343
+
344
+ - All-or-nothing operation
345
+ - Easy to retry after fixing collisions
346
+ - No partial/inconsistent states
347
+
348
+ ### Resolving Collisions
349
+
350
+ Check the `collisions` array in the error output:
351
+
352
+ ```json
353
+ "collisions": [
354
+ {
355
+ "filename": "transactions.csv",
356
+ "existingPath": "{paths.pending}/ubs/chf/transactions.csv"
357
+ }
358
+ ]
359
+ ```
360
+
361
+ Then:
362
+
363
+ 1. Inspect existing file: `cat {paths.pending}/ubs/chf/transactions.csv`
364
+ 2. Determine if it's already processed (check if transactions were imported)
365
+ 3. If processed: Move to `{paths.done}/` or delete
366
+ 4. If not processed: Rename one of the files or merge manually
367
+ 5. Re-run `classify-statements`
368
+
369
+ ## Error Handling
370
+
371
+ ### Common Errors
372
+
373
+ | Error | Cause | Solution |
374
+ | ------------------- | ------------------------------------------------- | --------------------------------------------------------------------- |
375
+ | File collision | Target file already exists in pending directory | Move existing file to done, rename, or delete; then re-run |
376
+ | Configuration error | Missing or invalid `config/import/providers.yaml` | Ensure config file exists with proper YAML syntax and required fields |
377
+ | Agent restriction | Called by wrong agent | Use `Task(subagent_type='accountant', prompt='classify statements')` |
378
+ | Permission error | Cannot read/write directories | Check file permissions on import/pending/unrecognized directories |
379
+ | No CSV files found | Import directory is empty | Add CSV files to `{paths.import}` directory first |
380
+ | CSV parsing error | Malformed CSV file | Check CSV structure; ensure proper delimiter and header row |
381
+
382
+ ### Configuration File Required Fields
383
+
384
+ Ensure `config/import/providers.yaml` contains:
385
+
386
+ ```yaml
387
+ paths:
388
+ import: <path> # Required
389
+ pending: <path> # Required
390
+ done: <path> # Required (used by import-statements tool)
391
+ unrecognized: <path> # Required
392
+ rules: <path> # Required (used by import-statements tool)
393
+
394
+ providers:
395
+ <provider-name>:
396
+ detect:
397
+ - header: <exact-csv-header> # Required
398
+ currencyField: <column-name> # Required
399
+ # Optional: filenamePattern, skipRows, delimiter, renamePattern, metadata
400
+ currencies:
401
+ <RAW-VALUE>: <normalized-folder> # Required (at least one)
402
+ ```
403
+
404
+ Missing any required field will cause a configuration error.