@fuzzle/opencode-accountant 0.5.0-next.1 → 0.5.0

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.
Files changed (2) hide show
  1. package/dist/index.js +141 -133
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2676,12 +2676,12 @@ var init_js_yaml = __esm(() => {
2676
2676
  });
2677
2677
 
2678
2678
  // src/utils/agentLoader.ts
2679
- import * as fs from "fs";
2679
+ import { existsSync, readFileSync } from "fs";
2680
2680
  function loadAgent(filePath) {
2681
- if (!fs.existsSync(filePath)) {
2681
+ if (!existsSync(filePath)) {
2682
2682
  return null;
2683
2683
  }
2684
- const content = fs.readFileSync(filePath, "utf-8");
2684
+ const content = readFileSync(filePath, "utf-8");
2685
2685
  const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
2686
2686
  if (!match) {
2687
2687
  throw new Error(`Invalid frontmatter format in ${filePath}`);
@@ -4231,7 +4231,7 @@ __export(exports_accountSuggester, {
4231
4231
  extractRulePatternsFromFile: () => extractRulePatternsFromFile,
4232
4232
  clearSuggestionCache: () => clearSuggestionCache
4233
4233
  });
4234
- import * as fs14 from "fs";
4234
+ import * as fs13 from "fs";
4235
4235
  import * as crypto from "crypto";
4236
4236
  function clearSuggestionCache() {
4237
4237
  Object.keys(suggestionCache).forEach((key) => delete suggestionCache[key]);
@@ -4240,11 +4240,11 @@ function hashTransaction(posting) {
4240
4240
  const data = `${posting.description}|${posting.amount}|${posting.account}`;
4241
4241
  return crypto.createHash("md5").update(data).digest("hex");
4242
4242
  }
4243
- function loadExistingAccounts(yearJournalPath) {
4244
- if (!fs14.existsSync(yearJournalPath)) {
4243
+ async function loadExistingAccounts(yearJournalPath) {
4244
+ if (!fs13.existsSync(yearJournalPath)) {
4245
4245
  return [];
4246
4246
  }
4247
- const content = fs14.readFileSync(yearJournalPath, "utf-8");
4247
+ const content = fs13.readFileSync(yearJournalPath, "utf-8");
4248
4248
  const lines = content.split(`
4249
4249
  `);
4250
4250
  const accounts = [];
@@ -4259,11 +4259,11 @@ function loadExistingAccounts(yearJournalPath) {
4259
4259
  }
4260
4260
  return accounts.sort();
4261
4261
  }
4262
- function extractRulePatternsFromFile(rulesPath) {
4263
- if (!fs14.existsSync(rulesPath)) {
4262
+ async function extractRulePatternsFromFile(rulesPath) {
4263
+ if (!fs13.existsSync(rulesPath)) {
4264
4264
  return [];
4265
4265
  }
4266
- const content = fs14.readFileSync(rulesPath, "utf-8");
4266
+ const content = fs13.readFileSync(rulesPath, "utf-8");
4267
4267
  const lines = content.split(`
4268
4268
  `);
4269
4269
  const patterns = [];
@@ -16844,16 +16844,16 @@ function checkAccountantAgent(agent, toolPrompt, additionalFields) {
16844
16844
 
16845
16845
  // src/utils/yamlLoader.ts
16846
16846
  init_js_yaml();
16847
- import * as fs2 from "fs";
16847
+ import * as fs from "fs";
16848
16848
  import * as path from "path";
16849
16849
  function loadYamlConfig(directory, configFile, validator, notFoundMessage) {
16850
16850
  const configPath = path.join(directory, configFile);
16851
- if (!fs2.existsSync(configPath)) {
16851
+ if (!fs.existsSync(configPath)) {
16852
16852
  throw new Error(notFoundMessage || `Configuration file not found: ${configFile}. Please create this file to configure the feature.`);
16853
16853
  }
16854
16854
  let parsed;
16855
16855
  try {
16856
- const content = fs2.readFileSync(configPath, "utf-8");
16856
+ const content = fs.readFileSync(configPath, "utf-8");
16857
16857
  parsed = jsYaml.load(content);
16858
16858
  } catch (err) {
16859
16859
  if (err instanceof jsYaml.YAMLException) {
@@ -16910,14 +16910,14 @@ function loadPricesConfig(directory) {
16910
16910
  }
16911
16911
 
16912
16912
  // src/utils/journalUtils.ts
16913
- import * as fs4 from "fs";
16913
+ import * as fs3 from "fs";
16914
16914
  import * as path3 from "path";
16915
16915
 
16916
16916
  // src/utils/fileUtils.ts
16917
- import * as fs3 from "fs";
16917
+ import * as fs2 from "fs";
16918
16918
  import * as path2 from "path";
16919
16919
  function findCsvFiles(baseDir, options = {}) {
16920
- if (!fs3.existsSync(baseDir)) {
16920
+ if (!fs2.existsSync(baseDir)) {
16921
16921
  return [];
16922
16922
  }
16923
16923
  let searchDir = baseDir;
@@ -16927,13 +16927,13 @@ function findCsvFiles(baseDir, options = {}) {
16927
16927
  searchDir = path2.join(searchDir, options.subsubdir);
16928
16928
  }
16929
16929
  }
16930
- if (!fs3.existsSync(searchDir)) {
16930
+ if (!fs2.existsSync(searchDir)) {
16931
16931
  return [];
16932
16932
  }
16933
16933
  const csvFiles = [];
16934
16934
  if (options.recursive) {
16935
16935
  let scanDirectory = function(dir) {
16936
- const entries = fs3.readdirSync(dir, { withFileTypes: true });
16936
+ const entries = fs2.readdirSync(dir, { withFileTypes: true });
16937
16937
  for (const entry of entries) {
16938
16938
  const fullPath = path2.join(dir, entry.name);
16939
16939
  if (entry.isDirectory()) {
@@ -16945,12 +16945,12 @@ function findCsvFiles(baseDir, options = {}) {
16945
16945
  };
16946
16946
  scanDirectory(searchDir);
16947
16947
  } else {
16948
- const entries = fs3.readdirSync(searchDir);
16948
+ const entries = fs2.readdirSync(searchDir);
16949
16949
  for (const name of entries) {
16950
16950
  if (!name.toLowerCase().endsWith(".csv"))
16951
16951
  continue;
16952
16952
  const fullPath = path2.join(searchDir, name);
16953
- if (fs3.statSync(fullPath).isFile()) {
16953
+ if (fs2.statSync(fullPath).isFile()) {
16954
16954
  csvFiles.push(options.fullPaths ? fullPath : name);
16955
16955
  }
16956
16956
  }
@@ -16958,8 +16958,8 @@ function findCsvFiles(baseDir, options = {}) {
16958
16958
  return csvFiles.sort();
16959
16959
  }
16960
16960
  function ensureDirectory(dirPath) {
16961
- if (!fs3.existsSync(dirPath)) {
16962
- fs3.mkdirSync(dirPath, { recursive: true });
16961
+ if (!fs2.existsSync(dirPath)) {
16962
+ fs2.mkdirSync(dirPath, { recursive: true });
16963
16963
  }
16964
16964
  }
16965
16965
 
@@ -16969,8 +16969,8 @@ function extractDateFromPriceLine(line) {
16969
16969
  }
16970
16970
  function updatePriceJournal(journalPath, newPriceLines) {
16971
16971
  let existingLines = [];
16972
- if (fs4.existsSync(journalPath)) {
16973
- existingLines = fs4.readFileSync(journalPath, "utf-8").split(`
16972
+ if (fs3.existsSync(journalPath)) {
16973
+ existingLines = fs3.readFileSync(journalPath, "utf-8").split(`
16974
16974
  `).filter((line) => line.trim() !== "");
16975
16975
  }
16976
16976
  const priceMap = new Map;
@@ -16985,7 +16985,7 @@ function updatePriceJournal(journalPath, newPriceLines) {
16985
16985
  priceMap.set(date5, line);
16986
16986
  }
16987
16987
  const sortedLines = Array.from(priceMap.entries()).sort((a, b) => a[0].localeCompare(b[0])).map(([, line]) => line);
16988
- fs4.writeFileSync(journalPath, sortedLines.join(`
16988
+ fs3.writeFileSync(journalPath, sortedLines.join(`
16989
16989
  `) + `
16990
16990
  `);
16991
16991
  }
@@ -16993,15 +16993,17 @@ function ensureYearJournalExists(directory, year) {
16993
16993
  const ledgerDir = path3.join(directory, "ledger");
16994
16994
  const yearJournalPath = path3.join(ledgerDir, `${year}.journal`);
16995
16995
  const mainJournalPath = path3.join(directory, ".hledger.journal");
16996
- ensureDirectory(ledgerDir);
16997
- if (!fs4.existsSync(yearJournalPath)) {
16998
- fs4.writeFileSync(yearJournalPath, `; ${year} transactions
16996
+ if (!fs3.existsSync(ledgerDir)) {
16997
+ fs3.mkdirSync(ledgerDir, { recursive: true });
16998
+ }
16999
+ if (!fs3.existsSync(yearJournalPath)) {
17000
+ fs3.writeFileSync(yearJournalPath, `; ${year} transactions
16999
17001
  `);
17000
17002
  }
17001
- if (!fs4.existsSync(mainJournalPath)) {
17003
+ if (!fs3.existsSync(mainJournalPath)) {
17002
17004
  throw new Error(`.hledger.journal not found at ${mainJournalPath}. Create it first with appropriate includes.`);
17003
17005
  }
17004
- const mainJournalContent = fs4.readFileSync(mainJournalPath, "utf-8");
17006
+ const mainJournalContent = fs3.readFileSync(mainJournalPath, "utf-8");
17005
17007
  const includeDirective = `include ledger/${year}.journal`;
17006
17008
  const lines = mainJournalContent.split(`
17007
17009
  `);
@@ -17013,7 +17015,7 @@ function ensureYearJournalExists(directory, year) {
17013
17015
  const newContent = mainJournalContent.trimEnd() + `
17014
17016
  ` + includeDirective + `
17015
17017
  `;
17016
- fs4.writeFileSync(mainJournalPath, newContent);
17018
+ fs3.writeFileSync(mainJournalPath, newContent);
17017
17019
  }
17018
17020
  return yearJournalPath;
17019
17021
  }
@@ -17033,30 +17035,6 @@ function getNextDay(dateStr) {
17033
17035
  return formatDateISO(date5);
17034
17036
  }
17035
17037
 
17036
- // src/utils/resultHelpers.ts
17037
- function buildToolErrorResult(error45, hint, extra) {
17038
- const result = {
17039
- success: false,
17040
- error: error45
17041
- };
17042
- if (hint) {
17043
- result.hint = hint;
17044
- }
17045
- if (extra) {
17046
- Object.assign(result, extra);
17047
- }
17048
- return JSON.stringify(result);
17049
- }
17050
- function buildToolSuccessResult(data) {
17051
- const result = {
17052
- success: true
17053
- };
17054
- if (data) {
17055
- Object.assign(result, data);
17056
- }
17057
- return JSON.stringify(result);
17058
- }
17059
-
17060
17038
  // src/tools/fetch-currency-prices.ts
17061
17039
  async function defaultPriceFetcher(cmdArgs) {
17062
17040
  const result = await $`pricehist ${cmdArgs}`.quiet();
@@ -17080,10 +17058,10 @@ function buildPricehistArgs(startDate, endDate, currencyConfig) {
17080
17058
  return cmdArgs;
17081
17059
  }
17082
17060
  function buildErrorResult(error45) {
17083
- return buildToolErrorResult(error45);
17061
+ return JSON.stringify({ error: error45 });
17084
17062
  }
17085
17063
  function buildSuccessResult(results, endDate, backfill) {
17086
- return buildToolSuccessResult({
17064
+ return JSON.stringify({
17087
17065
  success: results.every((r) => !("error" in r)),
17088
17066
  endDate,
17089
17067
  backfill,
@@ -17172,7 +17150,7 @@ var fetch_currency_prices_default = tool({
17172
17150
  }
17173
17151
  });
17174
17152
  // src/tools/classify-statements.ts
17175
- import * as fs6 from "fs";
17153
+ import * as fs5 from "fs";
17176
17154
  import * as path6 from "path";
17177
17155
 
17178
17156
  // src/utils/importConfig.ts
@@ -17427,14 +17405,17 @@ function detectProvider(filename, content, config2) {
17427
17405
  }
17428
17406
 
17429
17407
  // src/utils/importContext.ts
17430
- import * as fs5 from "fs";
17408
+ import * as fs4 from "fs";
17431
17409
  import * as path5 from "path";
17432
17410
  import { randomUUID } from "crypto";
17433
17411
  function getContextPath(directory, contextId) {
17434
17412
  return path5.join(directory, ".memory", `${contextId}.json`);
17435
17413
  }
17436
17414
  function ensureMemoryDir(directory) {
17437
- ensureDirectory(path5.join(directory, ".memory"));
17415
+ const memoryDir = path5.join(directory, ".memory");
17416
+ if (!fs4.existsSync(memoryDir)) {
17417
+ fs4.mkdirSync(memoryDir, { recursive: true });
17418
+ }
17438
17419
  }
17439
17420
  function createContext(directory, params) {
17440
17421
  const now = new Date().toISOString();
@@ -17456,7 +17437,7 @@ function createContext(directory, params) {
17456
17437
  };
17457
17438
  ensureMemoryDir(directory);
17458
17439
  const contextPath = getContextPath(directory, context.id);
17459
- fs5.writeFileSync(contextPath, JSON.stringify(context, null, 2), "utf-8");
17440
+ fs4.writeFileSync(contextPath, JSON.stringify(context, null, 2), "utf-8");
17460
17441
  return context;
17461
17442
  }
17462
17443
  function validateContext(context, contextId) {
@@ -17475,10 +17456,10 @@ function validateContext(context, contextId) {
17475
17456
  }
17476
17457
  function loadContext(directory, contextId) {
17477
17458
  const contextPath = getContextPath(directory, contextId);
17478
- if (!fs5.existsSync(contextPath)) {
17459
+ if (!fs4.existsSync(contextPath)) {
17479
17460
  throw new Error(`Context not found: ${contextId}`);
17480
17461
  }
17481
- const content = fs5.readFileSync(contextPath, "utf-8");
17462
+ const content = fs4.readFileSync(contextPath, "utf-8");
17482
17463
  let context;
17483
17464
  try {
17484
17465
  context = JSON.parse(content);
@@ -17498,13 +17479,14 @@ function updateContext(directory, contextId, updates) {
17498
17479
  updatedAt: new Date().toISOString()
17499
17480
  };
17500
17481
  const contextPath = getContextPath(directory, contextId);
17501
- fs5.writeFileSync(contextPath, JSON.stringify(updatedContext, null, 2), "utf-8");
17482
+ fs4.writeFileSync(contextPath, JSON.stringify(updatedContext, null, 2), "utf-8");
17502
17483
  return updatedContext;
17503
17484
  }
17504
17485
 
17505
17486
  // src/tools/classify-statements.ts
17506
17487
  function buildSuccessResult2(classified, unrecognized, message) {
17507
- return buildToolSuccessResult({
17488
+ return JSON.stringify({
17489
+ success: true,
17508
17490
  classified,
17509
17491
  unrecognized,
17510
17492
  message,
@@ -17516,14 +17498,19 @@ function buildSuccessResult2(classified, unrecognized, message) {
17516
17498
  });
17517
17499
  }
17518
17500
  function buildErrorResult2(error45, hint) {
17519
- return buildToolErrorResult(error45, hint, {
17501
+ return JSON.stringify({
17502
+ success: false,
17503
+ error: error45,
17504
+ hint,
17520
17505
  classified: [],
17521
17506
  unrecognized: []
17522
17507
  });
17523
17508
  }
17524
17509
  function buildCollisionError(collisions) {
17525
17510
  const error45 = `Cannot classify: ${collisions.length} file(s) would overwrite existing pending files.`;
17526
- return buildToolErrorResult(error45, undefined, {
17511
+ return JSON.stringify({
17512
+ success: false,
17513
+ error: error45,
17527
17514
  collisions,
17528
17515
  classified: [],
17529
17516
  unrecognized: []
@@ -17534,7 +17521,7 @@ function planMoves(csvFiles, importsDir, pendingDir, unrecognizedDir, config2) {
17534
17521
  const collisions = [];
17535
17522
  for (const filename of csvFiles) {
17536
17523
  const sourcePath = path6.join(importsDir, filename);
17537
- const content = fs6.readFileSync(sourcePath, "utf-8");
17524
+ const content = fs5.readFileSync(sourcePath, "utf-8");
17538
17525
  const detection = detectProvider(filename, content, config2);
17539
17526
  let targetPath;
17540
17527
  let targetFilename;
@@ -17546,7 +17533,7 @@ function planMoves(csvFiles, importsDir, pendingDir, unrecognizedDir, config2) {
17546
17533
  targetFilename = filename;
17547
17534
  targetPath = path6.join(unrecognizedDir, filename);
17548
17535
  }
17549
- if (fs6.existsSync(targetPath)) {
17536
+ if (fs5.existsSync(targetPath)) {
17550
17537
  collisions.push({
17551
17538
  filename,
17552
17539
  existingPath: targetPath
@@ -17582,7 +17569,7 @@ function executeMoves(plannedMoves, config2, unrecognizedDir, directory) {
17582
17569
  if (move.detection) {
17583
17570
  const targetDir = path6.dirname(move.targetPath);
17584
17571
  ensureDirectory(targetDir);
17585
- fs6.renameSync(move.sourcePath, move.targetPath);
17572
+ fs5.renameSync(move.sourcePath, move.targetPath);
17586
17573
  const targetPath = path6.join(config2.paths.pending, move.detection.provider, move.detection.currency, move.targetFilename);
17587
17574
  const metadata = extractMetadata2(move.detection);
17588
17575
  const context = createContext(directory, {
@@ -17607,7 +17594,7 @@ function executeMoves(plannedMoves, config2, unrecognizedDir, directory) {
17607
17594
  });
17608
17595
  } else {
17609
17596
  ensureDirectory(unrecognizedDir);
17610
- fs6.renameSync(move.sourcePath, move.targetPath);
17597
+ fs5.renameSync(move.sourcePath, move.targetPath);
17611
17598
  unrecognized.push({
17612
17599
  filename: move.filename,
17613
17600
  targetPath: path6.join(config2.paths.unrecognized, move.filename)
@@ -17662,7 +17649,7 @@ For each CSV file:
17662
17649
  }
17663
17650
  });
17664
17651
  // src/tools/import-statements.ts
17665
- import * as fs10 from "fs";
17652
+ import * as fs9 from "fs";
17666
17653
  import * as path9 from "path";
17667
17654
 
17668
17655
  // node_modules/minimatch/dist/esm/index.js
@@ -21447,8 +21434,8 @@ class PathScurryBase {
21447
21434
  #children;
21448
21435
  nocase;
21449
21436
  #fs;
21450
- constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs: fs7 = defaultFS } = {}) {
21451
- this.#fs = fsFromOption(fs7);
21437
+ constructor(cwd = process.cwd(), pathImpl, sep2, { nocase, childrenCacheSize = 16 * 1024, fs: fs6 = defaultFS } = {}) {
21438
+ this.#fs = fsFromOption(fs6);
21452
21439
  if (cwd instanceof URL || cwd.startsWith("file://")) {
21453
21440
  cwd = fileURLToPath(cwd);
21454
21441
  }
@@ -21924,8 +21911,8 @@ class PathScurryWin32 extends PathScurryBase {
21924
21911
  parseRootPath(dir) {
21925
21912
  return win32.parse(dir).root.toUpperCase();
21926
21913
  }
21927
- newRoot(fs7) {
21928
- return new PathWin32(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs: fs7 });
21914
+ newRoot(fs6) {
21915
+ return new PathWin32(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs: fs6 });
21929
21916
  }
21930
21917
  isAbsolute(p) {
21931
21918
  return p.startsWith("/") || p.startsWith("\\") || /^[a-z]:(\/|\\)/i.test(p);
@@ -21942,8 +21929,8 @@ class PathScurryPosix extends PathScurryBase {
21942
21929
  parseRootPath(_dir) {
21943
21930
  return "/";
21944
21931
  }
21945
- newRoot(fs7) {
21946
- return new PathPosix(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs: fs7 });
21932
+ newRoot(fs6) {
21933
+ return new PathPosix(this.rootPath, IFDIR, undefined, this.roots, this.nocase, this.childrenCache(), { fs: fs6 });
21947
21934
  }
21948
21935
  isAbsolute(p) {
21949
21936
  return p.startsWith("/");
@@ -22945,7 +22932,7 @@ var glob = Object.assign(glob_, {
22945
22932
  glob.glob = glob;
22946
22933
 
22947
22934
  // src/utils/rulesMatcher.ts
22948
- import * as fs7 from "fs";
22935
+ import * as fs6 from "fs";
22949
22936
  import * as path8 from "path";
22950
22937
  function parseSourceDirective(content) {
22951
22938
  const match2 = content.match(/^source\s+([^\n#]+)/m);
@@ -22963,22 +22950,22 @@ function resolveSourcePath(sourcePath, rulesFilePath) {
22963
22950
  }
22964
22951
  function loadRulesMapping(rulesDir) {
22965
22952
  const mapping = {};
22966
- if (!fs7.existsSync(rulesDir)) {
22953
+ if (!fs6.existsSync(rulesDir)) {
22967
22954
  return mapping;
22968
22955
  }
22969
- const files = fs7.readdirSync(rulesDir);
22956
+ const files = fs6.readdirSync(rulesDir);
22970
22957
  for (const file2 of files) {
22971
22958
  if (!file2.endsWith(".rules")) {
22972
22959
  continue;
22973
22960
  }
22974
22961
  const rulesFilePath = path8.join(rulesDir, file2);
22975
- const stat = fs7.statSync(rulesFilePath);
22962
+ const stat = fs6.statSync(rulesFilePath);
22976
22963
  if (!stat.isFile()) {
22977
22964
  continue;
22978
22965
  }
22979
22966
  let content;
22980
22967
  try {
22981
- content = fs7.readFileSync(rulesFilePath, "utf-8");
22968
+ content = fs6.readFileSync(rulesFilePath, "utf-8");
22982
22969
  } catch {
22983
22970
  continue;
22984
22971
  }
@@ -23153,7 +23140,7 @@ async function getAccountBalance(mainJournalPath, account, asOfDate, executor =
23153
23140
  }
23154
23141
 
23155
23142
  // src/utils/rulesParser.ts
23156
- import * as fs8 from "fs";
23143
+ import * as fs7 from "fs";
23157
23144
  function parseSkipRows(rulesContent) {
23158
23145
  const match2 = rulesContent.match(/^skip\s+(\d+)/m);
23159
23146
  return match2 ? parseInt(match2[1], 10) : 0;
@@ -23219,7 +23206,7 @@ function parseAccount1(rulesContent) {
23219
23206
  }
23220
23207
  function getAccountFromRulesFile(rulesFilePath) {
23221
23208
  try {
23222
- const content = fs8.readFileSync(rulesFilePath, "utf-8");
23209
+ const content = fs7.readFileSync(rulesFilePath, "utf-8");
23223
23210
  return parseAccount1(content);
23224
23211
  } catch {
23225
23212
  return null;
@@ -23239,7 +23226,7 @@ function parseRulesFile(rulesContent) {
23239
23226
 
23240
23227
  // src/utils/csvParser.ts
23241
23228
  var import_papaparse2 = __toESM(require_papaparse(), 1);
23242
- import * as fs9 from "fs";
23229
+ import * as fs8 from "fs";
23243
23230
 
23244
23231
  // src/utils/balanceUtils.ts
23245
23232
  function parseAmountValue(amountStr) {
@@ -23289,7 +23276,7 @@ function balancesMatch(balance1, balance2) {
23289
23276
  // src/utils/csvParser.ts
23290
23277
  var AMOUNT_MATCH_TOLERANCE = 0.001;
23291
23278
  function parseCsvFile(csvPath, config2) {
23292
- const csvContent = fs9.readFileSync(csvPath, "utf-8");
23279
+ const csvContent = fs8.readFileSync(csvPath, "utf-8");
23293
23280
  const lines = csvContent.split(`
23294
23281
  `);
23295
23282
  const headerIndex = config2.skipRows;
@@ -23427,7 +23414,10 @@ function findMatchingCsvRow(posting, csvRows, config2) {
23427
23414
 
23428
23415
  // src/tools/import-statements.ts
23429
23416
  function buildErrorResult3(error45, hint) {
23430
- return buildToolErrorResult(error45, hint, {
23417
+ const result = {
23418
+ success: false,
23419
+ error: error45,
23420
+ hint,
23431
23421
  files: [],
23432
23422
  summary: {
23433
23423
  filesProcessed: 0,
@@ -23437,16 +23427,28 @@ function buildErrorResult3(error45, hint) {
23437
23427
  matched: 0,
23438
23428
  unknown: 0
23439
23429
  }
23440
- });
23430
+ };
23431
+ return JSON.stringify(result);
23441
23432
  }
23442
23433
  function buildErrorResultWithDetails(error45, files, summary, hint) {
23443
- return buildToolErrorResult(error45, hint, { files, summary });
23434
+ return JSON.stringify({
23435
+ success: false,
23436
+ error: error45,
23437
+ hint,
23438
+ files,
23439
+ summary
23440
+ });
23444
23441
  }
23445
23442
  function buildSuccessResult3(files, summary, message) {
23446
- return buildToolSuccessResult({ files, summary, message });
23443
+ return JSON.stringify({
23444
+ success: true,
23445
+ files,
23446
+ summary,
23447
+ message
23448
+ });
23447
23449
  }
23448
23450
  function findCsvFromRulesFile(rulesFile) {
23449
- const content = fs10.readFileSync(rulesFile, "utf-8");
23451
+ const content = fs9.readFileSync(rulesFile, "utf-8");
23450
23452
  const match2 = content.match(/^source\s+([^\n#]+)/m);
23451
23453
  if (!match2) {
23452
23454
  return null;
@@ -23459,8 +23461,8 @@ function findCsvFromRulesFile(rulesFile) {
23459
23461
  return null;
23460
23462
  }
23461
23463
  matches.sort((a, b) => {
23462
- const aStat = fs10.statSync(a);
23463
- const bStat = fs10.statSync(b);
23464
+ const aStat = fs9.statSync(a);
23465
+ const bStat = fs9.statSync(b);
23464
23466
  return bStat.mtime.getTime() - aStat.mtime.getTime();
23465
23467
  });
23466
23468
  return matches[0];
@@ -23513,8 +23515,10 @@ async function executeImports(fileResults, directory, pendingDir, doneDir, hledg
23513
23515
  const relativePath = path9.relative(pendingDir, csvFile);
23514
23516
  const destPath = path9.join(doneDir, relativePath);
23515
23517
  const destDir = path9.dirname(destPath);
23516
- ensureDirectory(destDir);
23517
- fs10.renameSync(csvFile, destPath);
23518
+ if (!fs9.existsSync(destDir)) {
23519
+ fs9.mkdirSync(destDir, { recursive: true });
23520
+ }
23521
+ fs9.renameSync(csvFile, destPath);
23518
23522
  }
23519
23523
  return {
23520
23524
  success: true,
@@ -23562,7 +23566,7 @@ async function processCsvFile(csvFile, rulesMapping, directory, hledgerExecutor)
23562
23566
  const transactionYear = years.size === 1 ? Array.from(years)[0] : undefined;
23563
23567
  if (unknownPostings.length > 0) {
23564
23568
  try {
23565
- const rulesContent = fs10.readFileSync(rulesFile, "utf-8");
23569
+ const rulesContent = fs9.readFileSync(rulesFile, "utf-8");
23566
23570
  const rulesConfig = parseRulesFile(rulesContent);
23567
23571
  const csvRows = parseCsvFile(csvFile, rulesConfig);
23568
23572
  for (const posting of unknownPostings) {
@@ -23605,7 +23609,7 @@ async function importStatements(directory, agent, options, configLoader = loadIm
23605
23609
  const rulesMapping = loadRulesMapping(rulesDir);
23606
23610
  const importContext = loadContext(directory, options.contextId);
23607
23611
  const csvPath = path9.join(directory, importContext.filePath);
23608
- if (!fs10.existsSync(csvPath)) {
23612
+ if (!fs9.existsSync(csvPath)) {
23609
23613
  return buildErrorResult3(`CSV file not found: ${importContext.filePath}`, "The file may have been moved or deleted");
23610
23614
  }
23611
23615
  const csvFiles = [csvPath];
@@ -23640,8 +23644,8 @@ async function importStatements(directory, agent, options, configLoader = loadIm
23640
23644
  }
23641
23645
  for (const [_rulesFile, matchingCSVs] of rulesFileToCSVs.entries()) {
23642
23646
  matchingCSVs.sort((a, b) => {
23643
- const aStat = fs10.statSync(a);
23644
- const bStat = fs10.statSync(b);
23647
+ const aStat = fs9.statSync(a);
23648
+ const bStat = fs9.statSync(b);
23645
23649
  return bStat.mtime.getTime() - aStat.mtime.getTime();
23646
23650
  });
23647
23651
  const newestCSV = matchingCSVs[0];
@@ -23756,18 +23760,22 @@ Note: This tool is typically called via import-pipeline for the full workflow.`,
23756
23760
  }
23757
23761
  });
23758
23762
  // src/tools/reconcile-statement.ts
23759
- import * as fs11 from "fs";
23763
+ import * as fs10 from "fs";
23760
23764
  import * as path10 from "path";
23761
23765
  function buildErrorResult4(params) {
23762
- return buildToolErrorResult(params.error, params.hint, {
23766
+ const result = {
23767
+ success: false,
23763
23768
  account: params.account ?? "",
23764
23769
  expectedBalance: params.expectedBalance ?? "",
23765
23770
  actualBalance: params.actualBalance ?? "",
23766
23771
  lastTransactionDate: params.lastTransactionDate ?? "",
23767
23772
  csvFile: params.csvFile ?? "",
23768
23773
  difference: params.difference,
23769
- metadata: params.metadata
23770
- });
23774
+ metadata: params.metadata,
23775
+ error: params.error,
23776
+ hint: params.hint
23777
+ };
23778
+ return JSON.stringify(result);
23771
23779
  }
23772
23780
  function loadConfiguration(directory, configLoader) {
23773
23781
  try {
@@ -23784,7 +23792,7 @@ function loadConfiguration(directory, configLoader) {
23784
23792
  }
23785
23793
  function verifyCsvExists(directory, importContext) {
23786
23794
  const csvFile = path10.join(directory, importContext.filePath);
23787
- if (!fs11.existsSync(csvFile)) {
23795
+ if (!fs10.existsSync(csvFile)) {
23788
23796
  return {
23789
23797
  error: buildErrorResult4({
23790
23798
  error: `CSV file not found: ${importContext.filePath}`,
@@ -23822,7 +23830,7 @@ function getBalanceFromCsvAnalysis(csvFile, rulesDir) {
23822
23830
  }
23823
23831
  function extractCsvMetadata(csvFile, config2) {
23824
23832
  try {
23825
- const content = fs11.readFileSync(csvFile, "utf-8");
23833
+ const content = fs10.readFileSync(csvFile, "utf-8");
23826
23834
  const filename = path10.basename(csvFile);
23827
23835
  const detectionResult = detectProvider(filename, content, config2);
23828
23836
  if (detectionResult?.metadata) {
@@ -23900,7 +23908,7 @@ function tryExtractClosingBalanceFromCSV(csvFile, rulesDir) {
23900
23908
  if (!rulesFile) {
23901
23909
  return null;
23902
23910
  }
23903
- const rulesContent = fs11.readFileSync(rulesFile, "utf-8");
23911
+ const rulesContent = fs10.readFileSync(rulesFile, "utf-8");
23904
23912
  const rulesConfig = parseRulesFile(rulesContent);
23905
23913
  const csvRows = parseCsvFile(csvFile, rulesConfig);
23906
23914
  if (csvRows.length === 0) {
@@ -24108,13 +24116,13 @@ Note: This tool requires a contextId from a prior classify/import step.`,
24108
24116
  import * as path12 from "path";
24109
24117
 
24110
24118
  // src/utils/accountDeclarations.ts
24111
- import * as fs12 from "fs";
24119
+ import * as fs11 from "fs";
24112
24120
  function extractAccountsFromRulesFile(rulesPath) {
24113
24121
  const accounts = new Set;
24114
- if (!fs12.existsSync(rulesPath)) {
24122
+ if (!fs11.existsSync(rulesPath)) {
24115
24123
  return accounts;
24116
24124
  }
24117
- const content = fs12.readFileSync(rulesPath, "utf-8");
24125
+ const content = fs11.readFileSync(rulesPath, "utf-8");
24118
24126
  const lines = content.split(`
24119
24127
  `);
24120
24128
  for (const line of lines) {
@@ -24149,10 +24157,10 @@ function sortAccountDeclarations(accounts) {
24149
24157
  return Array.from(accounts).sort((a, b) => a.localeCompare(b));
24150
24158
  }
24151
24159
  function ensureAccountDeclarations(yearJournalPath, accounts) {
24152
- if (!fs12.existsSync(yearJournalPath)) {
24160
+ if (!fs11.existsSync(yearJournalPath)) {
24153
24161
  throw new Error(`Year journal not found: ${yearJournalPath}`);
24154
24162
  }
24155
- const content = fs12.readFileSync(yearJournalPath, "utf-8");
24163
+ const content = fs11.readFileSync(yearJournalPath, "utf-8");
24156
24164
  const lines = content.split(`
24157
24165
  `);
24158
24166
  const existingAccounts = new Set;
@@ -24214,7 +24222,7 @@ function ensureAccountDeclarations(yearJournalPath, accounts) {
24214
24222
  newContent.push("");
24215
24223
  }
24216
24224
  newContent.push(...otherLines);
24217
- fs12.writeFileSync(yearJournalPath, newContent.join(`
24225
+ fs11.writeFileSync(yearJournalPath, newContent.join(`
24218
24226
  `));
24219
24227
  return {
24220
24228
  added: Array.from(missingAccounts).sort(),
@@ -24223,7 +24231,7 @@ function ensureAccountDeclarations(yearJournalPath, accounts) {
24223
24231
  }
24224
24232
 
24225
24233
  // src/utils/logger.ts
24226
- import fs13 from "fs/promises";
24234
+ import fs12 from "fs/promises";
24227
24235
  import path11 from "path";
24228
24236
  var LOG_LINE_LIMIT = 50;
24229
24237
 
@@ -24338,8 +24346,8 @@ class MarkdownLogger {
24338
24346
  if (this.buffer.length === 0)
24339
24347
  return;
24340
24348
  try {
24341
- await fs13.mkdir(path11.dirname(this.logPath), { recursive: true });
24342
- await fs13.writeFile(this.logPath, this.buffer.join(`
24349
+ await fs12.mkdir(path11.dirname(this.logPath), { recursive: true });
24350
+ await fs12.writeFile(this.logPath, this.buffer.join(`
24343
24351
  `), "utf-8");
24344
24352
  } catch {}
24345
24353
  }
@@ -24393,12 +24401,12 @@ function buildStepResult(success2, message, details) {
24393
24401
  }
24394
24402
  return result;
24395
24403
  }
24396
- function buildPipelineSuccessResult(result, summary) {
24404
+ function buildSuccessResult4(result, summary) {
24397
24405
  result.success = true;
24398
24406
  result.summary = summary;
24399
24407
  return JSON.stringify(result);
24400
24408
  }
24401
- function buildPipelineErrorResult(result, error45, hint) {
24409
+ function buildErrorResult5(result, error45, hint) {
24402
24410
  result.success = false;
24403
24411
  result.error = error45;
24404
24412
  if (hint) {
@@ -24578,9 +24586,9 @@ async function executeDryRunStep(context, contextId, logger) {
24578
24586
  } catch {}
24579
24587
  }
24580
24588
  const suggestionContext = {
24581
- existingAccounts: yearJournalPath ? loadExistingAccounts2(yearJournalPath) : [],
24589
+ existingAccounts: yearJournalPath ? await loadExistingAccounts2(yearJournalPath) : [],
24582
24590
  rulesFilePath: firstRulesFile,
24583
- existingRules: firstRulesFile ? extractRulePatternsFromFile2(firstRulesFile) : undefined,
24591
+ existingRules: firstRulesFile ? await extractRulePatternsFromFile2(firstRulesFile) : undefined,
24584
24592
  yearJournalPath,
24585
24593
  logger
24586
24594
  };
@@ -24722,7 +24730,7 @@ async function executeReconcileStep(context, contextId, logger) {
24722
24730
  function handleNoTransactions(result) {
24723
24731
  result.steps.import = buildStepResult(true, "No transactions to import");
24724
24732
  result.steps.reconcile = buildStepResult(true, "Reconciliation skipped (no transactions)");
24725
- return buildPipelineSuccessResult(result, "No transactions found to import");
24733
+ return buildSuccessResult4(result, "No transactions found to import");
24726
24734
  }
24727
24735
  async function importPipeline(directory, agent, options, configLoader = loadImportConfig, hledgerExecutor = defaultHledgerExecutor) {
24728
24736
  const restrictionError = checkAccountantAgent(agent, "import pipeline");
@@ -24751,7 +24759,7 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
24751
24759
  const contextIds = await executeClassifyStep(context, logger);
24752
24760
  if (contextIds.length === 0) {
24753
24761
  logger.info("No files classified, nothing to import");
24754
- return buildPipelineSuccessResult(result, "No files to import");
24762
+ return buildSuccessResult4(result, "No files to import");
24755
24763
  }
24756
24764
  let totalTransactions = 0;
24757
24765
  for (const contextId of contextIds) {
@@ -24772,7 +24780,7 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
24772
24780
  }
24773
24781
  logger.info(`Log file: ${logger.getLogPath()}`);
24774
24782
  logger.endSection();
24775
- return buildPipelineSuccessResult(result, `Successfully imported ${totalTransactions} transaction(s) from ${contextIds.length} file(s)`);
24783
+ return buildSuccessResult4(result, `Successfully imported ${totalTransactions} transaction(s) from ${contextIds.length} file(s)`);
24776
24784
  } catch (error45) {
24777
24785
  logger.error("Pipeline step failed", error45);
24778
24786
  logger.info(`Log file: ${logger.getLogPath()}`);
@@ -24782,7 +24790,7 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
24782
24790
  if (!result.error) {
24783
24791
  result.error = error45 instanceof Error ? error45.message : String(error45);
24784
24792
  }
24785
- return buildPipelineErrorResult(result, result.error, result.hint);
24793
+ return buildErrorResult5(result, result.error, result.hint);
24786
24794
  } finally {
24787
24795
  logger.endSection();
24788
24796
  await logger.flush();
@@ -24833,7 +24841,7 @@ This tool orchestrates the full import workflow:
24833
24841
  }
24834
24842
  });
24835
24843
  // src/tools/init-directories.ts
24836
- import * as fs15 from "fs";
24844
+ import * as fs14 from "fs";
24837
24845
  import * as path13 from "path";
24838
24846
  async function initDirectories(directory) {
24839
24847
  try {
@@ -24841,8 +24849,8 @@ async function initDirectories(directory) {
24841
24849
  const directoriesCreated = [];
24842
24850
  const gitkeepFiles = [];
24843
24851
  const importBase = path13.join(directory, "import");
24844
- if (!fs15.existsSync(importBase)) {
24845
- fs15.mkdirSync(importBase, { recursive: true });
24852
+ if (!fs14.existsSync(importBase)) {
24853
+ fs14.mkdirSync(importBase, { recursive: true });
24846
24854
  directoriesCreated.push("import");
24847
24855
  }
24848
24856
  const pathsToCreate = [
@@ -24853,19 +24861,19 @@ async function initDirectories(directory) {
24853
24861
  ];
24854
24862
  for (const { path: dirPath } of pathsToCreate) {
24855
24863
  const fullPath = path13.join(directory, dirPath);
24856
- if (!fs15.existsSync(fullPath)) {
24857
- fs15.mkdirSync(fullPath, { recursive: true });
24864
+ if (!fs14.existsSync(fullPath)) {
24865
+ fs14.mkdirSync(fullPath, { recursive: true });
24858
24866
  directoriesCreated.push(dirPath);
24859
24867
  }
24860
24868
  const gitkeepPath = path13.join(fullPath, ".gitkeep");
24861
- if (!fs15.existsSync(gitkeepPath)) {
24862
- fs15.writeFileSync(gitkeepPath, "");
24869
+ if (!fs14.existsSync(gitkeepPath)) {
24870
+ fs14.writeFileSync(gitkeepPath, "");
24863
24871
  gitkeepFiles.push(path13.join(dirPath, ".gitkeep"));
24864
24872
  }
24865
24873
  }
24866
24874
  const gitignorePath = path13.join(importBase, ".gitignore");
24867
24875
  let gitignoreCreated = false;
24868
- if (!fs15.existsSync(gitignorePath)) {
24876
+ if (!fs14.existsSync(gitignorePath)) {
24869
24877
  const gitignoreContent = `# Ignore CSV/PDF files in temporary directories
24870
24878
  /incoming/*.csv
24871
24879
  /incoming/*.pdf
@@ -24883,7 +24891,7 @@ async function initDirectories(directory) {
24883
24891
  .DS_Store
24884
24892
  Thumbs.db
24885
24893
  `;
24886
- fs15.writeFileSync(gitignorePath, gitignoreContent);
24894
+ fs14.writeFileSync(gitignorePath, gitignoreContent);
24887
24895
  gitignoreCreated = true;
24888
24896
  }
24889
24897
  const parts = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzzle/opencode-accountant",
3
- "version": "0.5.0-next.1",
3
+ "version": "0.5.0",
4
4
  "description": "An OpenCode accounting agent, specialized in double-entry-bookkepping with hledger",
5
5
  "author": {
6
6
  "name": "ali bengali",