@fuzzle/opencode-accountant 0.0.9-next.1 → 0.0.10

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
@@ -97,6 +97,7 @@ paths:
97
97
  pending: doc/agent/todo/import
98
98
  done: doc/agent/done/import
99
99
  unrecognized: statements/import/unrecognized
100
+ rules: ledger/rules
100
101
 
101
102
  providers:
102
103
  revolut:
@@ -121,9 +122,9 @@ providers:
121
122
  currencyField: Currency
122
123
  skipRows: 9
123
124
  delimiter: ';'
124
- renamePattern: 'transactions-ubs-{accountnumber}.csv'
125
+ renamePattern: 'transactions-ubs-{account-number}.csv'
125
126
  metadata:
126
- - field: accountnumber
127
+ - field: account-number
127
128
  row: 0
128
129
  column: 1
129
130
  normalize: spaces-to-dashes
@@ -143,6 +144,7 @@ providers:
143
144
  | `pending` | Base path for classified files awaiting import |
144
145
  | `done` | Base path for archived files after import |
145
146
  | `unrecognized` | Directory for files that couldn't be classified |
147
+ | `rules` | Directory containing hledger `.rules` files |
146
148
 
147
149
  **Provider Detection Rules:**
148
150
 
@@ -175,15 +177,18 @@ your-project/
175
177
  ├── config/
176
178
  │ └── import/
177
179
  │ └── providers.yaml
180
+ ├── ledger/
181
+ │ └── rules/ # hledger rules files
182
+ │ └── <provider>-{account-number}.rules # {account-number} from metadata extraction
178
183
  ├── statements/
179
- │ └── import/ # Drop CSV files here
180
- │ └── unrecognized/ # Unclassified files moved here
184
+ │ └── import/ # Drop CSV files here
185
+ │ └── unrecognized/ # Unclassified files moved here
181
186
  └── doc/
182
187
  └── agent/
183
188
  ├── todo/
184
189
  │ └── import/
185
- │ └── <provider>/ # e.g. revolut
186
- │ └── <currency>/ # e.g. chf, eur, usd, btc
190
+ │ └── <provider>/ # e.g. revolut
191
+ │ └── <currency>/ # e.g. chf, eur, usd, btc
187
192
  └── done/
188
193
  └── import/
189
194
  └── <provider>/
@@ -196,7 +201,40 @@ your-project/
196
201
  2. Run `classify-statements` tool
197
202
  3. Files are moved to `doc/agent/todo/import/<provider>/<currency>/`
198
203
  4. Unrecognized files are moved to `statements/import/unrecognized/`
199
- 5. After successful import, files should be moved to `doc/agent/done/import/`
204
+ 5. Run `import-statements` with `checkOnly: true` to validate transactions
205
+ 6. If unknown postings found: Add rules to the `.rules` file, repeat step 5
206
+ 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/`
208
+
209
+ ### Statement Import
210
+
211
+ The `import-statements` tool imports classified CSV statements into hledger using rules files. It validates transactions before import and identifies any that cannot be categorized.
212
+
213
+ #### Tool Arguments
214
+
215
+ | Argument | Type | Default | Description |
216
+ | ----------- | ------- | ------- | ------------------------------------------- |
217
+ | `provider` | string | - | Filter by provider (e.g., `revolut`, `ubs`) |
218
+ | `currency` | string | - | Filter by currency (e.g., `chf`, `eur`) |
219
+ | `checkOnly` | boolean | `true` | If true, only validate without importing |
220
+
221
+ #### Rules File Matching
222
+
223
+ 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
+
225
+ ```
226
+ source ../../doc/agent/todo/import/ubs/chf/transactions.csv
227
+ ```
228
+
229
+ The tool will use that rules file when processing `transactions.csv`.
230
+
231
+ See the hledger documentation for details on rules file format and syntax.
232
+
233
+ #### Unknown Postings
234
+
235
+ When a transaction doesn't match any `if` pattern in the rules file, hledger assigns it to `income:unknown` or `expenses:unknown` depending on the transaction direction. The tool reports these so you can add appropriate rules.
236
+
237
+ For detailed output format examples, see [`docs/tools/import-statements.md`](docs/tools/import-statements.md).
200
238
 
201
239
  ## Development
202
240
 
@@ -3,13 +3,13 @@ description: Specialized agent for hledger accounting tasks and transaction mana
3
3
  prompt: You are an accounting specialist with expertise in hledger and double-entry bookkeeping. Your role is to help maintain accurate financial records following the user's conventions.
4
4
  mode: subagent
5
5
  temperature: 0.1
6
- steps: 5
6
+ steps: 8
7
7
  tools:
8
- bash: false
8
+ bash: true
9
9
  edit: true
10
10
  write: true
11
11
  permission:
12
- bash: deny
12
+ bash: allow
13
13
  edit: allow
14
14
  glob: allow
15
15
  grep: allow
@@ -24,25 +24,19 @@ permission:
24
24
 
25
25
  ## Repository Structure
26
26
 
27
- - `.hledger.journal` - Global hledger journal file,
27
+ - `.hledger.journal` - Global hledger journal file
28
28
  - `ledger/` - All ledger related files are stored here
29
29
  - `ledger/currencies/` - Currency exchange rate files
30
30
  - `ledger/YYYY.journal` - Annual hledger journal files
31
+ - `ledger/rules` - hledger rules files
31
32
  - `statements/` - Bank and broker account statements
32
33
  - `statements/import` - Upload folder for new statements to process
33
- - `statements/provider/YYYY` - Location where processed statements are stored
34
- - `doc/agent/todo/` - Agents task work directory
34
+ - `statements/{provider}/YYYY` - Processed statements organized by source and year
35
+ - `doc/agent/todo/` - Agent's task work directory
35
36
  - `doc/agent/done/` - Tasks completed by the agent
36
37
  - `config/conventions/` - Accounting conventions
37
38
  - `config/rules/` - import rules files
38
39
 
39
- ## System Environment
40
-
41
- **Required for accounting tasks:**
42
-
43
- - `pricehist` - Price data fetching
44
- - `hledger`, `hledger-fmt` - Accounting operations
45
-
46
40
  ## Conventions & Workflow
47
41
 
48
42
  All account conventions, conversion patterns (currency, crypto, equity postings), transaction status management, import
@@ -59,15 +53,5 @@ When working with accounting tasks:
59
53
  1. **File organization** - Keep transactions in appropriate year journals
60
54
  1. **Duplicate checking** - Take extra care to avoid duplicate transactions
61
55
  1. **Unintended edits** - If a balance is off, check the journal for unintended edits
62
- 1. **Statement tracking** - Move processed statements to `statements/provider/YYYY`
63
-
64
- ## Common Tasks
65
-
66
- - Adding new transactions from statements
67
- - Processing crypto purchases and transfers
68
- - Currency conversions between CHF/EUR/USD
69
- - Validating journal integrity
70
- - Generating balance reports
71
- - Correcting malformed transactions
72
-
73
- Focus on accuracy, precision, and strict adherence to the repository's accounting conventions.
56
+ 1. **Statement tracking** - Move processed statements to `statements/{provider}/YYYY`
57
+ 1. **Consistency** - Maintain consistent formatting and naming conventions across all files
package/dist/index.js CHANGED
@@ -1341,7 +1341,7 @@ var require_papaparse = __commonJS((exports, module) => {
1341
1341
  });
1342
1342
 
1343
1343
  // src/index.ts
1344
- import { dirname as dirname2, join as join5 } from "path";
1344
+ import { dirname as dirname4, join as join7 } from "path";
1345
1345
  import { fileURLToPath } from "url";
1346
1346
 
1347
1347
  // src/utils/agentLoader.ts
@@ -16584,7 +16584,13 @@ import * as fs4 from "fs";
16584
16584
  import * as fs3 from "fs";
16585
16585
  import * as path3 from "path";
16586
16586
  var CONFIG_FILE2 = "config/import/providers.yaml";
16587
- var REQUIRED_PATH_FIELDS = ["import", "pending", "done", "unrecognized"];
16587
+ var REQUIRED_PATH_FIELDS = [
16588
+ "import",
16589
+ "pending",
16590
+ "done",
16591
+ "unrecognized",
16592
+ "rules"
16593
+ ];
16588
16594
  var REQUIRED_DETECTION_FIELDS = ["header", "currencyField"];
16589
16595
  function validatePaths(paths) {
16590
16596
  if (typeof paths !== "object" || paths === null) {
@@ -16600,7 +16606,8 @@ function validatePaths(paths) {
16600
16606
  import: pathsObj.import,
16601
16607
  pending: pathsObj.pending,
16602
16608
  done: pathsObj.done,
16603
- unrecognized: pathsObj.unrecognized
16609
+ unrecognized: pathsObj.unrecognized,
16610
+ rules: pathsObj.rules
16604
16611
  };
16605
16612
  }
16606
16613
  function validateDetectionRule(providerName, index, rule) {
@@ -16968,15 +16975,369 @@ var classify_statements_default = tool({
16968
16975
  return classifyStatementsCore(directory, agent);
16969
16976
  }
16970
16977
  });
16978
+ // src/tools/import-statements.ts
16979
+ import * as fs6 from "fs";
16980
+ import * as path6 from "path";
16981
+
16982
+ // src/utils/rulesMatcher.ts
16983
+ import * as fs5 from "fs";
16984
+ import * as path5 from "path";
16985
+ function parseSourceDirective(content) {
16986
+ const match = content.match(/^source\s+([^\n#]+)/m);
16987
+ if (!match) {
16988
+ return null;
16989
+ }
16990
+ return match[1].trim();
16991
+ }
16992
+ function resolveSourcePath(sourcePath, rulesFilePath) {
16993
+ if (path5.isAbsolute(sourcePath)) {
16994
+ return sourcePath;
16995
+ }
16996
+ const rulesDir = path5.dirname(rulesFilePath);
16997
+ return path5.resolve(rulesDir, sourcePath);
16998
+ }
16999
+ function loadRulesMapping(rulesDir) {
17000
+ const mapping = {};
17001
+ if (!fs5.existsSync(rulesDir)) {
17002
+ return mapping;
17003
+ }
17004
+ const files = fs5.readdirSync(rulesDir);
17005
+ for (const file2 of files) {
17006
+ if (!file2.endsWith(".rules")) {
17007
+ continue;
17008
+ }
17009
+ const rulesFilePath = path5.join(rulesDir, file2);
17010
+ const stat = fs5.statSync(rulesFilePath);
17011
+ if (!stat.isFile()) {
17012
+ continue;
17013
+ }
17014
+ const content = fs5.readFileSync(rulesFilePath, "utf-8");
17015
+ const sourcePath = parseSourceDirective(content);
17016
+ if (!sourcePath) {
17017
+ continue;
17018
+ }
17019
+ const absoluteCsvPath = resolveSourcePath(sourcePath, rulesFilePath);
17020
+ mapping[absoluteCsvPath] = rulesFilePath;
17021
+ }
17022
+ return mapping;
17023
+ }
17024
+ function findRulesForCsv(csvPath, mapping) {
17025
+ if (mapping[csvPath]) {
17026
+ return mapping[csvPath];
17027
+ }
17028
+ const normalizedCsvPath = path5.normalize(csvPath);
17029
+ for (const [mappedCsv, rulesFile] of Object.entries(mapping)) {
17030
+ if (path5.normalize(mappedCsv) === normalizedCsvPath) {
17031
+ return rulesFile;
17032
+ }
17033
+ }
17034
+ return null;
17035
+ }
17036
+
17037
+ // src/utils/hledgerExecutor.ts
17038
+ var {$: $2 } = globalThis.Bun;
17039
+ async function defaultHledgerExecutor(cmdArgs) {
17040
+ try {
17041
+ const result = await $2`hledger ${cmdArgs}`.quiet().nothrow();
17042
+ return {
17043
+ stdout: result.stdout.toString(),
17044
+ stderr: result.stderr.toString(),
17045
+ exitCode: result.exitCode
17046
+ };
17047
+ } catch (error45) {
17048
+ return {
17049
+ stdout: "",
17050
+ stderr: error45 instanceof Error ? error45.message : String(error45),
17051
+ exitCode: 1
17052
+ };
17053
+ }
17054
+ }
17055
+ function parseUnknownPostings(hledgerOutput) {
17056
+ const unknownPostings = [];
17057
+ const lines = hledgerOutput.split(`
17058
+ `);
17059
+ let currentDate = "";
17060
+ let currentDescription = "";
17061
+ for (const line of lines) {
17062
+ const headerMatch = line.match(/^(\d{4}-\d{2}-\d{2})\s+(.+)$/);
17063
+ if (headerMatch) {
17064
+ currentDate = headerMatch[1];
17065
+ currentDescription = headerMatch[2].trim();
17066
+ continue;
17067
+ }
17068
+ const postingMatch = line.match(/^\s+(income:unknown|expenses:unknown)\s+([^\s]+(?:\s+[^\s=]+)?)\s*(?:=\s*(.+))?$/);
17069
+ if (postingMatch && currentDate) {
17070
+ unknownPostings.push({
17071
+ date: currentDate,
17072
+ description: currentDescription,
17073
+ amount: postingMatch[2].trim(),
17074
+ account: postingMatch[1],
17075
+ balance: postingMatch[3]?.trim()
17076
+ });
17077
+ }
17078
+ }
17079
+ return unknownPostings;
17080
+ }
17081
+ function countTransactions(hledgerOutput) {
17082
+ const lines = hledgerOutput.split(`
17083
+ `);
17084
+ let count = 0;
17085
+ for (const line of lines) {
17086
+ if (/^\d{4}-\d{2}-\d{2}\s+/.test(line)) {
17087
+ count++;
17088
+ }
17089
+ }
17090
+ return count;
17091
+ }
17092
+
17093
+ // src/tools/import-statements.ts
17094
+ function findPendingCsvFiles(pendingDir, provider, currency) {
17095
+ const csvFiles = [];
17096
+ if (!fs6.existsSync(pendingDir)) {
17097
+ return csvFiles;
17098
+ }
17099
+ let searchPath = pendingDir;
17100
+ if (provider) {
17101
+ searchPath = path6.join(searchPath, provider);
17102
+ if (currency) {
17103
+ searchPath = path6.join(searchPath, currency);
17104
+ }
17105
+ }
17106
+ if (!fs6.existsSync(searchPath)) {
17107
+ return csvFiles;
17108
+ }
17109
+ function scanDirectory(directory) {
17110
+ const entries = fs6.readdirSync(directory, { withFileTypes: true });
17111
+ for (const entry of entries) {
17112
+ const fullPath = path6.join(directory, entry.name);
17113
+ if (entry.isDirectory()) {
17114
+ scanDirectory(fullPath);
17115
+ } else if (entry.isFile() && entry.name.endsWith(".csv")) {
17116
+ csvFiles.push(fullPath);
17117
+ }
17118
+ }
17119
+ }
17120
+ scanDirectory(searchPath);
17121
+ return csvFiles.sort();
17122
+ }
17123
+ async function importStatementsCore(directory, agent, options, configLoader = loadImportConfig, hledgerExecutor = defaultHledgerExecutor) {
17124
+ if (agent !== "accountant") {
17125
+ return JSON.stringify({
17126
+ success: false,
17127
+ error: `This tool is restricted to the accountant agent only. Called by: ${agent || "main assistant"}`,
17128
+ hint: "Use: Task(subagent_type='accountant', prompt='import statements')"
17129
+ });
17130
+ }
17131
+ let config2;
17132
+ try {
17133
+ config2 = configLoader(directory);
17134
+ } catch (error45) {
17135
+ return JSON.stringify({
17136
+ success: false,
17137
+ error: `Failed to load configuration: ${error45 instanceof Error ? error45.message : String(error45)}`,
17138
+ hint: 'Ensure config/import/providers.yaml exists with required paths including "rules"'
17139
+ });
17140
+ }
17141
+ const pendingDir = path6.join(directory, config2.paths.pending);
17142
+ const rulesDir = path6.join(directory, config2.paths.rules);
17143
+ const doneDir = path6.join(directory, config2.paths.done);
17144
+ const rulesMapping = loadRulesMapping(rulesDir);
17145
+ const csvFiles = findPendingCsvFiles(pendingDir, options.provider, options.currency);
17146
+ if (csvFiles.length === 0) {
17147
+ return JSON.stringify({
17148
+ success: true,
17149
+ files: [],
17150
+ summary: {
17151
+ filesProcessed: 0,
17152
+ filesWithErrors: 0,
17153
+ filesWithoutRules: 0,
17154
+ totalTransactions: 0,
17155
+ matched: 0,
17156
+ unknown: 0
17157
+ },
17158
+ message: "No CSV files found to process"
17159
+ });
17160
+ }
17161
+ const fileResults = [];
17162
+ let totalTransactions = 0;
17163
+ let totalMatched = 0;
17164
+ let totalUnknown = 0;
17165
+ let filesWithErrors = 0;
17166
+ let filesWithoutRules = 0;
17167
+ for (const csvFile of csvFiles) {
17168
+ const rulesFile = findRulesForCsv(csvFile, rulesMapping);
17169
+ if (!rulesFile) {
17170
+ filesWithoutRules++;
17171
+ fileResults.push({
17172
+ csv: path6.relative(directory, csvFile),
17173
+ rulesFile: null,
17174
+ totalTransactions: 0,
17175
+ matchedTransactions: 0,
17176
+ unknownPostings: [],
17177
+ error: "No matching rules file found"
17178
+ });
17179
+ continue;
17180
+ }
17181
+ const result = await hledgerExecutor(["print", "-f", csvFile, "--rules-file", rulesFile]);
17182
+ if (result.exitCode !== 0) {
17183
+ filesWithErrors++;
17184
+ fileResults.push({
17185
+ csv: path6.relative(directory, csvFile),
17186
+ rulesFile: path6.relative(directory, rulesFile),
17187
+ totalTransactions: 0,
17188
+ matchedTransactions: 0,
17189
+ unknownPostings: [],
17190
+ error: `hledger error: ${result.stderr.trim() || "Unknown error"}`
17191
+ });
17192
+ continue;
17193
+ }
17194
+ const unknownPostings = parseUnknownPostings(result.stdout);
17195
+ const transactionCount = countTransactions(result.stdout);
17196
+ const matchedCount = transactionCount - unknownPostings.length;
17197
+ totalTransactions += transactionCount;
17198
+ totalMatched += matchedCount;
17199
+ totalUnknown += unknownPostings.length;
17200
+ fileResults.push({
17201
+ csv: path6.relative(directory, csvFile),
17202
+ rulesFile: path6.relative(directory, rulesFile),
17203
+ totalTransactions: transactionCount,
17204
+ matchedTransactions: matchedCount,
17205
+ unknownPostings
17206
+ });
17207
+ }
17208
+ const hasUnknowns = totalUnknown > 0;
17209
+ const hasErrors = filesWithErrors > 0 || filesWithoutRules > 0;
17210
+ if (options.checkOnly !== false) {
17211
+ const result = {
17212
+ success: !hasUnknowns && !hasErrors,
17213
+ files: fileResults,
17214
+ summary: {
17215
+ filesProcessed: csvFiles.length,
17216
+ filesWithErrors,
17217
+ filesWithoutRules,
17218
+ totalTransactions,
17219
+ matched: totalMatched,
17220
+ unknown: totalUnknown
17221
+ }
17222
+ };
17223
+ if (hasUnknowns) {
17224
+ result.message = `Found ${totalUnknown} transaction(s) with unknown accounts. Add rules to categorize them.`;
17225
+ } else if (hasErrors) {
17226
+ result.message = `Some files had errors. Check the file results for details.`;
17227
+ } else {
17228
+ result.message = "All transactions matched. Ready to import with checkOnly: false";
17229
+ }
17230
+ return JSON.stringify(result);
17231
+ }
17232
+ if (hasUnknowns || hasErrors) {
17233
+ return JSON.stringify({
17234
+ success: false,
17235
+ files: fileResults,
17236
+ summary: {
17237
+ filesProcessed: csvFiles.length,
17238
+ filesWithErrors,
17239
+ filesWithoutRules,
17240
+ totalTransactions,
17241
+ matched: totalMatched,
17242
+ unknown: totalUnknown
17243
+ },
17244
+ error: "Cannot import: some transactions have unknown accounts or files have errors",
17245
+ hint: "Run with checkOnly: true to see details, then add missing rules"
17246
+ });
17247
+ }
17248
+ const importedFiles = [];
17249
+ for (const csvFile of csvFiles) {
17250
+ const rulesFile = findRulesForCsv(csvFile, rulesMapping);
17251
+ if (!rulesFile)
17252
+ continue;
17253
+ const result = await hledgerExecutor(["import", csvFile, "--rules-file", rulesFile]);
17254
+ if (result.exitCode !== 0) {
17255
+ return JSON.stringify({
17256
+ success: false,
17257
+ files: fileResults,
17258
+ summary: {
17259
+ filesProcessed: csvFiles.length,
17260
+ filesWithErrors: 1,
17261
+ filesWithoutRules,
17262
+ totalTransactions,
17263
+ matched: totalMatched,
17264
+ unknown: totalUnknown
17265
+ },
17266
+ error: `Import failed for ${path6.relative(directory, csvFile)}: ${result.stderr.trim()}`
17267
+ });
17268
+ }
17269
+ importedFiles.push(csvFile);
17270
+ }
17271
+ for (const csvFile of importedFiles) {
17272
+ const relativePath = path6.relative(pendingDir, csvFile);
17273
+ const destPath = path6.join(doneDir, relativePath);
17274
+ const destDir = path6.dirname(destPath);
17275
+ if (!fs6.existsSync(destDir)) {
17276
+ fs6.mkdirSync(destDir, { recursive: true });
17277
+ }
17278
+ fs6.renameSync(csvFile, destPath);
17279
+ }
17280
+ return JSON.stringify({
17281
+ success: true,
17282
+ files: fileResults.map((f) => ({
17283
+ ...f,
17284
+ imported: true
17285
+ })),
17286
+ summary: {
17287
+ filesProcessed: csvFiles.length,
17288
+ filesWithErrors: 0,
17289
+ filesWithoutRules: 0,
17290
+ totalTransactions,
17291
+ matched: totalMatched,
17292
+ unknown: 0
17293
+ },
17294
+ message: `Successfully imported ${totalTransactions} transaction(s) from ${importedFiles.length} file(s)`
17295
+ });
17296
+ }
17297
+ var import_statements_default = tool({
17298
+ description: `Import classified bank statement CSVs into hledger using rules files.
17299
+
17300
+ This tool processes CSV files in the pending import directory and uses hledger's CSV import capabilities with matching rules files.
17301
+
17302
+ **Check Mode (checkOnly: true, default):**
17303
+ - Runs hledger print to preview transactions
17304
+ - Identifies transactions with 'income:unknown' or 'expenses:unknown' accounts
17305
+ - These indicate missing rules that need to be added
17306
+
17307
+ **Import Mode (checkOnly: false):**
17308
+ - First validates all transactions have known accounts
17309
+ - If any unknowns exist, aborts and reports them
17310
+ - If all clean, imports transactions and moves CSVs to done directory
17311
+
17312
+ **Workflow:**
17313
+ 1. Run with checkOnly: true (or no args)
17314
+ 2. If unknowns found, add rules to the appropriate .rules file
17315
+ 3. Repeat until no unknowns
17316
+ 4. Run with checkOnly: false to import`,
17317
+ args: {
17318
+ provider: tool.schema.string().optional().describe('Filter by provider (e.g., "revolut", "ubs"). If omitted, process all providers.'),
17319
+ currency: tool.schema.string().optional().describe('Filter by currency (e.g., "chf", "eur"). If omitted, process all currencies for the provider.'),
17320
+ checkOnly: tool.schema.boolean().optional().describe("If true (default), only check for unknown accounts without importing. Set to false to perform actual import.")
17321
+ },
17322
+ async execute(params, context) {
17323
+ const { directory, agent } = context;
17324
+ return importStatementsCore(directory, agent, {
17325
+ provider: params.provider,
17326
+ currency: params.currency,
17327
+ checkOnly: params.checkOnly
17328
+ });
17329
+ }
17330
+ });
16971
17331
  // src/index.ts
16972
- var __dirname2 = dirname2(fileURLToPath(import.meta.url));
16973
- var AGENT_FILE = join5(__dirname2, "..", "agent", "accountant.md");
17332
+ var __dirname2 = dirname4(fileURLToPath(import.meta.url));
17333
+ var AGENT_FILE = join7(__dirname2, "..", "agent", "accountant.md");
16974
17334
  var AccountantPlugin = async () => {
16975
17335
  const agent = loadAgent(AGENT_FILE);
16976
17336
  return {
16977
17337
  tool: {
16978
17338
  "update-prices": update_prices_default,
16979
- "classify-statements": classify_statements_default
17339
+ "classify-statements": classify_statements_default,
17340
+ "import-statements": import_statements_default
16980
17341
  },
16981
17342
  config: async (config2) => {
16982
17343
  if (agent) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzzle/opencode-accountant",
3
- "version": "0.0.9-next.1",
3
+ "version": "0.0.10",
4
4
  "description": "An OpenCode accounting agent, specialized in double-entry-bookkepping with hledger",
5
5
  "author": {
6
6
  "name": "ali bengali",