@fuzzle/opencode-accountant 0.1.2 → 0.1.3

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 +185 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1941,7 +1941,7 @@ var require_convert_csv_to_json = __commonJS((exports) => {
1941
1941
  });
1942
1942
 
1943
1943
  // src/index.ts
1944
- import { dirname as dirname6, join as join11 } from "path";
1944
+ import { dirname as dirname6, join as join13 } from "path";
1945
1945
  import { fileURLToPath } from "url";
1946
1946
 
1947
1947
  // src/utils/agentLoader.ts
@@ -17614,6 +17614,27 @@ function execGitSafe(args, cwd) {
17614
17614
  }
17615
17615
  return { success: true, output: (result.stdout || "").trim() };
17616
17616
  }
17617
+ function copyIncomingFiles(mainRepoPath, worktreePath) {
17618
+ const sourceDir = path5.join(mainRepoPath, "import/incoming");
17619
+ const targetDir = path5.join(worktreePath, "import/incoming");
17620
+ if (!fs4.existsSync(sourceDir)) {
17621
+ return;
17622
+ }
17623
+ fs4.mkdirSync(targetDir, { recursive: true });
17624
+ const entries = fs4.readdirSync(sourceDir, { withFileTypes: true });
17625
+ let copiedCount = 0;
17626
+ for (const entry of entries) {
17627
+ if (entry.isFile() && !entry.name.startsWith(".")) {
17628
+ const srcPath = path5.join(sourceDir, entry.name);
17629
+ const destPath = path5.join(targetDir, entry.name);
17630
+ fs4.copyFileSync(srcPath, destPath);
17631
+ copiedCount++;
17632
+ }
17633
+ }
17634
+ if (copiedCount > 0) {
17635
+ console.log(`[INFO] Copied ${copiedCount} file(s) from import/incoming/ to worktree`);
17636
+ }
17637
+ }
17617
17638
  function createImportWorktree(mainRepoPath, options = {}) {
17618
17639
  const baseDir = options.baseDir ?? "/tmp";
17619
17640
  const uuid3 = v4_default();
@@ -17631,6 +17652,7 @@ function createImportWorktree(mainRepoPath, options = {}) {
17631
17652
  execGitSafe(["branch", "-D", branch], mainRepoPath);
17632
17653
  throw error45;
17633
17654
  }
17655
+ copyIncomingFiles(mainRepoPath, worktreePath);
17634
17656
  return {
17635
17657
  path: worktreePath,
17636
17658
  branch,
@@ -18829,6 +18851,8 @@ It must be run inside an import worktree (use import-pipeline for the full workf
18829
18851
  }
18830
18852
  });
18831
18853
  // src/tools/import-pipeline.ts
18854
+ import * as fs12 from "fs";
18855
+ import * as path11 from "path";
18832
18856
  class NoTransactionsError extends Error {
18833
18857
  constructor() {
18834
18858
  super("No transactions to import");
@@ -18866,6 +18890,38 @@ function buildCommitMessage(provider, currency, fromDate, untilDate, transaction
18866
18890
  }
18867
18891
  return `${parts.join(" ")}${dateRange}${txStr}`;
18868
18892
  }
18893
+ function cleanupIncomingFiles(worktree, context) {
18894
+ const incomingDir = path11.join(worktree.mainRepoPath, "import/incoming");
18895
+ if (!fs12.existsSync(incomingDir)) {
18896
+ return;
18897
+ }
18898
+ const importStep = context.result.steps.import;
18899
+ if (!importStep?.success || !importStep.details) {
18900
+ return;
18901
+ }
18902
+ const importResult = importStep.details;
18903
+ if (!importResult.files || !Array.isArray(importResult.files)) {
18904
+ return;
18905
+ }
18906
+ let deletedCount = 0;
18907
+ for (const fileResult of importResult.files) {
18908
+ if (!fileResult.csv)
18909
+ continue;
18910
+ const filename = path11.basename(fileResult.csv);
18911
+ const filePath = path11.join(incomingDir, filename);
18912
+ if (fs12.existsSync(filePath)) {
18913
+ try {
18914
+ fs12.unlinkSync(filePath);
18915
+ deletedCount++;
18916
+ } catch (error45) {
18917
+ console.error(`[ERROR] Failed to delete ${filename}: ${error45 instanceof Error ? error45.message : String(error45)}`);
18918
+ }
18919
+ }
18920
+ }
18921
+ if (deletedCount > 0) {
18922
+ console.log(`[INFO] Cleaned up ${deletedCount} file(s) from import/incoming/`);
18923
+ }
18924
+ }
18869
18925
  async function executeClassifyStep(context, worktree) {
18870
18926
  if (context.options.skipClassify) {
18871
18927
  context.result.steps.classify = buildStepResult(true, "Classification skipped (skipClassify: true)");
@@ -18966,6 +19022,7 @@ async function executeMergeStep(context, worktree) {
18966
19022
  mergeWorktree(worktree, commitMessage);
18967
19023
  const mergeDetails = { commitMessage };
18968
19024
  context.result.steps.merge = buildStepResult(true, `Merged to main: "${commitMessage}"`, mergeDetails);
19025
+ cleanupIncomingFiles(worktree, context);
18969
19026
  } catch (error45) {
18970
19027
  const message = `Merge failed: ${error45 instanceof Error ? error45.message : String(error45)}`;
18971
19028
  context.result.steps.merge = buildStepResult(false, message);
@@ -19074,9 +19131,135 @@ This tool orchestrates the full import workflow in an isolated git worktree:
19074
19131
  });
19075
19132
  }
19076
19133
  });
19134
+ // src/tools/init-directories.ts
19135
+ import * as fs13 from "fs";
19136
+ import * as path12 from "path";
19137
+ async function initDirectories(directory) {
19138
+ try {
19139
+ const config2 = loadImportConfig(directory);
19140
+ const directoriesCreated = [];
19141
+ const gitkeepFiles = [];
19142
+ const importBase = path12.join(directory, "import");
19143
+ if (!fs13.existsSync(importBase)) {
19144
+ fs13.mkdirSync(importBase, { recursive: true });
19145
+ directoriesCreated.push("import");
19146
+ }
19147
+ const pathsToCreate = [
19148
+ { key: "import", path: config2.paths.import },
19149
+ { key: "pending", path: config2.paths.pending },
19150
+ { key: "done", path: config2.paths.done },
19151
+ { key: "unrecognized", path: config2.paths.unrecognized }
19152
+ ];
19153
+ for (const { path: dirPath } of pathsToCreate) {
19154
+ const fullPath = path12.join(directory, dirPath);
19155
+ if (!fs13.existsSync(fullPath)) {
19156
+ fs13.mkdirSync(fullPath, { recursive: true });
19157
+ directoriesCreated.push(dirPath);
19158
+ }
19159
+ const gitkeepPath = path12.join(fullPath, ".gitkeep");
19160
+ if (!fs13.existsSync(gitkeepPath)) {
19161
+ fs13.writeFileSync(gitkeepPath, "");
19162
+ gitkeepFiles.push(path12.join(dirPath, ".gitkeep"));
19163
+ }
19164
+ }
19165
+ const gitignorePath = path12.join(importBase, ".gitignore");
19166
+ let gitignoreCreated = false;
19167
+ if (!fs13.existsSync(gitignorePath)) {
19168
+ const gitignoreContent = `# Ignore CSV/PDF files in temporary directories
19169
+ /incoming/*.csv
19170
+ /incoming/*.pdf
19171
+ /pending/**/*.csv
19172
+ /pending/**/*.pdf
19173
+ /unrecognized/**/*.csv
19174
+ /unrecognized/**/*.pdf
19175
+
19176
+ # Track processed files in done/ (audit trail)
19177
+ # No ignore rule needed - tracked by default
19178
+
19179
+ # Ignore temporary files
19180
+ *.tmp
19181
+ *.temp
19182
+ .DS_Store
19183
+ Thumbs.db
19184
+ `;
19185
+ fs13.writeFileSync(gitignorePath, gitignoreContent);
19186
+ gitignoreCreated = true;
19187
+ }
19188
+ const parts = [];
19189
+ if (directoriesCreated.length > 0) {
19190
+ parts.push(`Created ${directoriesCreated.length} director${directoriesCreated.length === 1 ? "y" : "ies"}`);
19191
+ }
19192
+ if (gitkeepFiles.length > 0) {
19193
+ parts.push(`added ${gitkeepFiles.length} .gitkeep file${gitkeepFiles.length === 1 ? "" : "s"}`);
19194
+ }
19195
+ if (gitignoreCreated) {
19196
+ parts.push("created .gitignore");
19197
+ }
19198
+ const message = parts.length > 0 ? `Import directory structure initialized: ${parts.join(", ")}` : "Import directory structure already exists (no changes needed)";
19199
+ return {
19200
+ success: true,
19201
+ directoriesCreated,
19202
+ gitkeepFiles,
19203
+ gitignoreCreated,
19204
+ message
19205
+ };
19206
+ } catch (error45) {
19207
+ return {
19208
+ success: false,
19209
+ directoriesCreated: [],
19210
+ gitkeepFiles: [],
19211
+ gitignoreCreated: false,
19212
+ error: error45 instanceof Error ? error45.message : String(error45),
19213
+ message: "Failed to initialize import directory structure"
19214
+ };
19215
+ }
19216
+ }
19217
+ var init_directories_default = tool({
19218
+ description: "ACCOUNTANT AGENT ONLY: Initialize the import directory structure needed for processing bank statements. Creates import/incoming, import/pending, import/done, and import/unrecognized directories with .gitkeep files and appropriate .gitignore rules. Reads directory paths from config/import/providers.yaml. Safe to run multiple times (idempotent).",
19219
+ args: {},
19220
+ async execute(_params, context) {
19221
+ const restrictionError = checkAccountantAgent(context.agent, "init directories");
19222
+ if (restrictionError) {
19223
+ throw new Error(restrictionError);
19224
+ }
19225
+ const { directory } = context;
19226
+ const result = await initDirectories(directory);
19227
+ if (!result.success) {
19228
+ return `Error: ${result.error}
19229
+
19230
+ ${result.message}`;
19231
+ }
19232
+ const output = [];
19233
+ output.push(result.message || "");
19234
+ if (result.directoriesCreated.length > 0) {
19235
+ output.push(`
19236
+ Directories created:`);
19237
+ for (const dir of result.directoriesCreated) {
19238
+ output.push(` - ${dir}`);
19239
+ }
19240
+ }
19241
+ if (result.gitkeepFiles.length > 0) {
19242
+ output.push(`
19243
+ .gitkeep files added:`);
19244
+ for (const file2 of result.gitkeepFiles) {
19245
+ output.push(` - ${file2}`);
19246
+ }
19247
+ }
19248
+ if (result.gitignoreCreated) {
19249
+ output.push(`
19250
+ Created import/.gitignore with rules to:`);
19251
+ output.push(" - Ignore CSV/PDF files in incoming/, pending/, unrecognized/");
19252
+ output.push(" - Track processed files in done/ for audit trail");
19253
+ }
19254
+ output.push(`
19255
+ You can now drop CSV files into import/incoming/ and run import-pipeline.`);
19256
+ return output.join(`
19257
+ `);
19258
+ }
19259
+ });
19077
19260
  // src/index.ts
19078
19261
  var __dirname2 = dirname6(fileURLToPath(import.meta.url));
19079
- var AGENT_FILE = join11(__dirname2, "..", "agent", "accountant.md");
19262
+ var AGENT_FILE = join13(__dirname2, "..", "agent", "accountant.md");
19080
19263
  var AccountantPlugin = async () => {
19081
19264
  const agent = loadAgent(AGENT_FILE);
19082
19265
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fuzzle/opencode-accountant",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "An OpenCode accounting agent, specialized in double-entry-bookkepping with hledger",
5
5
  "author": {
6
6
  "name": "ali bengali",