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