@dizzlkheinz/ynab-mcpb 0.18.4 → 0.19.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 (343) hide show
  1. package/CLAUDE.md +87 -8
  2. package/bin/ynab-mcp-server.cjs +2 -2
  3. package/bin/ynab-mcp-server.js +3 -3
  4. package/biome.json +39 -0
  5. package/dist/bundle/index.cjs +67 -67
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.js +27 -27
  8. package/dist/server/YNABMCPServer.d.ts +3 -4
  9. package/dist/server/YNABMCPServer.js +111 -116
  10. package/dist/server/budgetResolver.d.ts +6 -5
  11. package/dist/server/budgetResolver.js +46 -36
  12. package/dist/server/cacheKeys.js +6 -6
  13. package/dist/server/cacheManager.js +14 -11
  14. package/dist/server/completions.d.ts +2 -2
  15. package/dist/server/completions.js +20 -15
  16. package/dist/server/config.d.ts +10 -5
  17. package/dist/server/config.js +24 -7
  18. package/dist/server/deltaCache.d.ts +2 -2
  19. package/dist/server/deltaCache.js +22 -16
  20. package/dist/server/deltaCache.merge.d.ts +2 -2
  21. package/dist/server/diagnostics.d.ts +4 -4
  22. package/dist/server/diagnostics.js +38 -32
  23. package/dist/server/errorHandler.d.ts +5 -12
  24. package/dist/server/errorHandler.js +219 -217
  25. package/dist/server/prompts.d.ts +2 -2
  26. package/dist/server/prompts.js +45 -45
  27. package/dist/server/rateLimiter.js +4 -4
  28. package/dist/server/requestLogger.d.ts +1 -1
  29. package/dist/server/requestLogger.js +40 -35
  30. package/dist/server/resources.d.ts +3 -3
  31. package/dist/server/resources.js +55 -52
  32. package/dist/server/responseFormatter.js +6 -6
  33. package/dist/server/securityMiddleware.d.ts +2 -2
  34. package/dist/server/securityMiddleware.js +22 -20
  35. package/dist/server/serverKnowledgeStore.js +1 -1
  36. package/dist/server/toolRegistry.d.ts +3 -3
  37. package/dist/server/toolRegistry.js +47 -40
  38. package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
  39. package/dist/tools/__tests__/deltaTestUtils.js +2 -2
  40. package/dist/tools/accountTools.d.ts +9 -8
  41. package/dist/tools/accountTools.js +47 -47
  42. package/dist/tools/adapters.d.ts +13 -8
  43. package/dist/tools/adapters.js +21 -11
  44. package/dist/tools/budgetTools.d.ts +8 -7
  45. package/dist/tools/budgetTools.js +22 -22
  46. package/dist/tools/categoryTools.d.ts +9 -8
  47. package/dist/tools/categoryTools.js +68 -59
  48. package/dist/tools/compareTransactions/formatter.d.ts +3 -3
  49. package/dist/tools/compareTransactions/formatter.js +9 -9
  50. package/dist/tools/compareTransactions/index.d.ts +6 -6
  51. package/dist/tools/compareTransactions/index.js +58 -43
  52. package/dist/tools/compareTransactions/matcher.d.ts +1 -1
  53. package/dist/tools/compareTransactions/matcher.js +28 -15
  54. package/dist/tools/compareTransactions/parser.d.ts +2 -2
  55. package/dist/tools/compareTransactions/parser.js +144 -138
  56. package/dist/tools/compareTransactions/types.d.ts +4 -4
  57. package/dist/tools/compareTransactions.d.ts +1 -1
  58. package/dist/tools/compareTransactions.js +1 -1
  59. package/dist/tools/deltaFetcher.d.ts +2 -2
  60. package/dist/tools/deltaFetcher.js +16 -15
  61. package/dist/tools/deltaSupport.d.ts +4 -4
  62. package/dist/tools/deltaSupport.js +35 -41
  63. package/dist/tools/exportTransactions.d.ts +5 -4
  64. package/dist/tools/exportTransactions.js +61 -59
  65. package/dist/tools/monthTools.d.ts +7 -6
  66. package/dist/tools/monthTools.js +31 -29
  67. package/dist/tools/payeeTools.d.ts +7 -6
  68. package/dist/tools/payeeTools.js +28 -28
  69. package/dist/tools/reconcileAdapter.d.ts +2 -2
  70. package/dist/tools/reconcileAdapter.js +19 -12
  71. package/dist/tools/reconciliation/analyzer.d.ts +4 -4
  72. package/dist/tools/reconciliation/analyzer.js +73 -59
  73. package/dist/tools/reconciliation/csvParser.d.ts +3 -3
  74. package/dist/tools/reconciliation/csvParser.js +128 -104
  75. package/dist/tools/reconciliation/executor.d.ts +4 -4
  76. package/dist/tools/reconciliation/executor.js +148 -109
  77. package/dist/tools/reconciliation/index.d.ts +10 -10
  78. package/dist/tools/reconciliation/index.js +96 -83
  79. package/dist/tools/reconciliation/matcher.d.ts +3 -3
  80. package/dist/tools/reconciliation/matcher.js +17 -16
  81. package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
  82. package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
  83. package/dist/tools/reconciliation/recommendationEngine.js +40 -40
  84. package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
  85. package/dist/tools/reconciliation/reportFormatter.js +59 -58
  86. package/dist/tools/reconciliation/signDetector.d.ts +1 -1
  87. package/dist/tools/reconciliation/types.d.ts +16 -16
  88. package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
  89. package/dist/tools/schemas/common.d.ts +1 -1
  90. package/dist/tools/schemas/common.js +1 -1
  91. package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
  92. package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
  93. package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
  94. package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
  95. package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
  96. package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
  97. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
  98. package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
  99. package/dist/tools/schemas/outputs/index.d.ts +14 -14
  100. package/dist/tools/schemas/outputs/index.js +14 -14
  101. package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
  102. package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
  103. package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
  104. package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
  105. package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
  106. package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
  107. package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
  108. package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
  109. package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
  110. package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
  111. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
  112. package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
  113. package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
  114. package/dist/tools/schemas/shared/commonOutputs.js +15 -9
  115. package/dist/tools/transactionReadTools.d.ts +11 -0
  116. package/dist/tools/transactionReadTools.js +202 -0
  117. package/dist/tools/transactionSchemas.d.ts +7 -7
  118. package/dist/tools/transactionSchemas.js +77 -57
  119. package/dist/tools/transactionTools.d.ts +6 -24
  120. package/dist/tools/transactionTools.js +7 -1499
  121. package/dist/tools/transactionUtils.d.ts +6 -6
  122. package/dist/tools/transactionUtils.js +78 -63
  123. package/dist/tools/transactionWriteTools.d.ts +20 -0
  124. package/dist/tools/transactionWriteTools.js +1342 -0
  125. package/dist/tools/utilityTools.d.ts +5 -4
  126. package/dist/tools/utilityTools.js +11 -11
  127. package/dist/types/index.d.ts +7 -7
  128. package/dist/types/index.js +6 -6
  129. package/dist/types/reconciliation.d.ts +1 -1
  130. package/dist/types/toolRegistration.d.ts +14 -12
  131. package/dist/utils/amountUtils.js +1 -1
  132. package/dist/utils/dateUtils.js +4 -4
  133. package/dist/utils/errors.d.ts +3 -3
  134. package/dist/utils/errors.js +4 -4
  135. package/dist/utils/money.d.ts +2 -2
  136. package/dist/utils/money.js +8 -8
  137. package/dist/utils/validationError.d.ts +1 -1
  138. package/dist/utils/validationError.js +1 -1
  139. package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
  140. package/docs/assets/schemas/reconciliation-v2.json +360 -336
  141. package/esbuild.config.mjs +53 -50
  142. package/meta.json +12548 -12548
  143. package/package.json +98 -111
  144. package/scripts/analyze-bundle.mjs +33 -30
  145. package/scripts/create-pr-description.js +169 -120
  146. package/scripts/run-all-tests.js +178 -169
  147. package/scripts/run-domain-integration-tests.js +28 -18
  148. package/scripts/run-generate-mcpb.js +19 -17
  149. package/scripts/run-throttled-integration-tests.js +92 -83
  150. package/scripts/test-delta-params.mjs +149 -120
  151. package/scripts/test-recommendations.ts +36 -32
  152. package/scripts/tmpTransaction.ts +80 -43
  153. package/scripts/validate-env.js +98 -91
  154. package/scripts/verify-build.js +78 -76
  155. package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
  156. package/src/__tests__/performance.test.ts +723 -671
  157. package/src/__tests__/setup.ts +442 -395
  158. package/src/__tests__/smoke.e2e.test.ts +41 -39
  159. package/src/__tests__/testRunner.ts +314 -295
  160. package/src/__tests__/testUtils.ts +456 -364
  161. package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
  162. package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
  163. package/src/index.ts +68 -59
  164. package/src/server/CLAUDE.md +480 -0
  165. package/src/server/YNABMCPServer.ts +821 -794
  166. package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
  167. package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
  168. package/src/server/__tests__/budgetResolver.test.ts +466 -423
  169. package/src/server/__tests__/cacheManager.test.ts +891 -874
  170. package/src/server/__tests__/completions.integration.test.ts +115 -106
  171. package/src/server/__tests__/completions.test.ts +334 -313
  172. package/src/server/__tests__/config.test.ts +98 -86
  173. package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
  174. package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
  175. package/src/server/__tests__/deltaCache.test.ts +946 -759
  176. package/src/server/__tests__/diagnostics.test.ts +825 -792
  177. package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
  178. package/src/server/__tests__/errorHandler.test.ts +402 -397
  179. package/src/server/__tests__/prompts.test.ts +424 -347
  180. package/src/server/__tests__/rateLimiter.test.ts +313 -309
  181. package/src/server/__tests__/requestLogger.test.ts +443 -403
  182. package/src/server/__tests__/resources.template.test.ts +196 -185
  183. package/src/server/__tests__/resources.test.ts +294 -288
  184. package/src/server/__tests__/security.integration.test.ts +487 -421
  185. package/src/server/__tests__/securityMiddleware.test.ts +519 -444
  186. package/src/server/__tests__/server-startup.integration.test.ts +509 -490
  187. package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
  188. package/src/server/__tests__/toolRegistration.test.ts +239 -210
  189. package/src/server/__tests__/toolRegistry.test.ts +907 -845
  190. package/src/server/budgetResolver.ts +221 -181
  191. package/src/server/cacheKeys.ts +6 -6
  192. package/src/server/cacheManager.ts +498 -484
  193. package/src/server/completions.ts +267 -243
  194. package/src/server/config.ts +35 -14
  195. package/src/server/deltaCache.merge.ts +146 -128
  196. package/src/server/deltaCache.ts +352 -309
  197. package/src/server/diagnostics.ts +257 -242
  198. package/src/server/errorHandler.ts +747 -744
  199. package/src/server/prompts.ts +181 -176
  200. package/src/server/rateLimiter.ts +131 -129
  201. package/src/server/requestLogger.ts +350 -322
  202. package/src/server/resources.ts +442 -374
  203. package/src/server/responseFormatter.ts +41 -37
  204. package/src/server/securityMiddleware.ts +223 -205
  205. package/src/server/serverKnowledgeStore.ts +67 -67
  206. package/src/server/toolRegistry.ts +508 -474
  207. package/src/tools/CLAUDE.md +604 -0
  208. package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
  209. package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
  210. package/src/tools/__tests__/accountTools.test.ts +685 -638
  211. package/src/tools/__tests__/adapters.test.ts +142 -108
  212. package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
  213. package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
  214. package/src/tools/__tests__/budgetTools.test.ts +442 -413
  215. package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
  216. package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
  217. package/src/tools/__tests__/categoryTools.test.ts +656 -625
  218. package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
  219. package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
  220. package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
  221. package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
  222. package/src/tools/__tests__/compareTransactions.test.ts +352 -332
  223. package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
  224. package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
  225. package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
  226. package/src/tools/__tests__/deltaSupport.test.ts +211 -184
  227. package/src/tools/__tests__/deltaTestUtils.ts +37 -33
  228. package/src/tools/__tests__/exportTransactions.test.ts +205 -200
  229. package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
  230. package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
  231. package/src/tools/__tests__/monthTools.test.ts +561 -512
  232. package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
  233. package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
  234. package/src/tools/__tests__/payeeTools.test.ts +486 -434
  235. package/src/tools/__tests__/transactionSchemas.test.ts +1202 -1186
  236. package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
  237. package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
  238. package/src/tools/__tests__/transactionUtils.test.ts +1004 -977
  239. package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
  240. package/src/tools/__tests__/utilityTools.test.ts +68 -58
  241. package/src/tools/accountTools.ts +293 -271
  242. package/src/tools/adapters.ts +120 -63
  243. package/src/tools/budgetTools.ts +121 -116
  244. package/src/tools/categoryTools.ts +379 -339
  245. package/src/tools/compareTransactions/formatter.ts +131 -119
  246. package/src/tools/compareTransactions/index.ts +249 -214
  247. package/src/tools/compareTransactions/matcher.ts +259 -209
  248. package/src/tools/compareTransactions/parser.ts +517 -487
  249. package/src/tools/compareTransactions/types.ts +38 -38
  250. package/src/tools/compareTransactions.ts +1 -1
  251. package/src/tools/deltaFetcher.ts +281 -260
  252. package/src/tools/deltaSupport.ts +264 -259
  253. package/src/tools/exportTransactions.ts +230 -218
  254. package/src/tools/monthTools.ts +180 -165
  255. package/src/tools/payeeTools.ts +152 -140
  256. package/src/tools/reconcileAdapter.ts +297 -252
  257. package/src/tools/reconciliation/CLAUDE.md +506 -0
  258. package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +133 -124
  259. package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -230
  260. package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -400
  261. package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
  262. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
  263. package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
  264. package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
  265. package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
  266. package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
  267. package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
  268. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -989
  269. package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
  270. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -533
  271. package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -74
  272. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -62
  273. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
  274. package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +56 -55
  275. package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
  276. package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
  277. package/src/tools/reconciliation/analyzer.ts +564 -504
  278. package/src/tools/reconciliation/csvParser.ts +656 -609
  279. package/src/tools/reconciliation/executor.ts +1290 -1128
  280. package/src/tools/reconciliation/index.ts +580 -528
  281. package/src/tools/reconciliation/matcher.ts +256 -240
  282. package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
  283. package/src/tools/reconciliation/recommendationEngine.ts +357 -345
  284. package/src/tools/reconciliation/reportFormatter.ts +343 -307
  285. package/src/tools/reconciliation/signDetector.ts +89 -83
  286. package/src/tools/reconciliation/types.ts +164 -159
  287. package/src/tools/reconciliation/ynabAdapter.ts +17 -15
  288. package/src/tools/schemas/CLAUDE.md +546 -0
  289. package/src/tools/schemas/common.ts +1 -1
  290. package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
  291. package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
  292. package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
  293. package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
  294. package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
  295. package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
  296. package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
  297. package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
  298. package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
  299. package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
  300. package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
  301. package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
  302. package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
  303. package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
  304. package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
  305. package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
  306. package/src/tools/schemas/outputs/index.ts +163 -163
  307. package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
  308. package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
  309. package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
  310. package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
  311. package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
  312. package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
  313. package/src/tools/schemas/shared/commonOutputs.ts +27 -19
  314. package/src/tools/toolCategories.ts +114 -114
  315. package/src/tools/transactionReadTools.ts +327 -0
  316. package/src/tools/transactionSchemas.ts +322 -291
  317. package/src/tools/transactionTools.ts +84 -2246
  318. package/src/tools/transactionUtils.ts +507 -422
  319. package/src/tools/transactionWriteTools.ts +2110 -0
  320. package/src/tools/utilityTools.ts +46 -41
  321. package/src/types/CLAUDE.md +477 -0
  322. package/src/types/__tests__/index.test.ts +51 -51
  323. package/src/types/index.ts +43 -39
  324. package/src/types/integration-tests.d.ts +26 -26
  325. package/src/types/reconciliation.ts +29 -29
  326. package/src/types/toolAnnotations.ts +30 -30
  327. package/src/types/toolRegistration.ts +43 -32
  328. package/src/utils/CLAUDE.md +508 -0
  329. package/src/utils/__tests__/dateUtils.test.ts +174 -168
  330. package/src/utils/__tests__/money.test.ts +193 -187
  331. package/src/utils/amountUtils.ts +5 -5
  332. package/src/utils/baseError.ts +5 -5
  333. package/src/utils/dateUtils.ts +29 -26
  334. package/src/utils/errors.ts +14 -14
  335. package/src/utils/money.ts +66 -52
  336. package/src/utils/validationError.ts +1 -1
  337. package/tsconfig.json +29 -29
  338. package/tsconfig.prod.json +16 -16
  339. package/vitest-reporters/split-json-reporter.ts +247 -204
  340. package/vitest.config.ts +99 -95
  341. package/.prettierignore +0 -10
  342. package/.prettierrc.json +0 -10
  343. package/eslint.config.js +0 -49
@@ -1,24 +1,24 @@
1
- import { z } from 'zod/v4';
2
- import { withToolErrorHandling } from '../types/index.js';
3
- import { responseFormatter } from '../server/responseFormatter.js';
4
- import { cacheManager, CACHE_TTLS, CacheManager } from '../server/cacheManager.js';
5
- import { CacheKeys } from '../server/cacheKeys.js';
6
- import { resolveDeltaFetcherArgs } from './deltaSupport.js';
7
- import { createAdapters, createBudgetResolver } from './adapters.js';
8
- import { ToolAnnotationPresets } from './toolCategories.js';
1
+ import { z } from "zod/v4";
2
+ import { CacheKeys } from "../server/cacheKeys.js";
3
+ import { CACHE_TTLS, CacheManager, cacheManager, } from "../server/cacheManager.js";
4
+ import { responseFormatter } from "../server/responseFormatter.js";
5
+ import { withToolErrorHandling } from "../types/index.js";
6
+ import { createAdapters, createBudgetResolver } from "./adapters.js";
7
+ import { resolveDeltaFetcherArgs } from "./deltaSupport.js";
8
+ import { ToolAnnotationPresets } from "./toolCategories.js";
9
9
  export const ListPayeesSchema = z
10
10
  .object({
11
- budget_id: z.string().min(1, 'Budget ID is required'),
11
+ budget_id: z.string().min(1, "Budget ID is required"),
12
12
  limit: z.number().int().positive().optional(),
13
13
  })
14
14
  .strict();
15
15
  export const GetPayeeSchema = z
16
16
  .object({
17
- budget_id: z.string().min(1, 'Budget ID is required'),
18
- payee_id: z.string().min(1, 'Payee ID is required'),
17
+ budget_id: z.string().min(1, "Budget ID is required"),
18
+ payee_id: z.string().min(1, "Payee ID is required"),
19
19
  })
20
20
  .strict();
21
- export async function handleListPayees(ynabAPI, deltaFetcherOrParams, maybeParams) {
21
+ export async function handleListPayees(ynabAPI, deltaFetcherOrParams, maybeParams, errorHandler) {
22
22
  const { deltaFetcher, params } = resolveDeltaFetcherArgs(ynabAPI, deltaFetcherOrParams, maybeParams);
23
23
  return await withToolErrorHandling(async () => {
24
24
  const result = await deltaFetcher.fetchPayees(params.budget_id);
@@ -31,7 +31,7 @@ export async function handleListPayees(ynabAPI, deltaFetcherOrParams, maybeParam
31
31
  return {
32
32
  content: [
33
33
  {
34
- type: 'text',
34
+ type: "text",
35
35
  text: responseFormatter.format({
36
36
  payees: payees.map((payee) => ({
37
37
  id: payee.id,
@@ -43,17 +43,17 @@ export async function handleListPayees(ynabAPI, deltaFetcherOrParams, maybeParam
43
43
  returned_count: payees.length,
44
44
  cached: wasCached,
45
45
  cache_info: wasCached
46
- ? `Data retrieved from cache for improved performance${result.usedDelta ? ' (delta merge applied)' : ''}`
47
- : 'Fresh data retrieved from YNAB API',
46
+ ? `Data retrieved from cache for improved performance${result.usedDelta ? " (delta merge applied)" : ""}`
47
+ : "Fresh data retrieved from YNAB API",
48
48
  }),
49
49
  },
50
50
  ],
51
51
  };
52
- }, 'ynab:list_payees', 'listing payees');
52
+ }, "ynab:list_payees", "listing payees", errorHandler);
53
53
  }
54
- export async function handleGetPayee(ynabAPI, params) {
54
+ export async function handleGetPayee(ynabAPI, params, errorHandler) {
55
55
  return await withToolErrorHandling(async () => {
56
- const cacheKey = CacheManager.generateKey(CacheKeys.PAYEES, 'get', params.budget_id, params.payee_id);
56
+ const cacheKey = CacheManager.generateKey(CacheKeys.PAYEES, "get", params.budget_id, params.payee_id);
57
57
  const wasCached = cacheManager.has(cacheKey);
58
58
  const payee = await cacheManager.wrap(cacheKey, {
59
59
  ttl: CACHE_TTLS.PAYEES,
@@ -65,7 +65,7 @@ export async function handleGetPayee(ynabAPI, params) {
65
65
  return {
66
66
  content: [
67
67
  {
68
- type: 'text',
68
+ type: "text",
69
69
  text: responseFormatter.format({
70
70
  payee: {
71
71
  id: payee.id,
@@ -75,40 +75,40 @@ export async function handleGetPayee(ynabAPI, params) {
75
75
  },
76
76
  cached: wasCached,
77
77
  cache_info: wasCached
78
- ? 'Data retrieved from cache for improved performance'
79
- : 'Fresh data retrieved from YNAB API',
78
+ ? "Data retrieved from cache for improved performance"
79
+ : "Fresh data retrieved from YNAB API",
80
80
  }),
81
81
  },
82
82
  ],
83
83
  };
84
- }, 'ynab:get_payee', 'getting payee details');
84
+ }, "ynab:get_payee", "getting payee details", errorHandler);
85
85
  }
86
86
  export const registerPayeeTools = (registry, context) => {
87
87
  const { adapt, adaptWithDelta } = createAdapters(context);
88
88
  const budgetResolver = createBudgetResolver(context);
89
89
  registry.register({
90
- name: 'list_payees',
91
- description: 'List all payees for a specific budget',
90
+ name: "list_payees",
91
+ description: "List all payees for a specific budget",
92
92
  inputSchema: ListPayeesSchema,
93
93
  handler: adaptWithDelta(handleListPayees),
94
94
  defaultArgumentResolver: budgetResolver(),
95
95
  metadata: {
96
96
  annotations: {
97
97
  ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
98
- title: 'YNAB: List Payees',
98
+ title: "YNAB: List Payees",
99
99
  },
100
100
  },
101
101
  });
102
102
  registry.register({
103
- name: 'get_payee',
104
- description: 'Get detailed information for a specific payee',
103
+ name: "get_payee",
104
+ description: "Get detailed information for a specific payee",
105
105
  inputSchema: GetPayeeSchema,
106
106
  handler: adapt(handleGetPayee),
107
107
  defaultArgumentResolver: budgetResolver(),
108
108
  metadata: {
109
109
  annotations: {
110
110
  ...ToolAnnotationPresets.READ_ONLY_EXTERNAL,
111
- title: 'YNAB: Get Payee Details',
111
+ title: "YNAB: Get Payee Details",
112
112
  },
113
113
  },
114
114
  });
@@ -1,5 +1,5 @@
1
- import type { ReconciliationAnalysis } from './reconciliation/types.js';
2
- import type { LegacyReconciliationResult } from './reconciliation/executor.js';
1
+ import type { LegacyReconciliationResult } from "./reconciliation/executor.js";
2
+ import type { ReconciliationAnalysis } from "./reconciliation/types.js";
3
3
  interface AdapterOptions {
4
4
  accountName?: string;
5
5
  accountId?: string;
@@ -1,7 +1,7 @@
1
- import { toMoneyValue, toMoneyValueFromDecimal } from '../utils/money.js';
2
- import { formatHumanReadableReport, } from './reconciliation/reportFormatter.js';
3
- const OUTPUT_VERSION = '2.0';
4
- const SCHEMA_URL = 'https://raw.githubusercontent.com/dizzlkheinz/ynab-mcp-mcpb/master/docs/schemas/reconciliation-v2.json';
1
+ import { toMoneyValue, toMoneyValueFromDecimal } from "../utils/money.js";
2
+ import { formatHumanReadableReport, } from "./reconciliation/reportFormatter.js";
3
+ const OUTPUT_VERSION = "2.0";
4
+ const SCHEMA_URL = "https://raw.githubusercontent.com/dizzlkheinz/ynab-mcp-mcpb/master/docs/schemas/reconciliation-v2.json";
5
5
  const toBankTransactionView = (txn, currency) => ({
6
6
  ...txn,
7
7
  amount_money: toMoneyValue(txn.amount, currency),
@@ -38,7 +38,8 @@ const convertSummary = (analysis) => ({
38
38
  statement_date_range: analysis.summary.statement_date_range,
39
39
  bank_transactions_count: analysis.summary.bank_transactions_count,
40
40
  ynab_transactions_count: analysis.summary.ynab_transactions_count,
41
- ynab_in_range_count: analysis.summary.ynab_in_range_count ?? analysis.summary.ynab_transactions_count,
41
+ ynab_in_range_count: analysis.summary.ynab_in_range_count ??
42
+ analysis.summary.ynab_transactions_count,
42
43
  ynab_outside_range_count: analysis.summary.ynab_outside_range_count ?? 0,
43
44
  auto_matched: analysis.summary.auto_matched,
44
45
  suggested_matches: analysis.summary.suggested_matches,
@@ -51,7 +52,11 @@ const convertSummary = (analysis) => ({
51
52
  });
52
53
  const convertBalanceInfo = (analysis) => {
53
54
  const discrepancyMilli = analysis.balance_info.discrepancy.value_milliunits;
54
- const direction = discrepancyMilli === 0 ? 'balanced' : discrepancyMilli > 0 ? 'ynab_higher' : 'bank_higher';
55
+ const direction = discrepancyMilli === 0
56
+ ? "balanced"
57
+ : discrepancyMilli > 0
58
+ ? "ynab_higher"
59
+ : "bank_higher";
55
60
  return {
56
61
  current_cleared: analysis.balance_info.current_cleared,
57
62
  current_uncleared: analysis.balance_info.current_uncleared,
@@ -133,8 +138,10 @@ const buildHumanNarrative = (analysis, options, execution) => {
133
138
  return formatHumanReadableReport(analysis, formatterOptions, execution);
134
139
  };
135
140
  export const buildReconciliationPayload = (analysis, options = {}, execution) => {
136
- const currency = options.currencyCode ?? 'USD';
137
- const executionView = execution ? convertExecution(execution, currency) : undefined;
141
+ const currency = options.currencyCode ?? "USD";
142
+ const executionView = execution
143
+ ? convertExecution(execution, currency)
144
+ : undefined;
138
145
  const structured = {
139
146
  version: OUTPUT_VERSION,
140
147
  schema_url: SCHEMA_URL,
@@ -158,16 +165,16 @@ export const buildReconciliationPayload = (analysis, options = {}, execution) =>
158
165
  },
159
166
  };
160
167
  if (analysis.recommendations && analysis.recommendations.length > 0) {
161
- structured['recommendations'] = analysis.recommendations;
168
+ structured["recommendations"] = analysis.recommendations;
162
169
  }
163
170
  if (options.csvFormat) {
164
- structured['csv_format'] = options.csvFormat;
171
+ structured["csv_format"] = options.csvFormat;
165
172
  }
166
173
  if (executionView) {
167
- structured['execution'] = executionView;
174
+ structured["execution"] = executionView;
168
175
  }
169
176
  if (options.auditMetadata) {
170
- structured['audit'] = options.auditMetadata;
177
+ structured["audit"] = options.auditMetadata;
171
178
  }
172
179
  return {
173
180
  human: buildHumanNarrative(analysis, options, execution),
@@ -1,7 +1,7 @@
1
- import type * as ynab from 'ynab';
2
- import { type ParseCSVOptions, type CSVParseResult } from './csvParser.js';
3
- import { type MatchingConfig } from './matcher.js';
4
- import type { ReconciliationAnalysis } from './types.js';
1
+ import type * as ynab from "ynab";
2
+ import { type CSVParseResult, type ParseCSVOptions } from "./csvParser.js";
3
+ import { type MatchingConfig } from "./matcher.js";
4
+ import type { ReconciliationAnalysis } from "./types.js";
5
5
  export declare function analyzeReconciliation(csvContentOrParsed: string | CSVParseResult, _csvFilePath: string | undefined, ynabTransactions: ynab.TransactionDetail[], statementBalance: number, config?: MatchingConfig, currency?: string, accountId?: string, budgetId?: string, invertBankAmounts?: boolean, csvOptions?: ParseCSVOptions, accountSnapshot?: {
6
6
  balance?: number;
7
7
  cleared_balance?: number;
@@ -1,8 +1,8 @@
1
- import { parseCSV } from './csvParser.js';
2
- import { findMatches, normalizeConfig, DEFAULT_CONFIG } from './matcher.js';
3
- import { normalizeYNABTransactions } from './ynabAdapter.js';
4
- import { toMoneyValue } from '../../utils/money.js';
5
- import { generateRecommendations } from './recommendationEngine.js';
1
+ import { parseCSV, } from "./csvParser.js";
2
+ import { DEFAULT_CONFIG, findMatches, normalizeConfig, } from "./matcher.js";
3
+ import { normalizeYNABTransactions } from "./ynabAdapter.js";
4
+ import { toMoneyValue } from "../../utils/money.js";
5
+ import { generateRecommendations } from "./recommendationEngine.js";
6
6
  function calculateDateRange(bankTransactions) {
7
7
  if (bankTransactions.length === 0) {
8
8
  return null;
@@ -14,20 +14,25 @@ function calculateDateRange(bankTransactions) {
14
14
  if (dates.length === 0) {
15
15
  return null;
16
16
  }
17
+ const minDate = dates[0];
18
+ const maxDate = dates[dates.length - 1];
19
+ if (!minDate || !maxDate) {
20
+ return null;
21
+ }
17
22
  return {
18
- minDate: dates[0],
19
- maxDate: dates[dates.length - 1],
23
+ minDate,
24
+ maxDate,
20
25
  };
21
26
  }
22
27
  function filterByDateRange(ynabTransactions, dateRange, dateToleranceDays = 7) {
28
+ const safeToleranceDays = dateToleranceDays < 0 ? 0 : dateToleranceDays;
23
29
  if (dateToleranceDays < 0) {
24
30
  console.warn(`[filterByDateRange] dateToleranceDays must be non-negative, got ${dateToleranceDays}. Using 0.`);
25
- dateToleranceDays = 0;
26
31
  }
27
32
  const inRange = [];
28
33
  const outsideRange = [];
29
- const minParts = dateRange.minDate.split('-').map(Number);
30
- const maxParts = dateRange.maxDate.split('-').map(Number);
34
+ const minParts = dateRange.minDate.split("-").map(Number);
35
+ const maxParts = dateRange.maxDate.split("-").map(Number);
31
36
  if (minParts.length !== 3 ||
32
37
  maxParts.length !== 3 ||
33
38
  minParts.some((n) => !Number.isFinite(n)) ||
@@ -37,10 +42,10 @@ function filterByDateRange(ynabTransactions, dateRange, dateToleranceDays = 7) {
37
42
  }
38
43
  const [minYear, minMonth, minDay] = minParts;
39
44
  const [maxYear, maxMonth, maxDay] = maxParts;
40
- const minDateWithBuffer = new Date(Date.UTC(minYear, minMonth - 1, minDay - dateToleranceDays));
41
- const minDateStr = minDateWithBuffer.toISOString().split('T')[0];
42
- const maxDateWithBuffer = new Date(Date.UTC(maxYear, maxMonth - 1, maxDay + dateToleranceDays));
43
- const maxDateStr = maxDateWithBuffer.toISOString().split('T')[0];
45
+ const minDateWithBuffer = new Date(Date.UTC(minYear, minMonth - 1, minDay - safeToleranceDays));
46
+ const minDateStr = minDateWithBuffer.toISOString().split("T")[0] ?? "";
47
+ const maxDateWithBuffer = new Date(Date.UTC(maxYear, maxMonth - 1, maxDay + safeToleranceDays));
48
+ const maxDateStr = maxDateWithBuffer.toISOString().split("T")[0] ?? "";
44
49
  for (const txn of ynabTransactions) {
45
50
  if (txn.date >= minDateStr && txn.date <= maxDateStr) {
46
51
  inRange.push(txn);
@@ -55,16 +60,16 @@ function mapToTransactionMatch(result) {
55
60
  const candidates = result.candidates.map((c) => ({
56
61
  ynab_transaction: c.ynabTransaction,
57
62
  confidence: c.scores.combined,
58
- match_reason: c.matchReasons.join(', '),
59
- explanation: c.matchReasons.join(', '),
63
+ match_reason: c.matchReasons.join(", "),
64
+ explanation: c.matchReasons.join(", "),
60
65
  }));
61
66
  const match = {
62
67
  bankTransaction: result.bankTransaction,
63
68
  candidates,
64
69
  confidence: result.confidence,
65
70
  confidenceScore: result.confidenceScore,
66
- matchReason: result.bestMatch?.matchReasons.join(', ') ?? 'No match found',
67
- actionHint: result.confidence === 'high' ? 'approve' : 'review',
71
+ matchReason: result.bestMatch?.matchReasons.join(", ") ?? "No match found",
72
+ actionHint: result.confidence === "high" ? "approve" : "review",
68
73
  };
69
74
  if (result.bestMatch) {
70
75
  match.ynabTransaction = result.bestMatch.ynabTransaction;
@@ -72,8 +77,9 @@ function mapToTransactionMatch(result) {
72
77
  if (result.candidates[0]) {
73
78
  match.topConfidence = result.candidates[0].scores.combined;
74
79
  }
75
- if (result.confidence === 'none') {
76
- match.recommendation = 'This bank transaction is not in YNAB. Consider adding it.';
80
+ if (result.confidence === "none") {
81
+ match.recommendation =
82
+ "This bank transaction is not in YNAB. Consider adding it.";
77
83
  }
78
84
  return match;
79
85
  }
@@ -82,7 +88,7 @@ function calculateBalances(ynabTransactions, statementBalanceDecimal, currency,
82
88
  let computedUncleared = 0;
83
89
  for (const txn of ynabTransactions) {
84
90
  const amount = txn.amount;
85
- if (txn.cleared === 'cleared' || txn.cleared === 'reconciled') {
91
+ if (txn.cleared === "cleared" || txn.cleared === "reconciled") {
86
92
  computedCleared += amount;
87
93
  }
88
94
  else {
@@ -105,11 +111,11 @@ function calculateBalances(ynabTransactions, statementBalanceDecimal, currency,
105
111
  }
106
112
  function generateSummary(bankTransactions, ynabTransactionsInRange, ynabTransactionsOutsideRange, autoMatches, suggestedMatches, unmatchedBank, unmatchedYNAB, balances) {
107
113
  const dates = bankTransactions.map((t) => t.date).sort();
108
- const dateRange = dates.length > 0 ? `${dates[0]} to ${dates[dates.length - 1]}` : 'Unknown';
114
+ const dateRange = dates.length > 0 ? `${dates[0]} to ${dates[dates.length - 1]}` : "Unknown";
109
115
  const totalYnabCount = ynabTransactionsInRange.length + ynabTransactionsOutsideRange.length;
110
- let discrepancyExplanation = '';
116
+ let discrepancyExplanation = "";
111
117
  if (balances.on_track) {
112
- discrepancyExplanation = 'Cleared balance matches statement';
118
+ discrepancyExplanation = "Cleared balance matches statement";
113
119
  }
114
120
  else {
115
121
  const actionsNeeded = [];
@@ -123,7 +129,9 @@ function generateSummary(bankTransactions, ynabTransactionsInRange, ynabTransact
123
129
  actionsNeeded.push(`review ${unmatchedYNAB.length} unmatched YNAB`);
124
130
  }
125
131
  discrepancyExplanation =
126
- actionsNeeded.length > 0 ? `Need to ${actionsNeeded.join(', ')}` : 'Manual review required';
132
+ actionsNeeded.length > 0
133
+ ? `Need to ${actionsNeeded.join(", ")}`
134
+ : "Manual review required";
127
135
  }
128
136
  return {
129
137
  statement_date_range: dateRange,
@@ -156,20 +164,20 @@ function generateNextSteps(summary) {
156
164
  steps.push(`Decide what to do with ${summary.unmatched_ynab} unmatched YNAB transactions (unclear/delete/ignore)`);
157
165
  }
158
166
  if (steps.length === 0) {
159
- steps.push('All transactions matched! Review and approve to complete reconciliation');
167
+ steps.push("All transactions matched! Review and approve to complete reconciliation");
160
168
  }
161
169
  return steps;
162
170
  }
163
- function formatCurrency(amountMilli, currency = 'USD') {
164
- const formatter = new Intl.NumberFormat('en-US', {
165
- style: 'currency',
171
+ function formatCurrency(amountMilli, currency = "USD") {
172
+ const formatter = new Intl.NumberFormat("en-US", {
173
+ style: "currency",
166
174
  currency: currency,
167
175
  minimumFractionDigits: 2,
168
176
  maximumFractionDigits: 2,
169
177
  });
170
178
  return formatter.format(amountMilli / 1000);
171
179
  }
172
- function repeatAmountInsights(unmatchedBank, currency = 'USD') {
180
+ function repeatAmountInsights(unmatchedBank, currency = "USD") {
173
181
  const insights = [];
174
182
  if (unmatchedBank.length === 0) {
175
183
  return insights;
@@ -188,13 +196,15 @@ function repeatAmountInsights(unmatchedBank, currency = 'USD') {
188
196
  return insights;
189
197
  }
190
198
  const top = repeated[0];
199
+ if (!top) {
200
+ return insights;
201
+ }
191
202
  insights.push({
192
203
  id: `repeat-${top.amount}`,
193
- type: 'repeat_amount',
194
- severity: top.txns.length >= 4 ? 'critical' : 'warning',
204
+ type: "repeat_amount",
205
+ severity: top.txns.length >= 4 ? "critical" : "warning",
195
206
  title: `${top.txns.length} unmatched transactions at ${formatCurrency(top.amount, currency)}`,
196
- description: `The bank statement shows ${top.txns.length} unmatched transaction(s) at ${formatCurrency(top.amount, currency)}. ` +
197
- 'Repeated amounts are usually the quickest wins — reconcile these first.',
207
+ description: `The bank statement shows ${top.txns.length} unmatched transaction(s) at ${formatCurrency(top.amount, currency)}. Repeated amounts are usually the quickest wins — reconcile these first.`,
198
208
  evidence: {
199
209
  amount: top.amount,
200
210
  occurrences: top.txns.length,
@@ -209,9 +219,9 @@ function anomalyInsights(balances) {
209
219
  const discrepancyAbs = Math.abs(balances.discrepancy.value_milliunits);
210
220
  if (discrepancyAbs >= 1000) {
211
221
  insights.push({
212
- id: 'balance-gap',
213
- type: 'anomaly',
214
- severity: discrepancyAbs >= 100000 ? 'critical' : 'warning',
222
+ id: "balance-gap",
223
+ type: "anomaly",
224
+ severity: discrepancyAbs >= 100000 ? "critical" : "warning",
215
225
  title: `Cleared balance off by ${balances.discrepancy.value_display}`,
216
226
  description: `YNAB cleared balance is ${balances.current_cleared.value_display} but the statement expects ` +
217
227
  `${balances.target_statement.value_display}. Focus on closing this gap.`,
@@ -235,14 +245,15 @@ function detectInsights(unmatchedBank, _summary, balances, currency, csvErrors =
235
245
  };
236
246
  if (csvErrors.length > 0) {
237
247
  addUnique({
238
- id: 'csv-parse-errors',
239
- type: 'anomaly',
240
- severity: csvErrors.length >= 5 ? 'critical' : 'warning',
248
+ id: "csv-parse-errors",
249
+ type: "anomaly",
250
+ severity: csvErrors.length >= 5 ? "critical" : "warning",
241
251
  title: `${csvErrors.length} CSV parsing error(s)`,
242
252
  description: csvErrors
243
253
  .slice(0, 3)
244
254
  .map((e) => `Row ${e.row}: ${e.message}`)
245
- .join('; ') + (csvErrors.length > 3 ? ` (+${csvErrors.length - 3} more)` : ''),
255
+ .join("; ") +
256
+ (csvErrors.length > 3 ? ` (+${csvErrors.length - 3} more)` : ""),
246
257
  evidence: {
247
258
  error_count: csvErrors.length,
248
259
  errors: csvErrors.slice(0, 5),
@@ -251,14 +262,15 @@ function detectInsights(unmatchedBank, _summary, balances, currency, csvErrors =
251
262
  }
252
263
  if (csvWarnings.length > 0) {
253
264
  addUnique({
254
- id: 'csv-parse-warnings',
255
- type: 'anomaly',
256
- severity: 'info',
265
+ id: "csv-parse-warnings",
266
+ type: "anomaly",
267
+ severity: "info",
257
268
  title: `${csvWarnings.length} CSV parsing warning(s)`,
258
269
  description: csvWarnings
259
270
  .slice(0, 3)
260
271
  .map((w) => `Row ${w.row}: ${w.message}`)
261
- .join('; ') + (csvWarnings.length > 3 ? ` (+${csvWarnings.length - 3} more)` : ''),
272
+ .join("; ") +
273
+ (csvWarnings.length > 3 ? ` (+${csvWarnings.length - 3} more)` : ""),
262
274
  evidence: {
263
275
  warning_count: csvWarnings.length,
264
276
  warnings: csvWarnings.slice(0, 5),
@@ -273,9 +285,9 @@ function detectInsights(unmatchedBank, _summary, balances, currency, csvErrors =
273
285
  }
274
286
  return insights.slice(0, 5);
275
287
  }
276
- export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTransactions, statementBalance, config = DEFAULT_CONFIG, currency = 'USD', accountId, budgetId, invertBankAmounts = false, csvOptions, accountSnapshot) {
288
+ export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTransactions, statementBalance, config = DEFAULT_CONFIG, currency = "USD", accountId, budgetId, invertBankAmounts = false, csvOptions, accountSnapshot) {
277
289
  let parseResult;
278
- if (typeof csvContentOrParsed === 'string') {
290
+ if (typeof csvContentOrParsed === "string") {
279
291
  parseResult = parseCSV(csvContentOrParsed, {
280
292
  ...csvOptions,
281
293
  invertAmounts: invertBankAmounts,
@@ -304,21 +316,23 @@ export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTran
304
316
  const normalizedConfig = normalizeConfig(config);
305
317
  const newMatches = findMatches(newBankTransactions, ynabInRange, normalizedConfig);
306
318
  const matches = newMatches.map(mapToTransactionMatch);
307
- const autoMatches = matches.filter((m) => m.confidence === 'high');
319
+ const autoMatches = matches.filter((m) => m.confidence === "high");
308
320
  const autoMatchedYnabIds = new Set();
309
- autoMatches.forEach((m) => {
310
- if (m.ynabTransaction)
311
- autoMatchedYnabIds.add(m.ynabTransaction.id);
312
- });
313
- const suggestedMatches = matches.filter((m) => m.confidence === 'medium' &&
321
+ for (const match of autoMatches) {
322
+ if (match.ynabTransaction) {
323
+ autoMatchedYnabIds.add(match.ynabTransaction.id);
324
+ }
325
+ }
326
+ const suggestedMatches = matches.filter((m) => m.confidence === "medium" &&
314
327
  (!m.ynabTransaction || !autoMatchedYnabIds.has(m.ynabTransaction.id)));
315
- const unmatchedBankMatches = matches.filter((m) => m.confidence === 'low' || m.confidence === 'none');
328
+ const unmatchedBankMatches = matches.filter((m) => m.confidence === "low" || m.confidence === "none");
316
329
  const unmatchedBank = unmatchedBankMatches.map((m) => m.bankTransaction);
317
330
  const matchedYnabIds = new Set();
318
- matches.forEach((m) => {
319
- if (m.ynabTransaction)
320
- matchedYnabIds.add(m.ynabTransaction.id);
321
- });
331
+ for (const match of matches) {
332
+ if (match.ynabTransaction) {
333
+ matchedYnabIds.add(match.ynabTransaction.id);
334
+ }
335
+ }
322
336
  const unmatchedYNAB = ynabInRange.filter((t) => !matchedYnabIds.has(t.id));
323
337
  const balances = calculateBalances(allYNABTransactions, statementBalance, currency, accountSnapshot);
324
338
  const summary = generateSummary(matches.map((m) => m.bankTransaction), ynabInRange, ynabOutsideRange, autoMatches, suggestedMatches, unmatchedBank, unmatchedYNAB, balances);
@@ -326,7 +340,7 @@ export function analyzeReconciliation(csvContentOrParsed, _csvFilePath, ynabTran
326
340
  const insights = detectInsights(unmatchedBank, summary, balances, currency, csvParseErrors, csvParseWarnings);
327
341
  const analysis = {
328
342
  success: true,
329
- phase: 'analysis',
343
+ phase: "analysis",
330
344
  summary,
331
345
  auto_matches: autoMatches,
332
346
  suggested_matches: suggestedMatches,
@@ -1,4 +1,4 @@
1
- import type { BankTransaction } from '../../types/reconciliation.js';
1
+ import type { BankTransaction } from "../../types/reconciliation.js";
2
2
  export interface CSVParseResult {
3
3
  transactions: BankTransaction[];
4
4
  errors: ParseError[];
@@ -29,7 +29,7 @@ export interface BankPreset {
29
29
  creditColumn?: string;
30
30
  descriptionColumn: string | string[];
31
31
  amountMultiplier?: number;
32
- dateFormat?: 'YMD' | 'MDY' | 'DMY';
32
+ dateFormat?: "YMD" | "MDY" | "DMY";
33
33
  header?: boolean;
34
34
  }
35
35
  export declare const BANK_PRESETS: Record<string, BankPreset>;
@@ -46,7 +46,7 @@ export interface ParseCSVOptions {
46
46
  credit?: string;
47
47
  description?: string;
48
48
  };
49
- dateFormat?: 'YMD' | 'MDY' | 'DMY';
49
+ dateFormat?: "YMD" | "MDY" | "DMY";
50
50
  header?: boolean;
51
51
  maxRows?: number;
52
52
  maxBytes?: number;