@fuzzle/opencode-accountant 0.4.1-next.1 → 0.4.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/agent/accountant.md +1 -61
- package/dist/index.js +54 -344
- package/package.json +1 -1
package/agent/accountant.md
CHANGED
|
@@ -97,71 +97,11 @@ The `import-pipeline` tool provides an **atomic, safe workflow** using git workt
|
|
|
97
97
|
- Deletes processed CSV files from main repo's import/incoming
|
|
98
98
|
- Cleans up the worktree
|
|
99
99
|
4. **Handle Failures**: If any step fails (e.g., unknown postings found):
|
|
100
|
-
- Worktree is
|
|
101
|
-
- Main branch remains untouched
|
|
100
|
+
- Worktree is discarded, main branch remains untouched
|
|
102
101
|
- Review error output for unknown postings with full CSV row data
|
|
103
102
|
- Update rules file with `if` directives to match the transaction
|
|
104
103
|
- Re-run `import-pipeline`
|
|
105
104
|
|
|
106
|
-
### Error Recovery and Worktree Preservation
|
|
107
|
-
|
|
108
|
-
**Default Behavior:**
|
|
109
|
-
|
|
110
|
-
- On success: Worktrees are automatically cleaned up
|
|
111
|
-
- On error: Worktrees are preserved in `/tmp/import-worktree-<uuid>` for debugging
|
|
112
|
-
- Worktrees in `/tmp` are automatically cleaned up on system reboot
|
|
113
|
-
|
|
114
|
-
**Manual Recovery from Failed Import:**
|
|
115
|
-
|
|
116
|
-
If an import fails and the worktree is preserved, you can:
|
|
117
|
-
|
|
118
|
-
1. **Inspect the worktree:**
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
cd /tmp/import-worktree-<uuid>
|
|
122
|
-
hledger check # Validate journal
|
|
123
|
-
hledger balance # Check balances
|
|
124
|
-
cat ledger/2026.journal # View imported transactions
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
2. **Continue the import manually:**
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
cd /tmp/import-worktree-<uuid>
|
|
131
|
-
# Fix any issues (edit rules, fix transactions, etc.)
|
|
132
|
-
git add .
|
|
133
|
-
git commit -m "Fix import issues"
|
|
134
|
-
git checkout main
|
|
135
|
-
git merge --no-ff import-<uuid>
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
3. **Clean up when done:**
|
|
139
|
-
|
|
140
|
-
```bash
|
|
141
|
-
git worktree remove /tmp/import-worktree-<uuid>
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
4. **Or use the cleanup tool:**
|
|
145
|
-
```bash
|
|
146
|
-
cleanup-worktrees # Removes worktrees >24h old
|
|
147
|
-
cleanup-worktrees --all true # Removes all import worktrees
|
|
148
|
-
cleanup-worktrees --dryRun true # Preview without removing
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
**Logs:**
|
|
152
|
-
|
|
153
|
-
- Every import run generates a detailed log: `.memory/import-<timestamp>.md`
|
|
154
|
-
- Log includes all commands, output, timing, and errors
|
|
155
|
-
- Log path is included in import-pipeline output
|
|
156
|
-
- Review the log to understand what failed and why
|
|
157
|
-
|
|
158
|
-
**Force Cleanup on Error:**
|
|
159
|
-
If you prefer the old behavior (always cleanup, even on error):
|
|
160
|
-
|
|
161
|
-
```bash
|
|
162
|
-
import-pipeline --keepWorktreeOnError false
|
|
163
|
-
```
|
|
164
|
-
|
|
165
105
|
### Rules Files
|
|
166
106
|
|
|
167
107
|
- The location of the rules files is configured in `config/import/providers.yaml`
|
package/dist/index.js
CHANGED
|
@@ -17509,8 +17509,7 @@ function validatePaths(paths) {
|
|
|
17509
17509
|
pending: pathsObj.pending,
|
|
17510
17510
|
done: pathsObj.done,
|
|
17511
17511
|
unrecognized: pathsObj.unrecognized,
|
|
17512
|
-
rules: pathsObj.rules
|
|
17513
|
-
logs: pathsObj.logs
|
|
17512
|
+
rules: pathsObj.rules
|
|
17514
17513
|
};
|
|
17515
17514
|
}
|
|
17516
17515
|
function validateDetectionRule(providerName, index, rule) {
|
|
@@ -17640,9 +17639,6 @@ function loadImportConfig(directory) {
|
|
|
17640
17639
|
for (const [name, config2] of Object.entries(providersObj)) {
|
|
17641
17640
|
providers[name] = validateProviderConfig(name, config2);
|
|
17642
17641
|
}
|
|
17643
|
-
if (!paths.logs) {
|
|
17644
|
-
paths.logs = ".memory";
|
|
17645
|
-
}
|
|
17646
17642
|
return { paths, providers };
|
|
17647
17643
|
}
|
|
17648
17644
|
|
|
@@ -17911,35 +17907,15 @@ function isInWorktree(directory) {
|
|
|
17911
17907
|
return false;
|
|
17912
17908
|
}
|
|
17913
17909
|
}
|
|
17914
|
-
async function withWorktree(directory, operation
|
|
17910
|
+
async function withWorktree(directory, operation) {
|
|
17915
17911
|
let createdWorktree = null;
|
|
17916
|
-
let operationSucceeded = false;
|
|
17917
|
-
const logger = options?.logger;
|
|
17918
|
-
const keepOnError = options?.keepOnError ?? true;
|
|
17919
17912
|
try {
|
|
17920
|
-
logger?.logStep("Create Worktree", "start");
|
|
17921
17913
|
createdWorktree = createImportWorktree(directory);
|
|
17922
|
-
logger?.logStep("Create Worktree", "success", `Path: ${createdWorktree.path}`);
|
|
17923
|
-
logger?.info(`Branch: ${createdWorktree.branch}`);
|
|
17924
|
-
logger?.info(`UUID: ${createdWorktree.uuid}`);
|
|
17925
17914
|
const result = await operation(createdWorktree);
|
|
17926
|
-
operationSucceeded = true;
|
|
17927
17915
|
return result;
|
|
17928
17916
|
} finally {
|
|
17929
17917
|
if (createdWorktree) {
|
|
17930
|
-
|
|
17931
|
-
logger?.logStep("Cleanup Worktree", "start");
|
|
17932
|
-
removeWorktree(createdWorktree, true);
|
|
17933
|
-
logger?.logStep("Cleanup Worktree", "success", "Worktree removed");
|
|
17934
|
-
} else if (!keepOnError) {
|
|
17935
|
-
logger?.warn("Operation failed, but keepOnError=false, removing worktree");
|
|
17936
|
-
removeWorktree(createdWorktree, true);
|
|
17937
|
-
} else {
|
|
17938
|
-
logger?.warn("Operation failed, worktree preserved for debugging");
|
|
17939
|
-
logger?.info(`Worktree path: ${createdWorktree.path}`);
|
|
17940
|
-
logger?.info(`To clean up manually: git worktree remove ${createdWorktree.path}`);
|
|
17941
|
-
logger?.info(`To list all worktrees: git worktree list`);
|
|
17942
|
-
}
|
|
17918
|
+
removeWorktree(createdWorktree, true);
|
|
17943
17919
|
}
|
|
17944
17920
|
}
|
|
17945
17921
|
}
|
|
@@ -24052,7 +24028,7 @@ async function processCsvFile(csvFile, rulesMapping, directory, hledgerExecutor)
|
|
|
24052
24028
|
transactionYear
|
|
24053
24029
|
};
|
|
24054
24030
|
}
|
|
24055
|
-
async function importStatements(directory, agent, options, configLoader = loadImportConfig, hledgerExecutor = defaultHledgerExecutor, worktreeChecker = isInWorktree
|
|
24031
|
+
async function importStatements(directory, agent, options, configLoader = loadImportConfig, hledgerExecutor = defaultHledgerExecutor, worktreeChecker = isInWorktree) {
|
|
24056
24032
|
const restrictionError = checkAccountantAgent(agent, "import statements");
|
|
24057
24033
|
if (restrictionError) {
|
|
24058
24034
|
return restrictionError;
|
|
@@ -24553,8 +24529,8 @@ It must be run inside an import worktree (use import-pipeline for the full workf
|
|
|
24553
24529
|
}
|
|
24554
24530
|
});
|
|
24555
24531
|
// src/tools/import-pipeline.ts
|
|
24556
|
-
import * as
|
|
24557
|
-
import * as
|
|
24532
|
+
import * as fs13 from "fs";
|
|
24533
|
+
import * as path12 from "path";
|
|
24558
24534
|
|
|
24559
24535
|
// src/utils/accountDeclarations.ts
|
|
24560
24536
|
import * as fs12 from "fs";
|
|
@@ -24671,158 +24647,6 @@ function ensureAccountDeclarations(yearJournalPath, accounts) {
|
|
|
24671
24647
|
};
|
|
24672
24648
|
}
|
|
24673
24649
|
|
|
24674
|
-
// src/utils/logger.ts
|
|
24675
|
-
import fs13 from "fs/promises";
|
|
24676
|
-
import path12 from "path";
|
|
24677
|
-
|
|
24678
|
-
class MarkdownLogger {
|
|
24679
|
-
buffer = [];
|
|
24680
|
-
logPath;
|
|
24681
|
-
context = {};
|
|
24682
|
-
autoFlush;
|
|
24683
|
-
sectionDepth = 0;
|
|
24684
|
-
constructor(config2) {
|
|
24685
|
-
this.autoFlush = config2.autoFlush ?? true;
|
|
24686
|
-
this.context = config2.context || {};
|
|
24687
|
-
const filename = config2.filename || `import-${this.getTimestamp()}.md`;
|
|
24688
|
-
this.logPath = path12.join(config2.logDir, filename);
|
|
24689
|
-
this.buffer.push(`# Import Pipeline Log`);
|
|
24690
|
-
this.buffer.push(`**Started**: ${new Date().toLocaleString()}`);
|
|
24691
|
-
this.buffer.push("");
|
|
24692
|
-
}
|
|
24693
|
-
startSection(title, level = 2) {
|
|
24694
|
-
this.buffer.push("");
|
|
24695
|
-
this.buffer.push(`${"#".repeat(level + 1)} ${title}`);
|
|
24696
|
-
this.buffer.push(`**Started**: ${this.getTime()}`);
|
|
24697
|
-
this.buffer.push("");
|
|
24698
|
-
this.sectionDepth++;
|
|
24699
|
-
}
|
|
24700
|
-
endSection() {
|
|
24701
|
-
if (this.sectionDepth > 0) {
|
|
24702
|
-
this.buffer.push("");
|
|
24703
|
-
this.buffer.push("---");
|
|
24704
|
-
this.buffer.push("");
|
|
24705
|
-
this.sectionDepth--;
|
|
24706
|
-
}
|
|
24707
|
-
}
|
|
24708
|
-
info(message) {
|
|
24709
|
-
this.buffer.push(message);
|
|
24710
|
-
if (this.autoFlush)
|
|
24711
|
-
this.flushAsync();
|
|
24712
|
-
}
|
|
24713
|
-
warn(message) {
|
|
24714
|
-
this.buffer.push(`\u26A0\uFE0F **WARNING**: ${message}`);
|
|
24715
|
-
if (this.autoFlush)
|
|
24716
|
-
this.flushAsync();
|
|
24717
|
-
}
|
|
24718
|
-
error(message, error45) {
|
|
24719
|
-
this.buffer.push(`\u274C **ERROR**: ${message}`);
|
|
24720
|
-
if (error45) {
|
|
24721
|
-
const errorStr = error45 instanceof Error ? error45.message : String(error45);
|
|
24722
|
-
this.buffer.push("");
|
|
24723
|
-
this.buffer.push("```");
|
|
24724
|
-
this.buffer.push(errorStr);
|
|
24725
|
-
if (error45 instanceof Error && error45.stack) {
|
|
24726
|
-
this.buffer.push("");
|
|
24727
|
-
this.buffer.push(error45.stack);
|
|
24728
|
-
}
|
|
24729
|
-
this.buffer.push("```");
|
|
24730
|
-
this.buffer.push("");
|
|
24731
|
-
}
|
|
24732
|
-
if (this.autoFlush)
|
|
24733
|
-
this.flushAsync();
|
|
24734
|
-
}
|
|
24735
|
-
debug(message) {
|
|
24736
|
-
this.buffer.push(`\uD83D\uDD0D ${message}`);
|
|
24737
|
-
if (this.autoFlush)
|
|
24738
|
-
this.flushAsync();
|
|
24739
|
-
}
|
|
24740
|
-
logStep(stepName, status, details) {
|
|
24741
|
-
const icon = status === "success" ? "\u2705" : status === "error" ? "\u274C" : "\u25B6\uFE0F";
|
|
24742
|
-
const statusText = status.charAt(0).toUpperCase() + status.slice(1);
|
|
24743
|
-
this.buffer.push(`**${stepName}**: ${icon} ${statusText}`);
|
|
24744
|
-
if (details) {
|
|
24745
|
-
this.buffer.push(` ${details}`);
|
|
24746
|
-
}
|
|
24747
|
-
this.buffer.push("");
|
|
24748
|
-
if (this.autoFlush)
|
|
24749
|
-
this.flushAsync();
|
|
24750
|
-
}
|
|
24751
|
-
logCommand(command, output) {
|
|
24752
|
-
this.buffer.push("```bash");
|
|
24753
|
-
this.buffer.push(`$ ${command}`);
|
|
24754
|
-
if (output) {
|
|
24755
|
-
this.buffer.push("");
|
|
24756
|
-
const lines = output.trim().split(`
|
|
24757
|
-
`);
|
|
24758
|
-
if (lines.length > 50) {
|
|
24759
|
-
this.buffer.push(...lines.slice(0, 50));
|
|
24760
|
-
this.buffer.push(`... (${lines.length - 50} more lines omitted)`);
|
|
24761
|
-
} else {
|
|
24762
|
-
this.buffer.push(output.trim());
|
|
24763
|
-
}
|
|
24764
|
-
}
|
|
24765
|
-
this.buffer.push("```");
|
|
24766
|
-
this.buffer.push("");
|
|
24767
|
-
if (this.autoFlush)
|
|
24768
|
-
this.flushAsync();
|
|
24769
|
-
}
|
|
24770
|
-
logResult(data) {
|
|
24771
|
-
this.buffer.push("```json");
|
|
24772
|
-
this.buffer.push(JSON.stringify(data, null, 2));
|
|
24773
|
-
this.buffer.push("```");
|
|
24774
|
-
this.buffer.push("");
|
|
24775
|
-
if (this.autoFlush)
|
|
24776
|
-
this.flushAsync();
|
|
24777
|
-
}
|
|
24778
|
-
setContext(key, value) {
|
|
24779
|
-
this.context[key] = value;
|
|
24780
|
-
}
|
|
24781
|
-
async flush() {
|
|
24782
|
-
if (this.buffer.length === 0)
|
|
24783
|
-
return;
|
|
24784
|
-
try {
|
|
24785
|
-
await fs13.mkdir(path12.dirname(this.logPath), { recursive: true });
|
|
24786
|
-
await fs13.writeFile(this.logPath, this.buffer.join(`
|
|
24787
|
-
`), "utf-8");
|
|
24788
|
-
} catch {}
|
|
24789
|
-
}
|
|
24790
|
-
getLogPath() {
|
|
24791
|
-
return this.logPath;
|
|
24792
|
-
}
|
|
24793
|
-
flushAsync() {
|
|
24794
|
-
this.flush().catch(() => {});
|
|
24795
|
-
}
|
|
24796
|
-
getTimestamp() {
|
|
24797
|
-
return new Date().toISOString().replace(/:/g, "-").split(".")[0];
|
|
24798
|
-
}
|
|
24799
|
-
getTime() {
|
|
24800
|
-
return new Date().toLocaleTimeString();
|
|
24801
|
-
}
|
|
24802
|
-
}
|
|
24803
|
-
function createLogger(config2) {
|
|
24804
|
-
return new MarkdownLogger(config2);
|
|
24805
|
-
}
|
|
24806
|
-
function createImportLogger(directory, worktreeId, provider) {
|
|
24807
|
-
const context = {};
|
|
24808
|
-
if (worktreeId)
|
|
24809
|
-
context.worktreeId = worktreeId;
|
|
24810
|
-
if (provider)
|
|
24811
|
-
context.provider = provider;
|
|
24812
|
-
const logger = createLogger({
|
|
24813
|
-
logDir: path12.join(directory, ".memory"),
|
|
24814
|
-
autoFlush: true,
|
|
24815
|
-
context
|
|
24816
|
-
});
|
|
24817
|
-
if (worktreeId)
|
|
24818
|
-
logger.info(`**Worktree ID**: ${worktreeId}`);
|
|
24819
|
-
if (provider)
|
|
24820
|
-
logger.info(`**Provider**: ${provider}`);
|
|
24821
|
-
logger.info(`**Repository**: ${directory}`);
|
|
24822
|
-
logger.info("");
|
|
24823
|
-
return logger;
|
|
24824
|
-
}
|
|
24825
|
-
|
|
24826
24650
|
// src/tools/import-pipeline.ts
|
|
24827
24651
|
class NoTransactionsError extends Error {
|
|
24828
24652
|
constructor() {
|
|
@@ -24862,8 +24686,8 @@ function buildCommitMessage(provider, currency, fromDate, untilDate, transaction
|
|
|
24862
24686
|
return `${parts.join(" ")}${dateRange}${txStr}`;
|
|
24863
24687
|
}
|
|
24864
24688
|
function cleanupIncomingFiles(worktree, context) {
|
|
24865
|
-
const incomingDir =
|
|
24866
|
-
if (!
|
|
24689
|
+
const incomingDir = path12.join(worktree.mainRepoPath, "import/incoming");
|
|
24690
|
+
if (!fs13.existsSync(incomingDir)) {
|
|
24867
24691
|
return;
|
|
24868
24692
|
}
|
|
24869
24693
|
const importStep = context.result.steps.import;
|
|
@@ -24878,11 +24702,11 @@ function cleanupIncomingFiles(worktree, context) {
|
|
|
24878
24702
|
for (const fileResult of importResult.files) {
|
|
24879
24703
|
if (!fileResult.csv)
|
|
24880
24704
|
continue;
|
|
24881
|
-
const filename =
|
|
24882
|
-
const filePath =
|
|
24883
|
-
if (
|
|
24705
|
+
const filename = path12.basename(fileResult.csv);
|
|
24706
|
+
const filePath = path12.join(incomingDir, filename);
|
|
24707
|
+
if (fs13.existsSync(filePath)) {
|
|
24884
24708
|
try {
|
|
24885
|
-
|
|
24709
|
+
fs13.unlinkSync(filePath);
|
|
24886
24710
|
deletedCount++;
|
|
24887
24711
|
} catch (error45) {
|
|
24888
24712
|
console.error(`[ERROR] Failed to delete ${filename}: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
@@ -24893,13 +24717,9 @@ function cleanupIncomingFiles(worktree, context) {
|
|
|
24893
24717
|
console.log(`[INFO] Cleaned up ${deletedCount} file(s) from import/incoming/`);
|
|
24894
24718
|
}
|
|
24895
24719
|
}
|
|
24896
|
-
async function executeClassifyStep(context, worktree
|
|
24897
|
-
logger?.startSection("Step 2: Classify Transactions");
|
|
24898
|
-
logger?.logStep("Classify", "start");
|
|
24720
|
+
async function executeClassifyStep(context, worktree) {
|
|
24899
24721
|
if (context.options.skipClassify) {
|
|
24900
|
-
logger?.info("Classification skipped (skipClassify: true)");
|
|
24901
24722
|
context.result.steps.classify = buildStepResult(true, "Classification skipped (skipClassify: true)");
|
|
24902
|
-
logger?.endSection();
|
|
24903
24723
|
return;
|
|
24904
24724
|
}
|
|
24905
24725
|
const inWorktree = () => true;
|
|
@@ -24909,23 +24729,18 @@ async function executeClassifyStep(context, worktree, logger) {
|
|
|
24909
24729
|
let message = success2 ? "Classification complete" : "Classification had issues";
|
|
24910
24730
|
if (classifyParsed.unrecognized?.length > 0) {
|
|
24911
24731
|
message = `Classification complete with ${classifyParsed.unrecognized.length} unrecognized file(s)`;
|
|
24912
|
-
logger?.warn(`${classifyParsed.unrecognized.length} unrecognized file(s)`);
|
|
24913
24732
|
}
|
|
24914
|
-
logger?.logStep("Classify", success2 ? "success" : "error", message);
|
|
24915
24733
|
const details = {
|
|
24916
24734
|
success: success2,
|
|
24917
24735
|
unrecognized: classifyParsed.unrecognized,
|
|
24918
24736
|
classified: classifyParsed
|
|
24919
24737
|
};
|
|
24920
24738
|
context.result.steps.classify = buildStepResult(success2, message, details);
|
|
24921
|
-
logger?.endSection();
|
|
24922
24739
|
}
|
|
24923
|
-
async function executeAccountDeclarationsStep(context, worktree
|
|
24924
|
-
logger?.startSection("Step 3: Check Account Declarations");
|
|
24925
|
-
logger?.logStep("Check Accounts", "start");
|
|
24740
|
+
async function executeAccountDeclarationsStep(context, worktree) {
|
|
24926
24741
|
const config2 = context.configLoader(worktree.path);
|
|
24927
|
-
const pendingDir =
|
|
24928
|
-
const rulesDir =
|
|
24742
|
+
const pendingDir = path12.join(worktree.path, config2.paths.pending);
|
|
24743
|
+
const rulesDir = path12.join(worktree.path, config2.paths.rules);
|
|
24929
24744
|
const csvFiles = findCsvFiles(pendingDir, context.options.provider, context.options.currency);
|
|
24930
24745
|
if (csvFiles.length === 0) {
|
|
24931
24746
|
context.result.steps.accountDeclarations = buildStepResult(true, "No CSV files to process", {
|
|
@@ -24956,7 +24771,7 @@ async function executeAccountDeclarationsStep(context, worktree, logger) {
|
|
|
24956
24771
|
context.result.steps.accountDeclarations = buildStepResult(true, "No accounts found in rules files", {
|
|
24957
24772
|
accountsAdded: [],
|
|
24958
24773
|
journalUpdated: "",
|
|
24959
|
-
rulesScanned: Array.from(matchedRulesFiles).map((f) =>
|
|
24774
|
+
rulesScanned: Array.from(matchedRulesFiles).map((f) => path12.relative(worktree.path, f))
|
|
24960
24775
|
});
|
|
24961
24776
|
return;
|
|
24962
24777
|
}
|
|
@@ -24979,7 +24794,7 @@ async function executeAccountDeclarationsStep(context, worktree, logger) {
|
|
|
24979
24794
|
context.result.steps.accountDeclarations = buildStepResult(false, "Could not determine transaction year from CSV files", {
|
|
24980
24795
|
accountsAdded: [],
|
|
24981
24796
|
journalUpdated: "",
|
|
24982
|
-
rulesScanned: Array.from(matchedRulesFiles).map((f) =>
|
|
24797
|
+
rulesScanned: Array.from(matchedRulesFiles).map((f) => path12.relative(worktree.path, f))
|
|
24983
24798
|
});
|
|
24984
24799
|
return;
|
|
24985
24800
|
}
|
|
@@ -24990,28 +24805,19 @@ async function executeAccountDeclarationsStep(context, worktree, logger) {
|
|
|
24990
24805
|
context.result.steps.accountDeclarations = buildStepResult(false, `Failed to create year journal: ${error45 instanceof Error ? error45.message : String(error45)}`, {
|
|
24991
24806
|
accountsAdded: [],
|
|
24992
24807
|
journalUpdated: "",
|
|
24993
|
-
rulesScanned: Array.from(matchedRulesFiles).map((f) =>
|
|
24808
|
+
rulesScanned: Array.from(matchedRulesFiles).map((f) => path12.relative(worktree.path, f))
|
|
24994
24809
|
});
|
|
24995
24810
|
return;
|
|
24996
24811
|
}
|
|
24997
24812
|
const result = ensureAccountDeclarations(yearJournalPath, allAccounts);
|
|
24998
|
-
const message = result.added.length > 0 ? `Added ${result.added.length} account declaration(s) to ${
|
|
24999
|
-
logger?.logStep("Check Accounts", "success", message);
|
|
25000
|
-
if (result.added.length > 0) {
|
|
25001
|
-
for (const account of result.added) {
|
|
25002
|
-
logger?.info(` - ${account}`);
|
|
25003
|
-
}
|
|
25004
|
-
}
|
|
24813
|
+
const message = result.added.length > 0 ? `Added ${result.added.length} account declaration(s) to ${path12.relative(worktree.path, yearJournalPath)}` : "All required accounts already declared";
|
|
25005
24814
|
context.result.steps.accountDeclarations = buildStepResult(true, message, {
|
|
25006
24815
|
accountsAdded: result.added,
|
|
25007
|
-
journalUpdated:
|
|
25008
|
-
rulesScanned: Array.from(matchedRulesFiles).map((f) =>
|
|
24816
|
+
journalUpdated: path12.relative(worktree.path, yearJournalPath),
|
|
24817
|
+
rulesScanned: Array.from(matchedRulesFiles).map((f) => path12.relative(worktree.path, f))
|
|
25009
24818
|
});
|
|
25010
|
-
logger?.endSection();
|
|
25011
24819
|
}
|
|
25012
|
-
async function executeDryRunStep(context, worktree
|
|
25013
|
-
logger?.startSection("Step 4: Dry Run Import");
|
|
25014
|
-
logger?.logStep("Dry Run", "start");
|
|
24820
|
+
async function executeDryRunStep(context, worktree) {
|
|
25015
24821
|
const inWorktree = () => true;
|
|
25016
24822
|
const dryRunResult = await importStatements(worktree.path, context.agent, {
|
|
25017
24823
|
provider: context.options.provider,
|
|
@@ -25020,30 +24826,20 @@ async function executeDryRunStep(context, worktree, logger) {
|
|
|
25020
24826
|
}, context.configLoader, context.hledgerExecutor, inWorktree);
|
|
25021
24827
|
const dryRunParsed = JSON.parse(dryRunResult);
|
|
25022
24828
|
const message = dryRunParsed.success ? `Dry run passed: ${dryRunParsed.summary?.totalTransactions || 0} transactions ready` : `Dry run failed: ${dryRunParsed.summary?.unknown || 0} unknown account(s)`;
|
|
25023
|
-
logger?.logStep("Dry Run", dryRunParsed.success ? "success" : "error", message);
|
|
25024
|
-
if (dryRunParsed.summary?.totalTransactions) {
|
|
25025
|
-
logger?.info(`Found ${dryRunParsed.summary.totalTransactions} transactions`);
|
|
25026
|
-
}
|
|
25027
24829
|
context.result.steps.dryRun = buildStepResult(dryRunParsed.success, message, {
|
|
25028
24830
|
success: dryRunParsed.success,
|
|
25029
24831
|
summary: dryRunParsed.summary
|
|
25030
24832
|
});
|
|
25031
24833
|
if (!dryRunParsed.success) {
|
|
25032
|
-
logger?.error("Dry run found unknown accounts or errors");
|
|
25033
|
-
logger?.endSection();
|
|
25034
24834
|
context.result.error = "Dry run found unknown accounts or errors";
|
|
25035
24835
|
context.result.hint = "Add rules to categorize unknown transactions, then retry";
|
|
25036
24836
|
throw new Error("Dry run failed");
|
|
25037
24837
|
}
|
|
25038
24838
|
if (dryRunParsed.summary?.totalTransactions === 0) {
|
|
25039
|
-
logger?.endSection();
|
|
25040
24839
|
throw new NoTransactionsError;
|
|
25041
24840
|
}
|
|
25042
|
-
logger?.endSection();
|
|
25043
24841
|
}
|
|
25044
|
-
async function executeImportStep(context, worktree
|
|
25045
|
-
logger?.startSection("Step 5: Import Transactions");
|
|
25046
|
-
logger?.logStep("Import", "start");
|
|
24842
|
+
async function executeImportStep(context, worktree) {
|
|
25047
24843
|
const inWorktree = () => true;
|
|
25048
24844
|
const importResult = await importStatements(worktree.path, context.agent, {
|
|
25049
24845
|
provider: context.options.provider,
|
|
@@ -25052,23 +24848,17 @@ async function executeImportStep(context, worktree, logger) {
|
|
|
25052
24848
|
}, context.configLoader, context.hledgerExecutor, inWorktree);
|
|
25053
24849
|
const importParsed = JSON.parse(importResult);
|
|
25054
24850
|
const message = importParsed.success ? `Imported ${importParsed.summary?.totalTransactions || 0} transactions` : `Import failed: ${importParsed.error || "Unknown error"}`;
|
|
25055
|
-
logger?.logStep("Import", importParsed.success ? "success" : "error", message);
|
|
25056
24851
|
context.result.steps.import = buildStepResult(importParsed.success, message, {
|
|
25057
24852
|
success: importParsed.success,
|
|
25058
24853
|
summary: importParsed.summary,
|
|
25059
24854
|
error: importParsed.error
|
|
25060
24855
|
});
|
|
25061
24856
|
if (!importParsed.success) {
|
|
25062
|
-
logger?.error("Import failed", new Error(importParsed.error || "Unknown error"));
|
|
25063
|
-
logger?.endSection();
|
|
25064
24857
|
context.result.error = `Import failed: ${importParsed.error || "Unknown error"}`;
|
|
25065
24858
|
throw new Error("Import failed");
|
|
25066
24859
|
}
|
|
25067
|
-
logger?.endSection();
|
|
25068
24860
|
}
|
|
25069
|
-
async function executeReconcileStep(context, worktree
|
|
25070
|
-
logger?.startSection("Step 6: Reconcile Balance");
|
|
25071
|
-
logger?.logStep("Reconcile", "start");
|
|
24861
|
+
async function executeReconcileStep(context, worktree) {
|
|
25072
24862
|
const inWorktree = () => true;
|
|
25073
24863
|
const reconcileResult = await reconcileStatement(worktree.path, context.agent, {
|
|
25074
24864
|
provider: context.options.provider,
|
|
@@ -25078,11 +24868,6 @@ async function executeReconcileStep(context, worktree, logger) {
|
|
|
25078
24868
|
}, context.configLoader, context.hledgerExecutor, inWorktree);
|
|
25079
24869
|
const reconcileParsed = JSON.parse(reconcileResult);
|
|
25080
24870
|
const message = reconcileParsed.success ? `Balance reconciled: ${reconcileParsed.actualBalance}` : `Balance mismatch: expected ${reconcileParsed.expectedBalance}, got ${reconcileParsed.actualBalance}`;
|
|
25081
|
-
logger?.logStep("Reconcile", reconcileParsed.success ? "success" : "error", message);
|
|
25082
|
-
if (reconcileParsed.success) {
|
|
25083
|
-
logger?.info(`Actual: ${reconcileParsed.actualBalance}`);
|
|
25084
|
-
logger?.info(`Expected: ${reconcileParsed.expectedBalance}`);
|
|
25085
|
-
}
|
|
25086
24871
|
context.result.steps.reconcile = buildStepResult(reconcileParsed.success, message, {
|
|
25087
24872
|
success: reconcileParsed.success,
|
|
25088
24873
|
actualBalance: reconcileParsed.actualBalance,
|
|
@@ -25091,17 +24876,12 @@ async function executeReconcileStep(context, worktree, logger) {
|
|
|
25091
24876
|
error: reconcileParsed.error
|
|
25092
24877
|
});
|
|
25093
24878
|
if (!reconcileParsed.success) {
|
|
25094
|
-
logger?.error("Reconciliation failed", new Error(reconcileParsed.error || "Balance mismatch"));
|
|
25095
|
-
logger?.endSection();
|
|
25096
24879
|
context.result.error = `Reconciliation failed: ${reconcileParsed.error || "Balance mismatch"}`;
|
|
25097
24880
|
context.result.hint = "Check for missing transactions or incorrect rules";
|
|
25098
24881
|
throw new Error("Reconciliation failed");
|
|
25099
24882
|
}
|
|
25100
|
-
logger?.endSection();
|
|
25101
24883
|
}
|
|
25102
|
-
async function executeMergeStep(context, worktree
|
|
25103
|
-
logger?.startSection("Step 7: Merge to Main");
|
|
25104
|
-
logger?.logStep("Merge", "start");
|
|
24884
|
+
async function executeMergeStep(context, worktree) {
|
|
25105
24885
|
const importDetails = context.result.steps.import?.details;
|
|
25106
24886
|
const reconcileDetails = context.result.steps.reconcile?.details;
|
|
25107
24887
|
if (!importDetails || !reconcileDetails) {
|
|
@@ -25114,17 +24894,11 @@ async function executeMergeStep(context, worktree, logger) {
|
|
|
25114
24894
|
const transactionCount = importDetails.summary?.totalTransactions || 0;
|
|
25115
24895
|
const commitMessage = buildCommitMessage(context.options.provider, context.options.currency, commitInfo.fromDate, commitInfo.untilDate, transactionCount);
|
|
25116
24896
|
try {
|
|
25117
|
-
logger?.info(`Commit message: "${commitMessage}"`);
|
|
25118
24897
|
mergeWorktree(worktree, commitMessage);
|
|
25119
|
-
logger?.logStep("Merge", "success", "Merged to main branch");
|
|
25120
24898
|
const mergeDetails = { commitMessage };
|
|
25121
24899
|
context.result.steps.merge = buildStepResult(true, `Merged to main: "${commitMessage}"`, mergeDetails);
|
|
25122
24900
|
cleanupIncomingFiles(worktree, context);
|
|
25123
|
-
logger?.endSection();
|
|
25124
24901
|
} catch (error45) {
|
|
25125
|
-
logger?.logStep("Merge", "error");
|
|
25126
|
-
logger?.error("Merge to main branch failed", error45);
|
|
25127
|
-
logger?.endSection();
|
|
25128
24902
|
const message = `Merge failed: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
25129
24903
|
context.result.steps.merge = buildStepResult(false, message);
|
|
25130
24904
|
context.result.error = "Merge to main branch failed";
|
|
@@ -25142,13 +24916,6 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
|
|
|
25142
24916
|
if (restrictionError) {
|
|
25143
24917
|
return restrictionError;
|
|
25144
24918
|
}
|
|
25145
|
-
const logger = createImportLogger(directory, undefined, options.provider);
|
|
25146
|
-
logger.startSection("Import Pipeline", 1);
|
|
25147
|
-
logger.info(`Provider filter: ${options.provider || "all"}`);
|
|
25148
|
-
logger.info(`Currency filter: ${options.currency || "all"}`);
|
|
25149
|
-
logger.info(`Skip classify: ${options.skipClassify || false}`);
|
|
25150
|
-
logger.info(`Keep worktree on error: ${options.keepWorktreeOnError ?? true}`);
|
|
25151
|
-
logger.info("");
|
|
25152
24919
|
const result = {
|
|
25153
24920
|
success: false,
|
|
25154
24921
|
steps: {}
|
|
@@ -25163,47 +24930,33 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
|
|
|
25163
24930
|
};
|
|
25164
24931
|
try {
|
|
25165
24932
|
return await withWorktree(directory, async (worktree) => {
|
|
25166
|
-
logger.setContext("worktreeId", worktree.uuid);
|
|
25167
|
-
logger.setContext("worktreePath", worktree.path);
|
|
25168
24933
|
result.worktreeId = worktree.uuid;
|
|
25169
24934
|
result.steps.worktree = buildStepResult(true, `Created worktree at ${worktree.path}`, {
|
|
25170
24935
|
path: worktree.path,
|
|
25171
24936
|
branch: worktree.branch
|
|
25172
24937
|
});
|
|
25173
|
-
logger.startSection("Step 1: Sync Files");
|
|
25174
|
-
logger.logStep("Sync Files", "start");
|
|
25175
24938
|
try {
|
|
25176
24939
|
const config2 = configLoader(directory);
|
|
25177
24940
|
const syncResult = syncCSVFilesToWorktree(directory, worktree.path, config2.paths.import);
|
|
25178
24941
|
if (syncResult.synced.length === 0 && syncResult.errors.length === 0) {
|
|
25179
|
-
logger.logStep("Sync Files", "success", "No CSV files to sync");
|
|
25180
24942
|
result.steps.sync = buildStepResult(true, "No CSV files to sync", {
|
|
25181
24943
|
synced: []
|
|
25182
24944
|
});
|
|
25183
24945
|
} else if (syncResult.errors.length > 0) {
|
|
25184
|
-
logger.warn(`Synced ${syncResult.synced.length} file(s) with ${syncResult.errors.length} error(s)`);
|
|
25185
24946
|
result.steps.sync = buildStepResult(true, `Synced ${syncResult.synced.length} file(s) with ${syncResult.errors.length} error(s)`, { synced: syncResult.synced, errors: syncResult.errors });
|
|
25186
24947
|
} else {
|
|
25187
|
-
logger.logStep("Sync Files", "success", `Synced ${syncResult.synced.length} CSV file(s)`);
|
|
25188
|
-
for (const file2 of syncResult.synced) {
|
|
25189
|
-
logger.info(` - ${file2}`);
|
|
25190
|
-
}
|
|
25191
24948
|
result.steps.sync = buildStepResult(true, `Synced ${syncResult.synced.length} CSV file(s) to worktree`, { synced: syncResult.synced });
|
|
25192
24949
|
}
|
|
25193
|
-
logger.endSection();
|
|
25194
24950
|
} catch (error45) {
|
|
25195
|
-
logger.logStep("Sync Files", "error");
|
|
25196
|
-
logger.error("Failed to sync CSV files", error45);
|
|
25197
|
-
logger.endSection();
|
|
25198
24951
|
const errorMsg = error45 instanceof Error ? error45.message : String(error45);
|
|
25199
24952
|
result.steps.sync = buildStepResult(false, `Failed to sync CSV files: ${errorMsg}`, { synced: [], errors: [{ file: "unknown", error: errorMsg }] });
|
|
25200
24953
|
}
|
|
25201
24954
|
try {
|
|
25202
|
-
await executeClassifyStep(context, worktree
|
|
25203
|
-
await executeAccountDeclarationsStep(context, worktree
|
|
25204
|
-
await executeDryRunStep(context, worktree
|
|
25205
|
-
await executeImportStep(context, worktree
|
|
25206
|
-
await executeReconcileStep(context, worktree
|
|
24955
|
+
await executeClassifyStep(context, worktree);
|
|
24956
|
+
await executeAccountDeclarationsStep(context, worktree);
|
|
24957
|
+
await executeDryRunStep(context, worktree);
|
|
24958
|
+
await executeImportStep(context, worktree);
|
|
24959
|
+
await executeReconcileStep(context, worktree);
|
|
25207
24960
|
try {
|
|
25208
24961
|
const config2 = configLoader(directory);
|
|
25209
24962
|
const cleanupResult = cleanupProcessedCSVFiles(directory, config2.paths.import);
|
|
@@ -25228,7 +24981,7 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
|
|
|
25228
24981
|
}
|
|
25229
24982
|
});
|
|
25230
24983
|
}
|
|
25231
|
-
await executeMergeStep(context, worktree
|
|
24984
|
+
await executeMergeStep(context, worktree);
|
|
25232
24985
|
const existingCleanup = result.steps.cleanup;
|
|
25233
24986
|
if (existingCleanup) {
|
|
25234
24987
|
existingCleanup.message += ", worktree cleaned up";
|
|
@@ -25238,30 +24991,10 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
|
|
|
25238
24991
|
};
|
|
25239
24992
|
}
|
|
25240
24993
|
const transactionCount = context.result.steps.import?.details?.summary?.totalTransactions || 0;
|
|
25241
|
-
logger.startSection("Summary");
|
|
25242
|
-
logger.info(`\u2705 Import completed successfully`);
|
|
25243
|
-
logger.info(`Total transactions imported: ${transactionCount}`);
|
|
25244
|
-
if (context.result.steps.reconcile?.details?.actualBalance) {
|
|
25245
|
-
logger.info(`Balance reconciliation: \u2705 Matched (${context.result.steps.reconcile.details.actualBalance})`);
|
|
25246
|
-
}
|
|
25247
|
-
logger.info(`Log file: ${logger.getLogPath()}`);
|
|
25248
|
-
logger.endSection();
|
|
25249
24994
|
return buildSuccessResult4(result, `Successfully imported ${transactionCount} transaction(s)`);
|
|
25250
24995
|
} catch (error45) {
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
logger.error("Pipeline step failed", error45);
|
|
25254
|
-
if (keepWorktree && worktreePath) {
|
|
25255
|
-
logger.warn(`Worktree preserved at: ${worktreePath}`);
|
|
25256
|
-
logger.info(`To continue manually: cd ${worktreePath}`);
|
|
25257
|
-
logger.info(`To clean up: git worktree remove ${worktreePath}`);
|
|
25258
|
-
}
|
|
25259
|
-
logger.info(`Log file: ${logger.getLogPath()}`);
|
|
25260
|
-
result.steps.cleanup = buildStepResult(true, keepWorktree ? `Worktree preserved for debugging (CSV files preserved for retry)` : "Worktree cleaned up after failure (CSV files preserved for retry)", {
|
|
25261
|
-
cleanedAfterFailure: !keepWorktree,
|
|
25262
|
-
worktreePreserved: keepWorktree,
|
|
25263
|
-
worktreePath,
|
|
25264
|
-
preserveReason: keepWorktree ? "error occurred" : undefined,
|
|
24996
|
+
result.steps.cleanup = buildStepResult(true, "Worktree cleaned up after failure (CSV files preserved for retry)", {
|
|
24997
|
+
cleanedAfterFailure: true,
|
|
25265
24998
|
csvCleanup: { deleted: [] }
|
|
25266
24999
|
});
|
|
25267
25000
|
if (error45 instanceof NoTransactionsError) {
|
|
@@ -25272,18 +25005,11 @@ async function importPipeline(directory, agent, options, configLoader = loadImpo
|
|
|
25272
25005
|
}
|
|
25273
25006
|
return buildErrorResult5(result, result.error, result.hint);
|
|
25274
25007
|
}
|
|
25275
|
-
}, {
|
|
25276
|
-
keepOnError: options.keepWorktreeOnError ?? true,
|
|
25277
|
-
logger
|
|
25278
25008
|
});
|
|
25279
25009
|
} catch (error45) {
|
|
25280
|
-
logger.error("Pipeline failed", error45);
|
|
25281
25010
|
result.steps.worktree = buildStepResult(false, `Failed to create worktree: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
25282
25011
|
result.error = "Failed to create worktree";
|
|
25283
25012
|
return buildErrorResult5(result, result.error);
|
|
25284
|
-
} finally {
|
|
25285
|
-
logger.endSection();
|
|
25286
|
-
await logger.flush();
|
|
25287
25013
|
}
|
|
25288
25014
|
}
|
|
25289
25015
|
var import_pipeline_default = tool({
|
|
@@ -25298,40 +25024,25 @@ This tool orchestrates the full import workflow in an isolated git worktree:
|
|
|
25298
25024
|
4. **Import**: Imports transactions to the journal
|
|
25299
25025
|
5. **Reconcile**: Validates closing balance matches CSV metadata
|
|
25300
25026
|
6. **Merge**: Merges worktree to main with --no-ff
|
|
25301
|
-
7. **Cleanup**: Removes worktree
|
|
25027
|
+
7. **Cleanup**: Removes worktree
|
|
25302
25028
|
|
|
25303
25029
|
**Safety Features:**
|
|
25304
25030
|
- All changes happen in isolated worktree
|
|
25305
|
-
- If any step fails, worktree is
|
|
25031
|
+
- If any step fails, worktree is discarded (main branch untouched)
|
|
25306
25032
|
- Balance reconciliation ensures data integrity
|
|
25307
25033
|
- Atomic commit with merge --no-ff preserves history
|
|
25308
25034
|
|
|
25309
|
-
**Worktree Cleanup:**
|
|
25310
|
-
- On success: Worktree is always cleaned up
|
|
25311
|
-
- On error (default): Worktree is kept at /tmp/import-worktree-<uuid> for debugging
|
|
25312
|
-
- On error (--keepWorktreeOnError false): Worktree is removed (old behavior)
|
|
25313
|
-
- Manual cleanup: git worktree remove /tmp/import-worktree-<uuid>
|
|
25314
|
-
- Auto cleanup: System reboot (worktrees are in /tmp)
|
|
25315
|
-
|
|
25316
|
-
**Logging:**
|
|
25317
|
-
- All operations are logged to .memory/import-<timestamp>.md
|
|
25318
|
-
- Log includes full command output, timing, and error details
|
|
25319
|
-
- Log path is included in tool output for easy access
|
|
25320
|
-
- NO console output (avoids polluting OpenCode TUI)
|
|
25321
|
-
|
|
25322
25035
|
**Usage:**
|
|
25323
25036
|
- Basic: import-pipeline (processes all pending CSVs)
|
|
25324
25037
|
- Filtered: import-pipeline --provider ubs --currency chf
|
|
25325
25038
|
- With manual balance: import-pipeline --closingBalance "CHF 1234.56"
|
|
25326
|
-
- Skip classify: import-pipeline --skipClassify true
|
|
25327
|
-
- Always cleanup: import-pipeline --keepWorktreeOnError false`,
|
|
25039
|
+
- Skip classify: import-pipeline --skipClassify true`,
|
|
25328
25040
|
args: {
|
|
25329
25041
|
provider: tool.schema.string().optional().describe('Filter by provider (e.g., "ubs", "revolut")'),
|
|
25330
25042
|
currency: tool.schema.string().optional().describe('Filter by currency (e.g., "chf", "eur")'),
|
|
25331
25043
|
closingBalance: tool.schema.string().optional().describe("Manual closing balance override (if not in CSV metadata)"),
|
|
25332
25044
|
account: tool.schema.string().optional().describe("Manual account override (auto-detected from rules file if not provided)"),
|
|
25333
|
-
skipClassify: tool.schema.boolean().optional().describe("Skip the classify step (default: false)")
|
|
25334
|
-
keepWorktreeOnError: tool.schema.boolean().optional().describe("Keep worktree on error for debugging (default: true)")
|
|
25045
|
+
skipClassify: tool.schema.boolean().optional().describe("Skip the classify step (default: false)")
|
|
25335
25046
|
},
|
|
25336
25047
|
async execute(params, context) {
|
|
25337
25048
|
const { directory, agent } = context;
|
|
@@ -25340,22 +25051,21 @@ This tool orchestrates the full import workflow in an isolated git worktree:
|
|
|
25340
25051
|
currency: params.currency,
|
|
25341
25052
|
closingBalance: params.closingBalance,
|
|
25342
25053
|
account: params.account,
|
|
25343
|
-
skipClassify: params.skipClassify
|
|
25344
|
-
keepWorktreeOnError: params.keepWorktreeOnError
|
|
25054
|
+
skipClassify: params.skipClassify
|
|
25345
25055
|
});
|
|
25346
25056
|
}
|
|
25347
25057
|
});
|
|
25348
25058
|
// src/tools/init-directories.ts
|
|
25349
|
-
import * as
|
|
25350
|
-
import * as
|
|
25059
|
+
import * as fs14 from "fs";
|
|
25060
|
+
import * as path13 from "path";
|
|
25351
25061
|
async function initDirectories(directory) {
|
|
25352
25062
|
try {
|
|
25353
25063
|
const config2 = loadImportConfig(directory);
|
|
25354
25064
|
const directoriesCreated = [];
|
|
25355
25065
|
const gitkeepFiles = [];
|
|
25356
|
-
const importBase =
|
|
25357
|
-
if (!
|
|
25358
|
-
|
|
25066
|
+
const importBase = path13.join(directory, "import");
|
|
25067
|
+
if (!fs14.existsSync(importBase)) {
|
|
25068
|
+
fs14.mkdirSync(importBase, { recursive: true });
|
|
25359
25069
|
directoriesCreated.push("import");
|
|
25360
25070
|
}
|
|
25361
25071
|
const pathsToCreate = [
|
|
@@ -25365,20 +25075,20 @@ async function initDirectories(directory) {
|
|
|
25365
25075
|
{ key: "unrecognized", path: config2.paths.unrecognized }
|
|
25366
25076
|
];
|
|
25367
25077
|
for (const { path: dirPath } of pathsToCreate) {
|
|
25368
|
-
const fullPath =
|
|
25369
|
-
if (!
|
|
25370
|
-
|
|
25078
|
+
const fullPath = path13.join(directory, dirPath);
|
|
25079
|
+
if (!fs14.existsSync(fullPath)) {
|
|
25080
|
+
fs14.mkdirSync(fullPath, { recursive: true });
|
|
25371
25081
|
directoriesCreated.push(dirPath);
|
|
25372
25082
|
}
|
|
25373
|
-
const gitkeepPath =
|
|
25374
|
-
if (!
|
|
25375
|
-
|
|
25376
|
-
gitkeepFiles.push(
|
|
25083
|
+
const gitkeepPath = path13.join(fullPath, ".gitkeep");
|
|
25084
|
+
if (!fs14.existsSync(gitkeepPath)) {
|
|
25085
|
+
fs14.writeFileSync(gitkeepPath, "");
|
|
25086
|
+
gitkeepFiles.push(path13.join(dirPath, ".gitkeep"));
|
|
25377
25087
|
}
|
|
25378
25088
|
}
|
|
25379
|
-
const gitignorePath =
|
|
25089
|
+
const gitignorePath = path13.join(importBase, ".gitignore");
|
|
25380
25090
|
let gitignoreCreated = false;
|
|
25381
|
-
if (!
|
|
25091
|
+
if (!fs14.existsSync(gitignorePath)) {
|
|
25382
25092
|
const gitignoreContent = `# Ignore CSV/PDF files in temporary directories
|
|
25383
25093
|
/incoming/*.csv
|
|
25384
25094
|
/incoming/*.pdf
|
|
@@ -25396,7 +25106,7 @@ async function initDirectories(directory) {
|
|
|
25396
25106
|
.DS_Store
|
|
25397
25107
|
Thumbs.db
|
|
25398
25108
|
`;
|
|
25399
|
-
|
|
25109
|
+
fs14.writeFileSync(gitignorePath, gitignoreContent);
|
|
25400
25110
|
gitignoreCreated = true;
|
|
25401
25111
|
}
|
|
25402
25112
|
const parts = [];
|