@fuzzle/opencode-accountant 0.4.6-next.1 → 0.4.6
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 +9 -5
- package/agent/accountant.md +87 -27
- package/dist/index.js +896 -502
- package/docs/tools/classify-statements.md +7 -84
- package/docs/tools/import-statements.md +5 -43
- package/package.json +4 -3
- package/docs/architecture/import-context.md +0 -674
- package/docs/tools/import-pipeline.md +0 -611
- package/docs/tools/reconcile-statement.md +0 -529
|
@@ -18,8 +18,6 @@ This tool is **restricted to the accountant agent only**.
|
|
|
18
18
|
- `{paths.pending}` = `import/pending`
|
|
19
19
|
- `{paths.unrecognized}` = `import/unrecognized`
|
|
20
20
|
|
|
21
|
-
**Note on Context IDs:** Each classified file receives a unique `contextId` (UUID). This ID references an import context file stored in `.memory/{uuid}.json` that tracks the file's metadata and progress through the import pipeline. The context is used by subsequent tools (`import-statements`, `reconcile-statement`) to locate files and retrieve metadata. See [Import Context Architecture](../architecture/import-context.md) for details.
|
|
22
|
-
|
|
23
21
|
### Success - All Files Classified
|
|
24
22
|
|
|
25
23
|
When all CSV files are successfully classified:
|
|
@@ -32,15 +30,13 @@ When all CSV files are successfully classified:
|
|
|
32
30
|
"filename": "transactions-ubs-2026-02.csv",
|
|
33
31
|
"provider": "ubs",
|
|
34
32
|
"currency": "chf",
|
|
35
|
-
"targetPath": "{paths.pending}/ubs/chf/transactions-ubs-2026-02.csv"
|
|
36
|
-
"contextId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
|
|
33
|
+
"targetPath": "{paths.pending}/ubs/chf/transactions-ubs-2026-02.csv"
|
|
37
34
|
},
|
|
38
35
|
{
|
|
39
36
|
"filename": "account-statement_2026-02.csv",
|
|
40
37
|
"provider": "revolut",
|
|
41
38
|
"currency": "eur",
|
|
42
|
-
"targetPath": "{paths.pending}/revolut/eur/account-statement_2026-02.csv"
|
|
43
|
-
"contextId": "8b3e9c21-1a4f-4d89-b123-9f8e7d6c5b4a"
|
|
39
|
+
"targetPath": "{paths.pending}/revolut/eur/account-statement_2026-02.csv"
|
|
44
40
|
}
|
|
45
41
|
],
|
|
46
42
|
"unrecognized": [],
|
|
@@ -65,8 +61,7 @@ When provider config includes `renamePattern` with metadata extraction:
|
|
|
65
61
|
"originalFilename": "export.csv",
|
|
66
62
|
"provider": "ubs",
|
|
67
63
|
"currency": "chf",
|
|
68
|
-
"targetPath": "{paths.pending}/ubs/chf/transactions-ubs-0235-90250546.csv"
|
|
69
|
-
"contextId": "a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6"
|
|
64
|
+
"targetPath": "{paths.pending}/ubs/chf/transactions-ubs-0235-90250546.csv"
|
|
70
65
|
}
|
|
71
66
|
],
|
|
72
67
|
"unrecognized": [],
|
|
@@ -92,8 +87,7 @@ When some files cannot be classified:
|
|
|
92
87
|
"filename": "transactions-ubs-2026-02.csv",
|
|
93
88
|
"provider": "ubs",
|
|
94
89
|
"currency": "chf",
|
|
95
|
-
"targetPath": "{paths.pending}/ubs/chf/transactions-ubs-2026-02.csv"
|
|
96
|
-
"contextId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
|
|
90
|
+
"targetPath": "{paths.pending}/ubs/chf/transactions-ubs-2026-02.csv"
|
|
97
91
|
}
|
|
98
92
|
],
|
|
99
93
|
"unrecognized": [
|
|
@@ -213,74 +207,13 @@ ubs:
|
|
|
213
207
|
|
|
214
208
|
This extracts metadata from the CSV (e.g., account number from row 0, column 1) and uses it in the output filename.
|
|
215
209
|
|
|
216
|
-
## Import Context
|
|
217
|
-
|
|
218
|
-
Each classified file gets an **import context** - a JSON file stored in `.memory/{contextId}.json` that tracks the file's metadata and progress through the import pipeline.
|
|
219
|
-
|
|
220
|
-
### What Gets Stored in the Context
|
|
221
|
-
|
|
222
|
-
When a CSV is classified, the context is created with:
|
|
223
|
-
|
|
224
|
-
- **File tracking**: filename, file path, original filename (if renamed)
|
|
225
|
-
- **Provider detection**: provider, currency, account number
|
|
226
|
-
- **CSV metadata**: transaction date range (from-date, until-date), opening/closing balances
|
|
227
|
-
- **Timestamps**: creation and last update times
|
|
228
|
-
|
|
229
|
-
### Example Context File
|
|
230
|
-
|
|
231
|
-
`.memory/f47ac10b-58cc-4372-a567-0e02b2c3d479.json`:
|
|
232
|
-
|
|
233
|
-
```json
|
|
234
|
-
{
|
|
235
|
-
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
236
|
-
"createdAt": "2026-02-28T10:30:00.000Z",
|
|
237
|
-
"updatedAt": "2026-02-28T10:30:00.000Z",
|
|
238
|
-
|
|
239
|
-
"filename": "ubs-0235-90250546.1-transactions-2026-02-22-to-2026-02-24.csv",
|
|
240
|
-
"filePath": "import/pending/ubs/chf/ubs-0235-90250546.1-transactions-2026-02-22-to-2026-02-24.csv",
|
|
241
|
-
"originalFilename": "transactions-0235-90250546.1-2026-02.csv",
|
|
242
|
-
|
|
243
|
-
"provider": "ubs",
|
|
244
|
-
"currency": "chf",
|
|
245
|
-
"accountNumber": "0235-90250546.1",
|
|
246
|
-
|
|
247
|
-
"fromDate": "2026-02-22",
|
|
248
|
-
"untilDate": "2026-02-24",
|
|
249
|
-
"openingBalance": "4001.55",
|
|
250
|
-
"closingBalance": "9001.55"
|
|
251
|
-
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
### Context Lifecycle
|
|
255
|
-
|
|
256
|
-
1. **Created by classify-statements**: Initial context with provider/currency/metadata
|
|
257
|
-
2. **Updated by import-statements**: Adds rules file, year journal, transaction count
|
|
258
|
-
3. **Updated by reconcile-statement**: Adds reconciliation results
|
|
259
|
-
4. **Persisted**: Remains in `.memory/` for audit/debugging
|
|
260
|
-
|
|
261
|
-
### Why Context IDs Matter
|
|
262
|
-
|
|
263
|
-
Context IDs solve the **multi-account problem**: when you have multiple accounts from the same provider/currency (e.g., UBS checking `.0` and savings `.1`), the context ID ensures each file is tracked independently and reconciled against the correct account.
|
|
264
|
-
|
|
265
|
-
**Without contexts**: Tools would select CSVs by timestamp → wrong account reconciled ❌
|
|
266
|
-
|
|
267
|
-
**With contexts**: Each CSV has an `accountNumber` in its context → correct account reconciled ✓
|
|
268
|
-
|
|
269
|
-
See [Import Context Architecture](../architecture/import-context.md) for complete technical details.
|
|
270
|
-
|
|
271
210
|
## Directory Structure
|
|
272
211
|
|
|
273
212
|
```
|
|
274
213
|
your-project/
|
|
275
|
-
├── .memory/ # Import context files (created by classify-statements)
|
|
276
|
-
│ ├── f47ac10b-58cc-4372-a567-0e02b2c3d479.json # Context for UBS checking
|
|
277
|
-
│ ├── 8b3e9c21-1a4f-4d89-b123-9f8e7d6c5b4a.json # Context for UBS savings
|
|
278
|
-
│ └── a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6.json # Context for Revolut EUR
|
|
279
|
-
│
|
|
280
214
|
├── config/
|
|
281
215
|
│ └── import/
|
|
282
216
|
│ └── providers.yaml # Defines all paths and detection rules
|
|
283
|
-
│
|
|
284
217
|
├── {paths.import}/ # Drop CSV files here (default: import/incoming)
|
|
285
218
|
│ ├── bank1.csv
|
|
286
219
|
│ └── bank2.csv
|
|
@@ -291,8 +224,7 @@ your-project/
|
|
|
291
224
|
│ │ └── classified.csv
|
|
292
225
|
│ ├── ubs/
|
|
293
226
|
│ │ └── chf/
|
|
294
|
-
│ │
|
|
295
|
-
│ │ └── ubs-0235-90250546.1-transactions-2026-02.csv # Savings
|
|
227
|
+
│ │ └── transactions-ubs-0235-90250546.csv
|
|
296
228
|
│ └── revolut/
|
|
297
229
|
│ └── eur/
|
|
298
230
|
│ └── account-statement_2026-02.csv
|
|
@@ -301,13 +233,6 @@ your-project/
|
|
|
301
233
|
└── mystery-bank.csv
|
|
302
234
|
```
|
|
303
235
|
|
|
304
|
-
**Note on `.memory/` directory:**
|
|
305
|
-
|
|
306
|
-
- Contains import context JSON files (one per classified CSV)
|
|
307
|
-
- Each context tracks metadata and progress through the pipeline
|
|
308
|
-
- Files persist for audit/debugging (not auto-deleted)
|
|
309
|
-
- Directory is gitignored (contexts are local/temporary)
|
|
310
|
-
|
|
311
236
|
## Typical Workflow
|
|
312
237
|
|
|
313
238
|
### Scenario 1: Successful Classification
|
|
@@ -315,10 +240,8 @@ your-project/
|
|
|
315
240
|
1. Drop CSV files into `{paths.import}/` (e.g., `import/incoming/`)
|
|
316
241
|
2. Run `classify-statements` tool (no arguments)
|
|
317
242
|
3. Check output - all files classified successfully
|
|
318
|
-
4.
|
|
319
|
-
5.
|
|
320
|
-
6. **Context IDs are passed automatically to subsequent steps** when using `import-pipeline`
|
|
321
|
-
7. Proceed to `import-statements` or `import-pipeline` tool
|
|
243
|
+
4. Files organized in `{paths.pending}/<provider>/<currency>/`
|
|
244
|
+
5. Proceed to `import-statements` tool
|
|
322
245
|
|
|
323
246
|
### Scenario 2: Handling Unrecognized Files
|
|
324
247
|
|
|
@@ -5,8 +5,6 @@ The `import-statements` tool imports classified CSV bank statements into hledger
|
|
|
5
5
|
- **Check mode** (`checkOnly: true`, default): Validates transactions and reports any that cannot be categorized
|
|
6
6
|
- **Import mode** (`checkOnly: false`): Imports validated transactions and moves processed files to the done directory
|
|
7
7
|
|
|
8
|
-
**Important**: This tool requires a `contextId` from a prior `classify-statements` run. The context provides the file path and metadata needed for import. See [Import Context Architecture](../architecture/import-context.md) for details.
|
|
9
|
-
|
|
10
8
|
## Year-Based Journal Routing
|
|
11
9
|
|
|
12
10
|
Transactions are automatically routed to year-specific journal files based on transaction dates:
|
|
@@ -21,47 +19,13 @@ Transactions are automatically routed to year-specific journal files based on tr
|
|
|
21
19
|
|
|
22
20
|
**Constraint:** Each CSV file must contain transactions from a single year. CSVs with transactions spanning multiple years are rejected during check mode with an error message listing the years found.
|
|
23
21
|
|
|
24
|
-
## Using Context IDs
|
|
25
|
-
|
|
26
|
-
The tool uses import contexts to locate CSV files and access metadata:
|
|
27
|
-
|
|
28
|
-
### How It Works
|
|
29
|
-
|
|
30
|
-
1. **classify-statements** creates a context for each CSV with a unique ID
|
|
31
|
-
2. **import-statements** receives the contextId (via import-pipeline or manual invocation)
|
|
32
|
-
3. Tool loads the context from `.memory/{contextId}.json`
|
|
33
|
-
4. Context provides the file path to the CSV (no need to search by provider/currency)
|
|
34
|
-
5. After import, tool updates context with results (rules file, year journal, transaction count)
|
|
35
|
-
|
|
36
|
-
### Manual Invocation Example
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
# Step 1: Classify statements
|
|
40
|
-
classify-statements
|
|
41
|
-
# Output: { "classified": [{ "contextId": "abc123-...", ... }] }
|
|
42
|
-
|
|
43
|
-
# Step 2: Import using contextId
|
|
44
|
-
import-statements --contextId "abc123-..." --checkOnly false
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
### Automatic Invocation via Pipeline
|
|
48
|
-
|
|
49
|
-
When using `import-pipeline`, context IDs are passed automatically:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
# Pipeline handles everything
|
|
53
|
-
import-pipeline
|
|
54
|
-
# Internally: classify → get contextIds → import each context → reconcile each context
|
|
55
|
-
```
|
|
56
|
-
|
|
57
22
|
## Arguments
|
|
58
23
|
|
|
59
|
-
| Argument | Type |
|
|
60
|
-
| ----------- | ------- |
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
|
|
64
|
-
**Note**: When called via `import-pipeline`, the `contextId` is passed automatically. For manual invocation, get the `contextId` from `classify-statements` output.
|
|
24
|
+
| Argument | Type | Default | Description |
|
|
25
|
+
| ----------- | ------- | ------- | ------------------------------------------- |
|
|
26
|
+
| `provider` | string | - | Filter by provider (e.g., `revolut`, `ubs`) |
|
|
27
|
+
| `currency` | string | - | Filter by currency (e.g., `chf`, `eur`) |
|
|
28
|
+
| `checkOnly` | boolean | `true` | If true, only validate without importing |
|
|
65
29
|
|
|
66
30
|
## Output Format
|
|
67
31
|
|
|
@@ -97,8 +61,6 @@ When all transactions have matching rules:
|
|
|
97
61
|
}
|
|
98
62
|
```
|
|
99
63
|
|
|
100
|
-
**Note**: When invoked via `import-pipeline`, the CSV file path comes from the import context (loaded via `contextId`). The context also provides metadata like account number and closing balance.
|
|
101
|
-
|
|
102
64
|
### Check Mode - Unknown Postings Found
|
|
103
65
|
|
|
104
66
|
When transactions don't match any `if` pattern in the rules file, the tool returns the full CSV row data for each unknown posting to provide context for classification:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fuzzle/opencode-accountant",
|
|
3
|
-
"version": "0.4.6
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "An OpenCode accounting agent, specialized in double-entry-bookkepping with hledger",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "ali bengali",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"docs"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@opencode-ai/plugin": "
|
|
31
|
+
"@opencode-ai/plugin": "latest",
|
|
32
|
+
"convert-csv-to-json": "^3.20.0",
|
|
32
33
|
"glob": "^10.3.10",
|
|
33
34
|
"js-yaml": "^4.1.0",
|
|
34
35
|
"minimatch": "^9.0.3",
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"@types/uuid": "^11.0.0",
|
|
44
45
|
"@typescript-eslint/eslint-plugin": "8.47.0",
|
|
45
46
|
"@typescript-eslint/parser": "8.47.0",
|
|
46
|
-
"bun-types": "
|
|
47
|
+
"bun-types": "latest",
|
|
47
48
|
"eslint": "^9.39.1",
|
|
48
49
|
"eslint-config-prettier": "10.1.8",
|
|
49
50
|
"eslint-plugin-prettier": "^5.1.3",
|