@dizzlkheinz/ynab-mcpb 0.18.3 → 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 (346) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/CLAUDE.md +87 -8
  3. package/bin/ynab-mcp-server.cjs +2 -2
  4. package/bin/ynab-mcp-server.js +3 -3
  5. package/biome.json +39 -0
  6. package/dist/bundle/index.cjs +67 -67
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.js +27 -27
  9. package/dist/server/YNABMCPServer.d.ts +3 -4
  10. package/dist/server/YNABMCPServer.js +111 -116
  11. package/dist/server/budgetResolver.d.ts +6 -5
  12. package/dist/server/budgetResolver.js +46 -36
  13. package/dist/server/cacheKeys.js +6 -6
  14. package/dist/server/cacheManager.js +14 -11
  15. package/dist/server/completions.d.ts +2 -2
  16. package/dist/server/completions.js +20 -15
  17. package/dist/server/config.d.ts +10 -5
  18. package/dist/server/config.js +24 -7
  19. package/dist/server/deltaCache.d.ts +2 -2
  20. package/dist/server/deltaCache.js +22 -16
  21. package/dist/server/deltaCache.merge.d.ts +2 -2
  22. package/dist/server/diagnostics.d.ts +4 -4
  23. package/dist/server/diagnostics.js +38 -32
  24. package/dist/server/errorHandler.d.ts +5 -12
  25. package/dist/server/errorHandler.js +219 -217
  26. package/dist/server/prompts.d.ts +2 -2
  27. package/dist/server/prompts.js +45 -45
  28. package/dist/server/rateLimiter.js +4 -4
  29. package/dist/server/requestLogger.d.ts +1 -1
  30. package/dist/server/requestLogger.js +40 -35
  31. package/dist/server/resources.d.ts +3 -3
  32. package/dist/server/resources.js +55 -52
  33. package/dist/server/responseFormatter.js +6 -6
  34. package/dist/server/securityMiddleware.d.ts +2 -2
  35. package/dist/server/securityMiddleware.js +22 -20
  36. package/dist/server/serverKnowledgeStore.js +1 -1
  37. package/dist/server/toolRegistry.d.ts +3 -3
  38. package/dist/server/toolRegistry.js +47 -40
  39. package/dist/tools/__tests__/deltaTestUtils.d.ts +3 -3
  40. package/dist/tools/__tests__/deltaTestUtils.js +2 -2
  41. package/dist/tools/accountTools.d.ts +9 -8
  42. package/dist/tools/accountTools.js +47 -47
  43. package/dist/tools/adapters.d.ts +13 -8
  44. package/dist/tools/adapters.js +21 -11
  45. package/dist/tools/budgetTools.d.ts +8 -7
  46. package/dist/tools/budgetTools.js +22 -22
  47. package/dist/tools/categoryTools.d.ts +9 -8
  48. package/dist/tools/categoryTools.js +68 -59
  49. package/dist/tools/compareTransactions/formatter.d.ts +3 -3
  50. package/dist/tools/compareTransactions/formatter.js +9 -9
  51. package/dist/tools/compareTransactions/index.d.ts +6 -6
  52. package/dist/tools/compareTransactions/index.js +58 -43
  53. package/dist/tools/compareTransactions/matcher.d.ts +1 -1
  54. package/dist/tools/compareTransactions/matcher.js +28 -15
  55. package/dist/tools/compareTransactions/parser.d.ts +2 -2
  56. package/dist/tools/compareTransactions/parser.js +144 -138
  57. package/dist/tools/compareTransactions/types.d.ts +4 -4
  58. package/dist/tools/compareTransactions.d.ts +1 -1
  59. package/dist/tools/compareTransactions.js +1 -1
  60. package/dist/tools/deltaFetcher.d.ts +2 -2
  61. package/dist/tools/deltaFetcher.js +16 -15
  62. package/dist/tools/deltaSupport.d.ts +4 -4
  63. package/dist/tools/deltaSupport.js +35 -41
  64. package/dist/tools/exportTransactions.d.ts +5 -4
  65. package/dist/tools/exportTransactions.js +61 -59
  66. package/dist/tools/monthTools.d.ts +7 -6
  67. package/dist/tools/monthTools.js +31 -29
  68. package/dist/tools/payeeTools.d.ts +7 -6
  69. package/dist/tools/payeeTools.js +28 -28
  70. package/dist/tools/reconcileAdapter.d.ts +2 -2
  71. package/dist/tools/reconcileAdapter.js +21 -11
  72. package/dist/tools/reconciliation/analyzer.d.ts +4 -4
  73. package/dist/tools/reconciliation/analyzer.js +136 -57
  74. package/dist/tools/reconciliation/csvParser.d.ts +3 -3
  75. package/dist/tools/reconciliation/csvParser.js +128 -104
  76. package/dist/tools/reconciliation/executor.d.ts +4 -4
  77. package/dist/tools/reconciliation/executor.js +148 -109
  78. package/dist/tools/reconciliation/index.d.ts +10 -10
  79. package/dist/tools/reconciliation/index.js +96 -83
  80. package/dist/tools/reconciliation/matcher.d.ts +3 -3
  81. package/dist/tools/reconciliation/matcher.js +17 -16
  82. package/dist/tools/reconciliation/payeeNormalizer.js +19 -8
  83. package/dist/tools/reconciliation/recommendationEngine.d.ts +1 -1
  84. package/dist/tools/reconciliation/recommendationEngine.js +40 -40
  85. package/dist/tools/reconciliation/reportFormatter.d.ts +2 -2
  86. package/dist/tools/reconciliation/reportFormatter.js +79 -54
  87. package/dist/tools/reconciliation/signDetector.d.ts +1 -1
  88. package/dist/tools/reconciliation/types.d.ts +19 -16
  89. package/dist/tools/reconciliation/ynabAdapter.d.ts +2 -2
  90. package/dist/tools/schemas/common.d.ts +1 -1
  91. package/dist/tools/schemas/common.js +1 -1
  92. package/dist/tools/schemas/outputs/accountOutputs.d.ts +1 -1
  93. package/dist/tools/schemas/outputs/accountOutputs.js +24 -18
  94. package/dist/tools/schemas/outputs/budgetOutputs.d.ts +1 -1
  95. package/dist/tools/schemas/outputs/budgetOutputs.js +14 -11
  96. package/dist/tools/schemas/outputs/categoryOutputs.d.ts +1 -1
  97. package/dist/tools/schemas/outputs/categoryOutputs.js +49 -29
  98. package/dist/tools/schemas/outputs/comparisonOutputs.d.ts +1 -1
  99. package/dist/tools/schemas/outputs/comparisonOutputs.js +12 -12
  100. package/dist/tools/schemas/outputs/index.d.ts +14 -14
  101. package/dist/tools/schemas/outputs/index.js +14 -14
  102. package/dist/tools/schemas/outputs/monthOutputs.d.ts +1 -1
  103. package/dist/tools/schemas/outputs/monthOutputs.js +56 -41
  104. package/dist/tools/schemas/outputs/payeeOutputs.d.ts +1 -1
  105. package/dist/tools/schemas/outputs/payeeOutputs.js +10 -10
  106. package/dist/tools/schemas/outputs/reconciliationOutputs.d.ts +2 -2
  107. package/dist/tools/schemas/outputs/reconciliationOutputs.js +45 -45
  108. package/dist/tools/schemas/outputs/transactionMutationOutputs.d.ts +1 -1
  109. package/dist/tools/schemas/outputs/transactionMutationOutputs.js +28 -22
  110. package/dist/tools/schemas/outputs/transactionOutputs.d.ts +1 -1
  111. package/dist/tools/schemas/outputs/transactionOutputs.js +43 -35
  112. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +1 -1
  113. package/dist/tools/schemas/outputs/utilityOutputs.js +5 -3
  114. package/dist/tools/schemas/shared/commonOutputs.d.ts +1 -1
  115. package/dist/tools/schemas/shared/commonOutputs.js +15 -9
  116. package/dist/tools/transactionReadTools.d.ts +11 -0
  117. package/dist/tools/transactionReadTools.js +202 -0
  118. package/dist/tools/transactionSchemas.d.ts +309 -0
  119. package/dist/tools/transactionSchemas.js +235 -0
  120. package/dist/tools/transactionTools.d.ts +6 -302
  121. package/dist/tools/transactionTools.js +7 -2054
  122. package/dist/tools/transactionUtils.d.ts +31 -0
  123. package/dist/tools/transactionUtils.js +364 -0
  124. package/dist/tools/transactionWriteTools.d.ts +20 -0
  125. package/dist/tools/transactionWriteTools.js +1342 -0
  126. package/dist/tools/utilityTools.d.ts +5 -4
  127. package/dist/tools/utilityTools.js +11 -11
  128. package/dist/types/index.d.ts +7 -7
  129. package/dist/types/index.js +6 -6
  130. package/dist/types/reconciliation.d.ts +1 -1
  131. package/dist/types/toolRegistration.d.ts +14 -12
  132. package/dist/utils/amountUtils.js +1 -1
  133. package/dist/utils/dateUtils.js +4 -4
  134. package/dist/utils/errors.d.ts +3 -3
  135. package/dist/utils/errors.js +4 -4
  136. package/dist/utils/money.d.ts +2 -2
  137. package/dist/utils/money.js +8 -8
  138. package/dist/utils/validationError.d.ts +1 -1
  139. package/dist/utils/validationError.js +1 -1
  140. package/docs/assets/examples/reconciliation-with-recommendations.json +66 -66
  141. package/docs/assets/schemas/reconciliation-v2.json +360 -336
  142. package/docs/plans/2025-12-25-transaction-tools-refactor-design.md +211 -0
  143. package/docs/plans/2025-12-25-transaction-tools-refactor.md +905 -0
  144. package/esbuild.config.mjs +53 -50
  145. package/meta.json +12548 -12548
  146. package/package.json +98 -109
  147. package/scripts/analyze-bundle.mjs +33 -30
  148. package/scripts/create-pr-description.js +169 -120
  149. package/scripts/run-all-tests.js +205 -0
  150. package/scripts/run-domain-integration-tests.js +28 -18
  151. package/scripts/run-generate-mcpb.js +19 -17
  152. package/scripts/run-throttled-integration-tests.js +92 -83
  153. package/scripts/test-delta-params.mjs +149 -120
  154. package/scripts/test-recommendations.ts +36 -32
  155. package/scripts/tmpTransaction.ts +80 -43
  156. package/scripts/validate-env.js +98 -91
  157. package/scripts/verify-build.js +78 -76
  158. package/src/__tests__/comprehensive.integration.test.ts +1281 -1154
  159. package/src/__tests__/performance.test.ts +723 -671
  160. package/src/__tests__/setup.ts +442 -395
  161. package/src/__tests__/smoke.e2e.test.ts +41 -39
  162. package/src/__tests__/testRunner.ts +314 -295
  163. package/src/__tests__/testUtils.ts +456 -364
  164. package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +109 -107
  165. package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +41 -41
  166. package/src/index.ts +68 -59
  167. package/src/server/CLAUDE.md +480 -0
  168. package/src/server/YNABMCPServer.ts +821 -794
  169. package/src/server/__tests__/YNABMCPServer.integration.test.ts +929 -893
  170. package/src/server/__tests__/YNABMCPServer.test.ts +903 -899
  171. package/src/server/__tests__/budgetResolver.test.ts +466 -423
  172. package/src/server/__tests__/cacheManager.test.ts +891 -874
  173. package/src/server/__tests__/completions.integration.test.ts +115 -106
  174. package/src/server/__tests__/completions.test.ts +334 -313
  175. package/src/server/__tests__/config.test.ts +98 -86
  176. package/src/server/__tests__/deltaCache.merge.test.ts +774 -703
  177. package/src/server/__tests__/deltaCache.swr.test.ts +198 -153
  178. package/src/server/__tests__/deltaCache.test.ts +946 -759
  179. package/src/server/__tests__/diagnostics.test.ts +825 -792
  180. package/src/server/__tests__/errorHandler.integration.test.ts +512 -462
  181. package/src/server/__tests__/errorHandler.test.ts +402 -397
  182. package/src/server/__tests__/prompts.test.ts +424 -347
  183. package/src/server/__tests__/rateLimiter.test.ts +313 -309
  184. package/src/server/__tests__/requestLogger.test.ts +443 -403
  185. package/src/server/__tests__/resources.template.test.ts +196 -185
  186. package/src/server/__tests__/resources.test.ts +294 -288
  187. package/src/server/__tests__/security.integration.test.ts +487 -421
  188. package/src/server/__tests__/securityMiddleware.test.ts +519 -444
  189. package/src/server/__tests__/server-startup.integration.test.ts +509 -490
  190. package/src/server/__tests__/serverKnowledgeStore.test.ts +174 -173
  191. package/src/server/__tests__/toolRegistration.test.ts +239 -210
  192. package/src/server/__tests__/toolRegistry.test.ts +907 -845
  193. package/src/server/budgetResolver.ts +221 -181
  194. package/src/server/cacheKeys.ts +6 -6
  195. package/src/server/cacheManager.ts +498 -484
  196. package/src/server/completions.ts +267 -243
  197. package/src/server/config.ts +35 -14
  198. package/src/server/deltaCache.merge.ts +146 -128
  199. package/src/server/deltaCache.ts +352 -309
  200. package/src/server/diagnostics.ts +257 -242
  201. package/src/server/errorHandler.ts +747 -744
  202. package/src/server/prompts.ts +181 -176
  203. package/src/server/rateLimiter.ts +131 -129
  204. package/src/server/requestLogger.ts +350 -322
  205. package/src/server/resources.ts +442 -374
  206. package/src/server/responseFormatter.ts +41 -37
  207. package/src/server/securityMiddleware.ts +223 -205
  208. package/src/server/serverKnowledgeStore.ts +67 -67
  209. package/src/server/toolRegistry.ts +508 -474
  210. package/src/tools/CLAUDE.md +604 -0
  211. package/src/tools/__tests__/accountTools.delta.integration.test.ts +128 -111
  212. package/src/tools/__tests__/accountTools.integration.test.ts +129 -111
  213. package/src/tools/__tests__/accountTools.test.ts +685 -638
  214. package/src/tools/__tests__/adapters.test.ts +142 -108
  215. package/src/tools/__tests__/budgetTools.delta.integration.test.ts +73 -73
  216. package/src/tools/__tests__/budgetTools.integration.test.ts +132 -124
  217. package/src/tools/__tests__/budgetTools.test.ts +442 -413
  218. package/src/tools/__tests__/categoryTools.delta.integration.test.ts +76 -68
  219. package/src/tools/__tests__/categoryTools.integration.test.ts +314 -288
  220. package/src/tools/__tests__/categoryTools.test.ts +656 -625
  221. package/src/tools/__tests__/compareTransactions/formatter.test.ts +535 -462
  222. package/src/tools/__tests__/compareTransactions/index.test.ts +378 -358
  223. package/src/tools/__tests__/compareTransactions/matcher.test.ts +497 -398
  224. package/src/tools/__tests__/compareTransactions/parser.test.ts +765 -747
  225. package/src/tools/__tests__/compareTransactions.test.ts +352 -332
  226. package/src/tools/__tests__/compareTransactions.window.test.ts +150 -146
  227. package/src/tools/__tests__/deltaFetcher.scheduled.integration.test.ts +69 -65
  228. package/src/tools/__tests__/deltaFetcher.test.ts +325 -265
  229. package/src/tools/__tests__/deltaSupport.test.ts +211 -184
  230. package/src/tools/__tests__/deltaTestUtils.ts +37 -33
  231. package/src/tools/__tests__/exportTransactions.test.ts +205 -200
  232. package/src/tools/__tests__/monthTools.delta.integration.test.ts +68 -68
  233. package/src/tools/__tests__/monthTools.integration.test.ts +178 -166
  234. package/src/tools/__tests__/monthTools.test.ts +561 -512
  235. package/src/tools/__tests__/payeeTools.delta.integration.test.ts +68 -68
  236. package/src/tools/__tests__/payeeTools.integration.test.ts +158 -142
  237. package/src/tools/__tests__/payeeTools.test.ts +486 -434
  238. package/src/tools/__tests__/transactionSchemas.test.ts +1204 -0
  239. package/src/tools/__tests__/transactionTools.integration.test.ts +875 -825
  240. package/src/tools/__tests__/transactionTools.test.ts +4923 -4366
  241. package/src/tools/__tests__/transactionUtils.test.ts +1016 -0
  242. package/src/tools/__tests__/utilityTools.integration.test.ts +32 -32
  243. package/src/tools/__tests__/utilityTools.test.ts +68 -58
  244. package/src/tools/accountTools.ts +293 -271
  245. package/src/tools/adapters.ts +120 -63
  246. package/src/tools/budgetTools.ts +121 -116
  247. package/src/tools/categoryTools.ts +379 -339
  248. package/src/tools/compareTransactions/formatter.ts +131 -119
  249. package/src/tools/compareTransactions/index.ts +249 -214
  250. package/src/tools/compareTransactions/matcher.ts +259 -209
  251. package/src/tools/compareTransactions/parser.ts +517 -487
  252. package/src/tools/compareTransactions/types.ts +38 -38
  253. package/src/tools/compareTransactions.ts +1 -1
  254. package/src/tools/deltaFetcher.ts +281 -260
  255. package/src/tools/deltaSupport.ts +264 -259
  256. package/src/tools/exportTransactions.ts +230 -218
  257. package/src/tools/monthTools.ts +180 -165
  258. package/src/tools/payeeTools.ts +152 -140
  259. package/src/tools/reconcileAdapter.ts +297 -246
  260. package/src/tools/reconciliation/CLAUDE.md +506 -0
  261. package/src/tools/reconciliation/__tests__/adapter.causes.test.ts +135 -112
  262. package/src/tools/reconciliation/__tests__/adapter.test.ts +249 -227
  263. package/src/tools/reconciliation/__tests__/analyzer.test.ts +408 -335
  264. package/src/tools/reconciliation/__tests__/csvParser.test.ts +71 -69
  265. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +348 -323
  266. package/src/tools/reconciliation/__tests__/executor.progress.test.ts +503 -457
  267. package/src/tools/reconciliation/__tests__/executor.test.ts +898 -831
  268. package/src/tools/reconciliation/__tests__/matcher.test.ts +667 -663
  269. package/src/tools/reconciliation/__tests__/payeeNormalizer.test.ts +296 -276
  270. package/src/tools/reconciliation/__tests__/recommendationEngine.integration.test.ts +692 -624
  271. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +1008 -986
  272. package/src/tools/reconciliation/__tests__/reconciliation.delta.integration.test.ts +187 -146
  273. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +583 -530
  274. package/src/tools/reconciliation/__tests__/scenarios/adapterCurrency.scenario.test.ts +75 -71
  275. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +70 -58
  276. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +102 -88
  277. package/src/tools/reconciliation/__tests__/schemaUrl.test.ts +58 -43
  278. package/src/tools/reconciliation/__tests__/signDetector.test.ts +209 -206
  279. package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +66 -60
  280. package/src/tools/reconciliation/analyzer.ts +582 -406
  281. package/src/tools/reconciliation/csvParser.ts +656 -609
  282. package/src/tools/reconciliation/executor.ts +1290 -1128
  283. package/src/tools/reconciliation/index.ts +580 -528
  284. package/src/tools/reconciliation/matcher.ts +256 -240
  285. package/src/tools/reconciliation/payeeNormalizer.ts +92 -78
  286. package/src/tools/reconciliation/recommendationEngine.ts +357 -345
  287. package/src/tools/reconciliation/reportFormatter.ts +349 -276
  288. package/src/tools/reconciliation/signDetector.ts +89 -83
  289. package/src/tools/reconciliation/types.ts +164 -153
  290. package/src/tools/reconciliation/ynabAdapter.ts +17 -15
  291. package/src/tools/schemas/CLAUDE.md +546 -0
  292. package/src/tools/schemas/common.ts +1 -1
  293. package/src/tools/schemas/outputs/__tests__/accountOutputs.test.ts +410 -409
  294. package/src/tools/schemas/outputs/__tests__/budgetOutputs.test.ts +305 -299
  295. package/src/tools/schemas/outputs/__tests__/categoryOutputs.test.ts +431 -430
  296. package/src/tools/schemas/outputs/__tests__/comparisonOutputs.test.ts +510 -495
  297. package/src/tools/schemas/outputs/__tests__/dateValidation.test.ts +179 -153
  298. package/src/tools/schemas/outputs/__tests__/discrepancyDirection.test.ts +293 -254
  299. package/src/tools/schemas/outputs/__tests__/monthOutputs.test.ts +457 -457
  300. package/src/tools/schemas/outputs/__tests__/payeeOutputs.test.ts +362 -356
  301. package/src/tools/schemas/outputs/__tests__/reconciliationOutputs.test.ts +402 -399
  302. package/src/tools/schemas/outputs/__tests__/transactionMutationSchemas.test.ts +225 -211
  303. package/src/tools/schemas/outputs/__tests__/transactionOutputs.test.ts +457 -454
  304. package/src/tools/schemas/outputs/__tests__/utilityOutputs.test.ts +316 -315
  305. package/src/tools/schemas/outputs/accountOutputs.ts +40 -34
  306. package/src/tools/schemas/outputs/budgetOutputs.ts +24 -19
  307. package/src/tools/schemas/outputs/categoryOutputs.ts +76 -56
  308. package/src/tools/schemas/outputs/comparisonOutputs.ts +192 -169
  309. package/src/tools/schemas/outputs/index.ts +163 -163
  310. package/src/tools/schemas/outputs/monthOutputs.ts +95 -80
  311. package/src/tools/schemas/outputs/payeeOutputs.ts +18 -18
  312. package/src/tools/schemas/outputs/reconciliationOutputs.ts +386 -373
  313. package/src/tools/schemas/outputs/transactionMutationOutputs.ts +259 -231
  314. package/src/tools/schemas/outputs/transactionOutputs.ts +81 -71
  315. package/src/tools/schemas/outputs/utilityOutputs.ts +90 -84
  316. package/src/tools/schemas/shared/commonOutputs.ts +27 -19
  317. package/src/tools/toolCategories.ts +114 -114
  318. package/src/tools/transactionReadTools.ts +327 -0
  319. package/src/tools/transactionSchemas.ts +484 -0
  320. package/src/tools/transactionTools.ts +107 -2990
  321. package/src/tools/transactionUtils.ts +621 -0
  322. package/src/tools/transactionWriteTools.ts +2110 -0
  323. package/src/tools/utilityTools.ts +46 -41
  324. package/src/types/CLAUDE.md +477 -0
  325. package/src/types/__tests__/index.test.ts +51 -51
  326. package/src/types/index.ts +43 -39
  327. package/src/types/integration-tests.d.ts +26 -26
  328. package/src/types/reconciliation.ts +29 -29
  329. package/src/types/toolAnnotations.ts +30 -30
  330. package/src/types/toolRegistration.ts +43 -32
  331. package/src/utils/CLAUDE.md +508 -0
  332. package/src/utils/__tests__/dateUtils.test.ts +174 -168
  333. package/src/utils/__tests__/money.test.ts +193 -187
  334. package/src/utils/amountUtils.ts +5 -5
  335. package/src/utils/baseError.ts +5 -5
  336. package/src/utils/dateUtils.ts +29 -26
  337. package/src/utils/errors.ts +14 -14
  338. package/src/utils/money.ts +66 -52
  339. package/src/utils/validationError.ts +1 -1
  340. package/tsconfig.json +29 -29
  341. package/tsconfig.prod.json +16 -16
  342. package/vitest-reporters/split-json-reporter.ts +247 -204
  343. package/vitest.config.ts +99 -95
  344. package/.prettierignore +0 -10
  345. package/.prettierrc.json +0 -10
  346. package/eslint.config.js +0 -49
@@ -1,6 +1,6 @@
1
- import { randomUUID } from 'crypto';
2
- import { toMoneyValue, toMoneyValueFromDecimal, fromMilli } from '../../utils/money.js';
3
- const RECOMMENDATION_VERSION = '1.0';
1
+ import { randomUUID } from "node:crypto";
2
+ import { fromMilli, toMoneyValue, toMoneyValueFromDecimal, } from "../../utils/money.js";
3
+ const RECOMMENDATION_VERSION = "1.0";
4
4
  const CONFIDENCE = {
5
5
  CREATE_EXACT_MATCH: 0.95,
6
6
  NEAR_MATCH_REVIEW: 0.7,
@@ -22,11 +22,11 @@ export function generateRecommendations(context) {
22
22
  }
23
23
  function processInsight(insight, context) {
24
24
  switch (insight.type) {
25
- case 'near_match':
25
+ case "near_match":
26
26
  return [createNearMatchRecommendation(insight, context)];
27
- case 'repeat_amount':
27
+ case "repeat_amount":
28
28
  return createRepeatAmountRecommendations(insight, context);
29
- case 'anomaly':
29
+ case "anomaly":
30
30
  return [createManualReviewRecommendation(insight, context)];
31
31
  default:
32
32
  return [];
@@ -34,11 +34,11 @@ function processInsight(insight, context) {
34
34
  }
35
35
  function createSuggestedMatchRecommendation(match, context) {
36
36
  const bankTxn = match.bankTransaction;
37
- if (match.ynabTransaction && match.confidence !== 'none') {
37
+ if (match.ynabTransaction && match.confidence !== "none") {
38
38
  return {
39
39
  id: randomUUID(),
40
- action_type: 'review_duplicate',
41
- priority: 'high',
40
+ action_type: "review_duplicate",
41
+ priority: "high",
42
42
  confidence: Math.max(0, Math.min(1, match.confidenceScore / 100)),
43
43
  message: `Review possible match: ${bankTxn.payee}`,
44
44
  reason: match.matchReason,
@@ -64,7 +64,7 @@ function createSuggestedMatchRecommendation(match, context) {
64
64
  date: bankTxn.date,
65
65
  amount: bankTxn.amount,
66
66
  payee_name: bankTxn.payee,
67
- cleared: 'cleared',
67
+ cleared: "cleared",
68
68
  approved: true,
69
69
  };
70
70
  if (bankTxn.memo) {
@@ -72,11 +72,11 @@ function createSuggestedMatchRecommendation(match, context) {
72
72
  }
73
73
  return {
74
74
  id: randomUUID(),
75
- action_type: 'create_transaction',
76
- priority: 'high',
75
+ action_type: "create_transaction",
76
+ priority: "high",
77
77
  confidence: CONFIDENCE.CREATE_EXACT_MATCH,
78
78
  message: `Create transaction for ${bankTxn.payee}`,
79
- reason: `This transaction exactly matches your discrepancy`,
79
+ reason: "This transaction exactly matches your discrepancy",
80
80
  estimated_impact: toMoneyValue(bankTxn.amount, context.analysis.balance_info.current_cleared.currency),
81
81
  account_id: context.account_id,
82
82
  metadata: {
@@ -99,12 +99,12 @@ function createCombinationReviewRecommendation(match, context) {
99
99
  }, 0) ?? 0;
100
100
  return {
101
101
  id: randomUUID(),
102
- action_type: 'manual_review',
103
- priority: 'medium',
102
+ action_type: "manual_review",
103
+ priority: "medium",
104
104
  confidence: CONFIDENCE.NEAR_MATCH_REVIEW,
105
105
  message: `Review combination match: ${bankTxn.payee}`,
106
106
  reason: match.recommendation ??
107
- 'Multiple YNAB transactions appear to match this bank transaction. Review before creating anything new.',
107
+ "Multiple YNAB transactions appear to match this bank transaction. Review before creating anything new.",
108
108
  estimated_impact: toMoneyValueFromDecimal(0, context.analysis.balance_info.current_cleared.currency),
109
109
  account_id: context.account_id,
110
110
  metadata: {
@@ -115,18 +115,18 @@ function createCombinationReviewRecommendation(match, context) {
115
115
  candidate_count: match.candidates?.length ?? 0,
116
116
  },
117
117
  parameters: {
118
- issue_type: 'complex_match',
118
+ issue_type: "complex_match",
119
119
  related_transactions: [
120
120
  {
121
- source: 'bank',
121
+ source: "bank",
122
122
  id: bankTxn.id,
123
123
  description: bankTxn.payee,
124
124
  },
125
125
  ...candidateIds.map((id) => ({
126
- source: 'ynab',
126
+ source: "ynab",
127
127
  id,
128
- description: match.candidates?.find((c) => c.ynab_transaction.id === id)?.ynab_transaction.payee ??
129
- 'Unknown',
128
+ description: match.candidates?.find((c) => c.ynab_transaction.id === id)
129
+ ?.ynab_transaction.payee ?? "Unknown",
130
130
  })),
131
131
  ],
132
132
  },
@@ -135,8 +135,8 @@ function createCombinationReviewRecommendation(match, context) {
135
135
  function createNearMatchRecommendation(insight, context) {
136
136
  return {
137
137
  id: randomUUID(),
138
- action_type: 'manual_review',
139
- priority: 'medium',
138
+ action_type: "manual_review",
139
+ priority: "medium",
140
140
  confidence: CONFIDENCE.NEAR_MATCH_REVIEW,
141
141
  message: `Review: ${insight.title}`,
142
142
  reason: insight.description,
@@ -150,7 +150,7 @@ function createNearMatchRecommendation(insight, context) {
150
150
  insight_severity: insight.severity,
151
151
  },
152
152
  parameters: {
153
- issue_type: 'complex_match',
153
+ issue_type: "complex_match",
154
154
  },
155
155
  };
156
156
  }
@@ -158,8 +158,8 @@ function createRepeatAmountRecommendations(insight, context) {
158
158
  return [
159
159
  {
160
160
  id: randomUUID(),
161
- action_type: 'manual_review',
162
- priority: 'medium',
161
+ action_type: "manual_review",
162
+ priority: "medium",
163
163
  confidence: CONFIDENCE.REPEAT_AMOUNT,
164
164
  message: `Review recurring pattern: ${insight.title}`,
165
165
  reason: insight.description,
@@ -173,7 +173,7 @@ function createRepeatAmountRecommendations(insight, context) {
173
173
  insight_severity: insight.severity,
174
174
  },
175
175
  parameters: {
176
- issue_type: 'complex_match',
176
+ issue_type: "complex_match",
177
177
  },
178
178
  },
179
179
  ];
@@ -181,8 +181,8 @@ function createRepeatAmountRecommendations(insight, context) {
181
181
  function createManualReviewRecommendation(insight, context) {
182
182
  return {
183
183
  id: randomUUID(),
184
- action_type: 'manual_review',
185
- priority: 'low',
184
+ action_type: "manual_review",
185
+ priority: "low",
186
186
  confidence: CONFIDENCE.ANOMALY_REVIEW,
187
187
  message: `Review: ${insight.title}`,
188
188
  reason: insight.description,
@@ -196,7 +196,7 @@ function createManualReviewRecommendation(insight, context) {
196
196
  insight_severity: insight.severity,
197
197
  },
198
198
  parameters: {
199
- issue_type: insight.severity === 'critical' ? 'large_discrepancy' : 'unknown',
199
+ issue_type: insight.severity === "critical" ? "large_discrepancy" : "unknown",
200
200
  },
201
201
  };
202
202
  }
@@ -213,7 +213,7 @@ function processUnmatchedTransactions(context) {
213
213
  recommendations.push(createSuggestedMatchRecommendation(match, context));
214
214
  }
215
215
  for (const ynabTxn of context.analysis.unmatched_ynab) {
216
- if (ynabTxn.cleared === 'uncleared') {
216
+ if (ynabTxn.cleared === "uncleared") {
217
217
  recommendations.push(createUpdateClearedRecommendation(ynabTxn, context));
218
218
  }
219
219
  }
@@ -225,7 +225,7 @@ function createUnmatchedBankRecommendation(txn, context) {
225
225
  date: txn.date,
226
226
  amount: txn.amount,
227
227
  payee_name: txn.payee,
228
- cleared: 'cleared',
228
+ cleared: "cleared",
229
229
  approved: true,
230
230
  };
231
231
  if (txn.memo) {
@@ -233,11 +233,11 @@ function createUnmatchedBankRecommendation(txn, context) {
233
233
  }
234
234
  return {
235
235
  id: randomUUID(),
236
- action_type: 'create_transaction',
237
- priority: 'medium',
236
+ action_type: "create_transaction",
237
+ priority: "medium",
238
238
  confidence: CONFIDENCE.UNMATCHED_BANK,
239
239
  message: `Create missing transaction: ${txn.payee}`,
240
- reason: 'Transaction appears on bank statement but not in YNAB',
240
+ reason: "Transaction appears on bank statement but not in YNAB",
241
241
  estimated_impact: toMoneyValue(txn.amount, context.analysis.balance_info.current_cleared.currency),
242
242
  account_id: context.account_id,
243
243
  metadata: {
@@ -250,11 +250,11 @@ function createUnmatchedBankRecommendation(txn, context) {
250
250
  function createUpdateClearedRecommendation(txn, context) {
251
251
  return {
252
252
  id: randomUUID(),
253
- action_type: 'update_cleared',
254
- priority: 'low',
253
+ action_type: "update_cleared",
254
+ priority: "low",
255
255
  confidence: CONFIDENCE.UPDATE_CLEARED,
256
- message: `Mark transaction as cleared: ${txn.payee || 'Unknown'}`,
257
- reason: 'Transaction exists in YNAB but not yet cleared',
256
+ message: `Mark transaction as cleared: ${txn.payee || "Unknown"}`,
257
+ reason: "Transaction exists in YNAB but not yet cleared",
258
258
  estimated_impact: toMoneyValueFromDecimal(0, context.analysis.balance_info.current_cleared.currency),
259
259
  account_id: context.account_id,
260
260
  metadata: {
@@ -263,7 +263,7 @@ function createUpdateClearedRecommendation(txn, context) {
263
263
  },
264
264
  parameters: {
265
265
  transaction_id: txn.id,
266
- cleared: 'cleared',
266
+ cleared: "cleared",
267
267
  },
268
268
  };
269
269
  }
@@ -1,5 +1,5 @@
1
- import type { ReconciliationAnalysis, BankTransaction, YNABTransaction, BalanceInfo } from './types.js';
2
- import type { LegacyReconciliationResult } from './executor.js';
1
+ import type { LegacyReconciliationResult } from "./executor.js";
2
+ import type { BalanceInfo, BankTransaction, ReconciliationAnalysis, YNABTransaction } from "./types.js";
3
3
  export interface ReportFormatterOptions {
4
4
  accountName?: string | undefined;
5
5
  accountId?: string | undefined;
@@ -1,6 +1,6 @@
1
- const SECTION_DIVIDER = '-'.repeat(60);
1
+ const SECTION_DIVIDER = "-".repeat(60);
2
2
  export function formatHumanReadableReport(analysis, options = {}, execution) {
3
- const accountLabel = options.accountName ?? 'Account';
3
+ const accountLabel = options.accountName ?? "Account";
4
4
  const sections = [];
5
5
  sections.push(formatHeader(accountLabel, analysis));
6
6
  if (options.notes && options.notes.length > 0) {
@@ -15,57 +15,64 @@ export function formatHumanReadableReport(analysis, options = {}, execution) {
15
15
  sections.push(formatExecutionSection(execution));
16
16
  }
17
17
  sections.push(formatRecommendationsSection(analysis, execution));
18
- return sections.join('\n\n');
18
+ return sections.join("\n\n");
19
19
  }
20
20
  function formatHeader(accountName, analysis) {
21
21
  const lines = [];
22
22
  lines.push(`${accountName} Reconciliation Report`);
23
23
  lines.push(SECTION_DIVIDER);
24
24
  lines.push(`Statement Period: ${analysis.summary.statement_date_range}`);
25
- return lines.join('\n');
25
+ return lines.join("\n");
26
26
  }
27
27
  function formatNotesSection(notes) {
28
28
  const lines = [];
29
- lines.push('Notes');
29
+ lines.push("Notes");
30
30
  lines.push(SECTION_DIVIDER);
31
31
  for (const note of notes) {
32
32
  lines.push(`- ${note}`);
33
33
  }
34
- return lines.join('\n');
34
+ return lines.join("\n");
35
35
  }
36
36
  function formatBalanceSection(balanceInfo, summary) {
37
37
  const lines = [];
38
- lines.push('Balance Check');
38
+ lines.push("Balance Check");
39
39
  lines.push(SECTION_DIVIDER);
40
40
  lines.push(`- YNAB Cleared Balance: ${summary.current_cleared_balance.value_display}`);
41
41
  lines.push(`- Statement Balance: ${summary.target_statement_balance.value_display}`);
42
- lines.push('');
42
+ lines.push("");
43
43
  const discrepancyMilli = balanceInfo.discrepancy.value_milliunits;
44
44
  if (discrepancyMilli === 0) {
45
- lines.push('Balances match perfectly.');
45
+ lines.push("Balances match perfectly.");
46
46
  }
47
47
  else {
48
- const direction = discrepancyMilli > 0 ? 'ynab_higher' : 'bank_higher';
49
- const directionLabel = direction === 'ynab_higher'
50
- ? 'YNAB shows MORE than statement'
51
- : 'Statement shows MORE than YNAB';
48
+ const direction = discrepancyMilli > 0 ? "ynab_higher" : "bank_higher";
49
+ const directionLabel = direction === "ynab_higher"
50
+ ? "YNAB shows MORE than statement"
51
+ : "Statement shows MORE than YNAB";
52
52
  lines.push(`Discrepancy: ${balanceInfo.discrepancy.value_display}`);
53
53
  lines.push(`Direction: ${directionLabel}`);
54
54
  }
55
- return lines.join('\n');
55
+ return lines.join("\n");
56
56
  }
57
57
  function formatTransactionAnalysisSection(analysis, options) {
58
58
  const lines = [];
59
- lines.push('Transaction Analysis');
59
+ lines.push("Transaction Analysis");
60
60
  lines.push(SECTION_DIVIDER);
61
61
  const summary = analysis.summary;
62
+ const outsideRangeCount = summary.ynab_outside_range_count ?? 0;
63
+ if (outsideRangeCount > 0) {
64
+ const inRangeCount = summary.ynab_in_range_count ?? summary.ynab_transactions_count;
65
+ lines.push(`Comparing ${summary.bank_transactions_count} bank transactions with ${inRangeCount} YNAB transactions within statement period.`);
66
+ lines.push(`(${outsideRangeCount} YNAB transactions outside statement period - not compared)`);
67
+ lines.push("");
68
+ }
62
69
  lines.push(`- Automatically matched: ${summary.auto_matched} of ${summary.bank_transactions_count} transactions`);
63
70
  lines.push(`- Suggested matches: ${summary.suggested_matches}`);
64
71
  lines.push(`- Unmatched bank: ${summary.unmatched_bank}`);
65
72
  lines.push(`- Unmatched YNAB: ${summary.unmatched_ynab}`);
66
73
  if (analysis.unmatched_bank.length > 0) {
67
- lines.push('');
68
- lines.push('Unmatched bank transactions:');
74
+ lines.push("");
75
+ lines.push("Missing from YNAB (bank transactions without matches):");
69
76
  const maxToShow = options.maxUnmatchedToShow ?? 5;
70
77
  const toShow = analysis.unmatched_bank.slice(0, maxToShow);
71
78
  for (const txn of toShow) {
@@ -75,9 +82,21 @@ function formatTransactionAnalysisSection(analysis, options) {
75
82
  lines.push(` ... and ${analysis.unmatched_bank.length - maxToShow} more`);
76
83
  }
77
84
  }
85
+ if (analysis.unmatched_ynab.length > 0) {
86
+ lines.push("");
87
+ lines.push("Missing from bank statement (YNAB transactions without matches):");
88
+ const maxToShow = options.maxUnmatchedToShow ?? 5;
89
+ const toShow = analysis.unmatched_ynab.slice(0, maxToShow);
90
+ for (const txn of toShow) {
91
+ lines.push(formatYnabTransactionLine(txn));
92
+ }
93
+ if (analysis.unmatched_ynab.length > maxToShow) {
94
+ lines.push(` ... and ${analysis.unmatched_ynab.length - maxToShow} more`);
95
+ }
96
+ }
78
97
  if (analysis.suggested_matches.length > 0) {
79
- lines.push('');
80
- lines.push('Suggested matches:');
98
+ lines.push("");
99
+ lines.push("Suggested matches (review manually):");
81
100
  const maxToShow = options.maxUnmatchedToShow ?? 3;
82
101
  const toShow = analysis.suggested_matches.slice(0, maxToShow);
83
102
  for (const match of toShow) {
@@ -87,7 +106,12 @@ function formatTransactionAnalysisSection(analysis, options) {
87
106
  lines.push(` ... and ${analysis.suggested_matches.length - maxToShow} more suggestions`);
88
107
  }
89
108
  }
90
- return lines.join('\n');
109
+ return lines.join("\n");
110
+ }
111
+ function formatYnabTransactionLine(txn) {
112
+ const amountStr = formatAmount(txn.amount);
113
+ const payee = txn.payee ?? "Unknown";
114
+ return ` ${txn.date} - ${payee.substring(0, 40).padEnd(40)} ${amountStr}`;
91
115
  }
92
116
  function formatBankTransactionLine(txn) {
93
117
  const amountStr = formatAmount(txn.amount);
@@ -101,13 +125,13 @@ function formatSuggestedMatchLine(match) {
101
125
  }
102
126
  function formatAmount(amountMilli) {
103
127
  const amount = amountMilli / 1000;
104
- const sign = amount >= 0 ? '+' : '-';
128
+ const sign = amount >= 0 ? "+" : "-";
105
129
  const absAmount = Math.abs(amount);
106
130
  return `${sign}$${absAmount.toFixed(2)}`.padStart(10);
107
131
  }
108
132
  function formatInsightsSection(insights, maxToShow = 3) {
109
133
  const lines = [];
110
- lines.push('Key Insights');
134
+ lines.push("Key Insights");
111
135
  lines.push(SECTION_DIVIDER);
112
136
  const toShow = insights.slice(0, maxToShow);
113
137
  for (const insight of toShow) {
@@ -120,49 +144,50 @@ function formatInsightsSection(insights, maxToShow = 3) {
120
144
  lines.push(` Evidence: ${evidenceSummary}`);
121
145
  }
122
146
  }
123
- lines.push('');
147
+ lines.push("");
124
148
  }
125
149
  if (insights.length > maxToShow) {
126
150
  lines.push(`... and ${insights.length - maxToShow} more insights (see structured output)`);
127
151
  }
128
- return lines.join('\n').trimEnd();
152
+ return lines.join("\n").trimEnd();
129
153
  }
130
154
  function getSeverityIcon(severity) {
131
155
  switch (severity) {
132
- case 'critical':
133
- return '[CRITICAL]';
134
- case 'warning':
135
- return '[WARN]';
136
- case 'info':
137
- return '[INFO]';
156
+ case "critical":
157
+ return "[CRITICAL]";
158
+ case "warning":
159
+ return "[WARN]";
160
+ case "info":
161
+ return "[INFO]";
138
162
  default:
139
- return '[NOTE]';
163
+ return "[NOTE]";
140
164
  }
141
165
  }
142
166
  function formatEvidenceSummary(evidence) {
143
- if ('transaction_count' in evidence) {
144
- return `${evidence['transaction_count']} transactions`;
167
+ if ("transaction_count" in evidence) {
168
+ return `${evidence["transaction_count"]} transactions`;
145
169
  }
146
- if ('amount' in evidence && typeof evidence['amount'] === 'object') {
147
- const amount = evidence['amount'];
170
+ if ("amount" in evidence && typeof evidence["amount"] === "object") {
171
+ const amount = evidence["amount"];
148
172
  return amount.value_display;
149
173
  }
150
- if ('transaction_ids' in evidence && Array.isArray(evidence['transaction_ids'])) {
151
- return `${evidence['transaction_ids'].length} transactions involved`;
174
+ if ("transaction_ids" in evidence &&
175
+ Array.isArray(evidence["transaction_ids"])) {
176
+ return `${evidence["transaction_ids"].length} transactions involved`;
152
177
  }
153
178
  return null;
154
179
  }
155
180
  function formatExecutionSection(execution) {
156
181
  const lines = [];
157
- lines.push('Execution Summary');
182
+ lines.push("Execution Summary");
158
183
  lines.push(SECTION_DIVIDER);
159
184
  const summary = execution.summary;
160
185
  lines.push(`Transactions created: ${summary.transactions_created}`);
161
186
  lines.push(`Transactions updated: ${summary.transactions_updated}`);
162
187
  lines.push(`Date adjustments: ${summary.dates_adjusted}`);
163
188
  if (execution.recommendations.length > 0) {
164
- lines.push('');
165
- lines.push('Recommendations:');
189
+ lines.push("");
190
+ lines.push("Recommendations:");
166
191
  const maxRecs = 3;
167
192
  const toShow = execution.recommendations.slice(0, maxRecs);
168
193
  for (const rec of toShow) {
@@ -172,22 +197,22 @@ function formatExecutionSection(execution) {
172
197
  lines.push(` ... and ${execution.recommendations.length - maxRecs} more`);
173
198
  }
174
199
  }
175
- lines.push('');
200
+ lines.push("");
176
201
  if (summary.dry_run) {
177
- lines.push('NOTE: Dry run only - no YNAB changes were applied.');
202
+ lines.push("NOTE: Dry run only - no YNAB changes were applied.");
178
203
  }
179
204
  else {
180
- lines.push('Changes applied to YNAB. Review structured output for action details.');
205
+ lines.push("Changes applied to YNAB. Review structured output for action details.");
181
206
  }
182
- return lines.join('\n');
207
+ return lines.join("\n");
183
208
  }
184
209
  function formatRecommendationsSection(analysis, execution) {
185
210
  const lines = [];
186
- lines.push('Recommended Actions');
211
+ lines.push("Recommended Actions");
187
212
  lines.push(SECTION_DIVIDER);
188
213
  if (execution && !execution.summary.dry_run) {
189
- lines.push('All recommended actions have been applied.');
190
- return lines.join('\n');
214
+ lines.push("All recommended actions have been applied.");
215
+ return lines.join("\n");
191
216
  }
192
217
  if (analysis.next_steps.length > 0) {
193
218
  for (const step of analysis.next_steps) {
@@ -195,10 +220,10 @@ function formatRecommendationsSection(analysis, execution) {
195
220
  }
196
221
  }
197
222
  else {
198
- lines.push('No specific actions recommended.');
199
- lines.push('Review the structured output for detailed match information.');
223
+ lines.push("No specific actions recommended.");
224
+ lines.push("Review the structured output for detailed match information.");
200
225
  }
201
- return lines.join('\n');
226
+ return lines.join("\n");
202
227
  }
203
228
  export function formatBalanceInfo(balance) {
204
229
  const lines = [];
@@ -206,15 +231,15 @@ export function formatBalanceInfo(balance) {
206
231
  lines.push(`Current Total: ${balance.current_total.value_display}`);
207
232
  lines.push(`Target Statement: ${balance.target_statement.value_display}`);
208
233
  lines.push(`Discrepancy: ${balance.discrepancy.value_display}`);
209
- return lines.join('\n');
234
+ return lines.join("\n");
210
235
  }
211
236
  export function formatTransactionList(transactions, maxItems = 10) {
212
237
  const lines = [];
213
238
  const toShow = transactions.slice(0, maxItems);
214
239
  for (const txn of toShow) {
215
- if ('cleared' in txn) {
240
+ if ("cleared" in txn) {
216
241
  const ynabTxn = txn;
217
- const payee = ynabTxn.payee_name ?? ynabTxn.payee ?? 'Unknown';
242
+ const payee = ynabTxn.payee_name ?? ynabTxn.payee ?? "Unknown";
218
243
  lines.push(` ${ynabTxn.date} - ${payee.substring(0, 40).padEnd(40)} ${formatAmount(ynabTxn.amount)}`);
219
244
  }
220
245
  else {
@@ -224,5 +249,5 @@ export function formatTransactionList(transactions, maxItems = 10) {
224
249
  if (transactions.length > maxItems) {
225
250
  lines.push(` ... and ${transactions.length - maxItems} more`);
226
251
  }
227
- return lines.join('\n');
252
+ return lines.join("\n");
228
253
  }
@@ -1,2 +1,2 @@
1
- import type { BankTransaction, NormalizedYNABTransaction } from '../../types/reconciliation.js';
1
+ import type { BankTransaction, NormalizedYNABTransaction } from "../../types/reconciliation.js";
2
2
  export declare function detectSignInversion(bankTransactions: BankTransaction[], ynabTransactions: NormalizedYNABTransaction[]): boolean;
@@ -1,8 +1,8 @@
1
- import type { MoneyValue } from '../../utils/money.js';
2
- import type { BankTransaction as CanonicalBankTransaction, NormalizedYNABTransaction as CanonicalYNABTransaction } from '../../types/reconciliation.js';
1
+ import type { BankTransaction as CanonicalBankTransaction, NormalizedYNABTransaction as CanonicalYNABTransaction } from "../../types/reconciliation.js";
2
+ import type { MoneyValue } from "../../utils/money.js";
3
3
  export type BankTransaction = CanonicalBankTransaction;
4
4
  export type YNABTransaction = CanonicalYNABTransaction;
5
- export type MatchConfidence = 'high' | 'medium' | 'low' | 'none';
5
+ export type MatchConfidence = "high" | "medium" | "low" | "none";
6
6
  export interface MatchCandidate {
7
7
  ynab_transaction: YNABTransaction;
8
8
  confidence: number;
@@ -32,6 +32,8 @@ export interface ReconciliationSummary {
32
32
  statement_date_range: string;
33
33
  bank_transactions_count: number;
34
34
  ynab_transactions_count: number;
35
+ ynab_in_range_count: number;
36
+ ynab_outside_range_count: number;
35
37
  auto_matched: number;
36
38
  suggested_matches: number;
37
39
  unmatched_bank: number;
@@ -41,8 +43,8 @@ export interface ReconciliationSummary {
41
43
  discrepancy: MoneyValue;
42
44
  discrepancy_explanation: string;
43
45
  }
44
- export type InsightSeverity = 'info' | 'warning' | 'critical';
45
- export type InsightKind = 'repeat_amount' | 'near_match' | 'anomaly';
46
+ export type InsightSeverity = "info" | "warning" | "critical";
47
+ export type InsightKind = "repeat_amount" | "near_match" | "anomaly";
46
48
  export interface ReconciliationInsight {
47
49
  id: string;
48
50
  type: InsightKind;
@@ -53,18 +55,19 @@ export interface ReconciliationInsight {
53
55
  }
54
56
  export interface ReconciliationAnalysis {
55
57
  success: true;
56
- phase: 'analysis';
58
+ phase: "analysis";
57
59
  summary: ReconciliationSummary;
58
60
  auto_matches: TransactionMatch[];
59
61
  suggested_matches: TransactionMatch[];
60
62
  unmatched_bank: BankTransaction[];
61
63
  unmatched_ynab: YNABTransaction[];
64
+ ynab_outside_date_range: YNABTransaction[];
62
65
  balance_info: BalanceInfo;
63
66
  next_steps: string[];
64
67
  insights: ReconciliationInsight[];
65
68
  recommendations?: ActionableRecommendation[];
66
69
  }
67
- export type ReconciliationActionType = 'match' | 'add' | 'unclear' | 'delete' | 'ignore';
70
+ export type ReconciliationActionType = "match" | "add" | "unclear" | "delete" | "ignore";
68
71
  export interface ReconciliationAction {
69
72
  type: ReconciliationActionType;
70
73
  bank_txn_id?: string;
@@ -97,7 +100,7 @@ export interface ParsedCSVData {
97
100
  valid_rows: number;
98
101
  errors: string[];
99
102
  }
100
- export type RecommendationPriority = 'high' | 'medium' | 'low';
103
+ export type RecommendationPriority = "high" | "medium" | "low";
101
104
  export interface BaseRecommendation {
102
105
  id: string;
103
106
  priority: RecommendationPriority;
@@ -110,27 +113,27 @@ export interface BaseRecommendation {
110
113
  metadata?: Record<string, unknown>;
111
114
  }
112
115
  export interface CreateTransactionRecommendation extends BaseRecommendation {
113
- action_type: 'create_transaction';
116
+ action_type: "create_transaction";
114
117
  parameters: {
115
118
  account_id: string;
116
119
  date: string;
117
120
  amount: number;
118
121
  payee_name: string;
119
122
  memo?: string;
120
- cleared: 'cleared' | 'uncleared';
123
+ cleared: "cleared" | "uncleared";
121
124
  approved: boolean;
122
125
  category_id?: string;
123
126
  };
124
127
  }
125
128
  export interface UpdateClearedRecommendation extends BaseRecommendation {
126
- action_type: 'update_cleared';
129
+ action_type: "update_cleared";
127
130
  parameters: {
128
131
  transaction_id: string;
129
- cleared: 'cleared' | 'uncleared' | 'reconciled';
132
+ cleared: "cleared" | "uncleared" | "reconciled";
130
133
  };
131
134
  }
132
135
  export interface ReviewDuplicateRecommendation extends BaseRecommendation {
133
- action_type: 'review_duplicate';
136
+ action_type: "review_duplicate";
134
137
  parameters: {
135
138
  candidate_ids: string[];
136
139
  bank_transaction?: BankTransaction;
@@ -138,14 +141,14 @@ export interface ReviewDuplicateRecommendation extends BaseRecommendation {
138
141
  };
139
142
  }
140
143
  export interface RelatedTransaction {
141
- source: 'bank' | 'ynab';
144
+ source: "bank" | "ynab";
142
145
  id: string;
143
146
  description: string;
144
147
  }
145
148
  export interface ManualReviewRecommendation extends BaseRecommendation {
146
- action_type: 'manual_review';
149
+ action_type: "manual_review";
147
150
  parameters: {
148
- issue_type: 'complex_match' | 'large_discrepancy' | 'unknown';
151
+ issue_type: "complex_match" | "large_discrepancy" | "unknown";
149
152
  related_transactions?: RelatedTransaction[];
150
153
  };
151
154
  }
@@ -1,4 +1,4 @@
1
- import type * as ynab from 'ynab';
2
- import type { NormalizedYNABTransaction } from '../../types/reconciliation.js';
1
+ import type * as ynab from "ynab";
2
+ import type { NormalizedYNABTransaction } from "../../types/reconciliation.js";
3
3
  export declare function normalizeYNABTransaction(txn: ynab.TransactionDetail): NormalizedYNABTransaction;
4
4
  export declare function normalizeYNABTransactions(txns: ynab.TransactionDetail[]): NormalizedYNABTransaction[];
@@ -1,3 +1,3 @@
1
- import { z } from 'zod/v4';
1
+ import { z } from "zod/v4";
2
2
  export declare const emptyObjectSchema: z.ZodObject<{}, z.core.$strict>;
3
3
  export declare const looseObjectSchema: z.ZodObject<{}, z.core.$loose>;
@@ -1,3 +1,3 @@
1
- import { z } from 'zod/v4';
1
+ import { z } from "zod/v4";
2
2
  export const emptyObjectSchema = z.object({}).strict();
3
3
  export const looseObjectSchema = z.object({}).passthrough();
@@ -1,4 +1,4 @@
1
- import { z } from 'zod/v4';
1
+ import { z } from "zod/v4";
2
2
  export declare const AccountSchema: z.ZodObject<{
3
3
  id: z.ZodString;
4
4
  name: z.ZodString;