@dizzlkheinz/ynab-mcpb 0.12.2 → 0.15.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 (262) hide show
  1. package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +3 -0
  2. package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +3 -0
  3. package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +1 -0
  4. package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +5 -0
  5. package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +1569 -0
  6. package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +3 -0
  7. package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +2832 -0
  8. package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +2709 -0
  9. package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +2832 -0
  10. package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +2832 -0
  11. package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +2709 -0
  12. package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +1 -0
  13. package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +5217 -0
  14. package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +2594 -0
  15. package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +2594 -0
  16. package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +231 -0
  17. package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +2590 -0
  18. package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +5195 -0
  19. package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +286 -0
  20. package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +218 -0
  21. package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +180 -0
  22. package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +3 -0
  23. package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +1 -0
  24. package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +747 -0
  25. package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +1 -0
  26. package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +2594 -0
  27. package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +2594 -0
  28. package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +144 -0
  29. package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +416 -0
  30. package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +2590 -0
  31. package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +2590 -0
  32. package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +2590 -0
  33. package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +790 -0
  34. package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +766 -0
  35. package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +790 -0
  36. package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +2594 -0
  37. package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +1000 -0
  38. package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +3489 -0
  39. package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +766 -0
  40. package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +2594 -0
  41. package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +2456 -0
  42. package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +2594 -0
  43. package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +18 -0
  44. package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +48 -0
  45. package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +1 -0
  46. package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +1 -0
  47. package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +1 -0
  48. package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +1 -0
  49. package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +1271 -0
  50. package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +1570 -0
  51. package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +2590 -0
  52. package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +3 -0
  53. package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +3299 -0
  54. package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +3299 -0
  55. package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +1882 -0
  56. package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +2594 -0
  57. package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +1 -0
  58. package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +170 -0
  59. package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +1 -0
  60. package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +3 -0
  61. package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +1 -0
  62. package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +1 -0
  63. package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +3 -0
  64. package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +1 -0
  65. package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +1 -0
  66. package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +5 -0
  67. package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +1 -0
  68. package/.github/workflows/ci-tests.yml +6 -2
  69. package/.github/workflows/publish.yml +3 -3
  70. package/.github/workflows/release.yml +4 -0
  71. package/CHANGELOG.md +89 -1
  72. package/NUL +1 -1
  73. package/README.md +36 -10
  74. package/dist/bundle/index.cjs +65 -42
  75. package/dist/index.js +9 -20
  76. package/dist/server/YNABMCPServer.d.ts +2 -1
  77. package/dist/server/YNABMCPServer.js +61 -27
  78. package/dist/server/cacheKeys.d.ts +8 -0
  79. package/dist/server/cacheKeys.js +8 -0
  80. package/dist/server/config.d.ts +22 -3
  81. package/dist/server/config.js +16 -17
  82. package/dist/server/errorHandler.d.ts +2 -0
  83. package/dist/server/errorHandler.js +49 -5
  84. package/dist/server/securityMiddleware.js +3 -6
  85. package/dist/server/toolRegistry.js +8 -10
  86. package/dist/tools/accountTools.js +4 -3
  87. package/dist/tools/categoryTools.js +8 -7
  88. package/dist/tools/monthTools.js +2 -1
  89. package/dist/tools/payeeTools.js +2 -1
  90. package/dist/tools/reconcileAdapter.js +10 -5
  91. package/dist/tools/reconciliation/analyzer.d.ts +4 -2
  92. package/dist/tools/reconciliation/analyzer.js +120 -404
  93. package/dist/tools/reconciliation/csvParser.d.ts +51 -0
  94. package/dist/tools/reconciliation/csvParser.js +413 -0
  95. package/dist/tools/reconciliation/executor.d.ts +8 -0
  96. package/dist/tools/reconciliation/executor.js +277 -50
  97. package/dist/tools/reconciliation/index.d.ts +7 -7
  98. package/dist/tools/reconciliation/index.js +115 -39
  99. package/dist/tools/reconciliation/matcher.d.ts +24 -3
  100. package/dist/tools/reconciliation/matcher.js +175 -133
  101. package/dist/tools/reconciliation/recommendationEngine.js +22 -18
  102. package/dist/tools/reconciliation/reportFormatter.js +9 -8
  103. package/dist/tools/reconciliation/signDetector.d.ts +2 -0
  104. package/dist/tools/reconciliation/signDetector.js +54 -0
  105. package/dist/tools/reconciliation/types.d.ts +20 -34
  106. package/dist/tools/reconciliation/types.js +1 -7
  107. package/dist/tools/reconciliation/ynabAdapter.d.ts +4 -0
  108. package/dist/tools/reconciliation/ynabAdapter.js +15 -0
  109. package/dist/tools/transactionTools.d.ts +3 -17
  110. package/dist/tools/transactionTools.js +5 -17
  111. package/dist/types/reconciliation.d.ts +24 -0
  112. package/dist/types/reconciliation.js +1 -0
  113. package/dist/utils/baseError.d.ts +3 -0
  114. package/dist/utils/baseError.js +7 -0
  115. package/dist/utils/errors.d.ts +13 -0
  116. package/dist/utils/errors.js +15 -0
  117. package/dist/utils/validationError.d.ts +3 -0
  118. package/dist/utils/validationError.js +3 -0
  119. package/docs/guides/ARCHITECTURE.md +12 -129
  120. package/docs/plans/2025-11-20-reloadable-config-token-validation.md +93 -0
  121. package/docs/plans/2025-11-21-fix-transaction-cached-property.md +362 -0
  122. package/docs/plans/2025-11-21-reconciliation-error-handling.md +90 -0
  123. package/docs/plans/2025-11-21-v014-hardening.md +153 -0
  124. package/docs/plans/reconciliation-v2-redesign.md +1571 -0
  125. package/package.json +8 -2
  126. package/scripts/run-throttled-integration-tests.js +9 -3
  127. package/scripts/test-recommendations.ts +1 -1
  128. package/src/__tests__/performance.test.ts +12 -5
  129. package/src/__tests__/testUtils.ts +62 -5
  130. package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +129 -0
  131. package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +53 -0
  132. package/src/__tests__/workflows.e2e.test.ts +33 -0
  133. package/src/index.ts +8 -31
  134. package/src/server/YNABMCPServer.ts +81 -42
  135. package/src/server/__tests__/YNABMCPServer.integration.test.ts +10 -12
  136. package/src/server/__tests__/YNABMCPServer.test.ts +27 -15
  137. package/src/server/__tests__/config.test.ts +76 -152
  138. package/src/server/__tests__/server-startup.integration.test.ts +42 -14
  139. package/src/server/__tests__/toolRegistry.test.ts +1 -1
  140. package/src/server/cacheKeys.ts +8 -0
  141. package/src/server/config.ts +20 -38
  142. package/src/server/errorHandler.ts +52 -5
  143. package/src/server/securityMiddleware.ts +3 -7
  144. package/src/server/toolRegistry.ts +14 -10
  145. package/src/tools/__tests__/categoryTools.test.ts +37 -19
  146. package/src/tools/__tests__/transactionTools.test.ts +58 -2
  147. package/src/tools/accountTools.ts +8 -3
  148. package/src/tools/categoryTools.ts +12 -7
  149. package/src/tools/monthTools.ts +7 -1
  150. package/src/tools/payeeTools.ts +7 -1
  151. package/src/tools/reconcileAdapter.ts +10 -5
  152. package/src/tools/reconciliation/__tests__/adapter.test.ts +28 -22
  153. package/src/tools/reconciliation/__tests__/analyzer.test.ts +114 -180
  154. package/src/tools/reconciliation/__tests__/csvParser.test.ts +87 -0
  155. package/src/tools/reconciliation/__tests__/executor.integration.test.ts +26 -6
  156. package/src/tools/reconciliation/__tests__/executor.test.ts +133 -60
  157. package/src/tools/reconciliation/__tests__/matcher.test.ts +68 -54
  158. package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +37 -30
  159. package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +6 -5
  160. package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +30 -11
  161. package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +50 -15
  162. package/src/tools/reconciliation/__tests__/signDetector.test.ts +211 -0
  163. package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +61 -0
  164. package/src/tools/reconciliation/analyzer.ts +174 -545
  165. package/src/tools/reconciliation/csvParser.ts +617 -0
  166. package/src/tools/reconciliation/executor.ts +344 -58
  167. package/src/tools/reconciliation/index.ts +141 -48
  168. package/src/tools/reconciliation/matcher.ts +234 -214
  169. package/src/tools/reconciliation/recommendationEngine.ts +23 -19
  170. package/src/tools/reconciliation/reportFormatter.ts +16 -11
  171. package/src/tools/reconciliation/signDetector.ts +117 -0
  172. package/src/tools/reconciliation/types.ts +39 -61
  173. package/src/tools/reconciliation/ynabAdapter.ts +33 -0
  174. package/src/tools/schemas/outputs/utilityOutputs.ts +1 -1
  175. package/src/tools/transactionTools.ts +7 -18
  176. package/src/types/reconciliation.ts +49 -0
  177. package/src/utils/baseError.ts +7 -0
  178. package/src/utils/errors.ts +21 -0
  179. package/src/utils/validationError.ts +3 -0
  180. package/temp-recon.ts +126 -0
  181. package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +3662 -0
  182. package/test_mcp_tools.mjs +75 -0
  183. package/.code/agents/0427d95e-edca-431f-a214-5e53264e29c4/error.txt +0 -8
  184. package/.code/agents/0d675174-d1e1-41c3-9975-4c2e275819a9/error.txt +0 -3
  185. package/.code/agents/0d8c5afd-4787-422b-abf8-2e5943fc7e67/error.txt +0 -3
  186. package/.code/agents/0ec34a70-ed5d-4b9e-bee4-bb0e4cccbc4b/error.txt +0 -1
  187. package/.code/agents/0ef51a21-1ab1-49d7-9561-0eaa43875ebc/error.txt +0 -12
  188. package/.code/agents/15db95d7-abad-4b4d-9c3b-8446089cb61d/error.txt +0 -1
  189. package/.code/agents/19ab9acb-f675-4ff0-902a-09a5476f8149/error.txt +0 -1
  190. package/.code/agents/1ef7e12d-f6ff-4897-8a9b-152d523d898e/error.txt +0 -5
  191. package/.code/agents/2465/exec-call_lroN9KKzJVWC7t5423DK1nT9.txt +0 -1453
  192. package/.code/agents/28edb6fe-95a9-41a0-ae69-aa0100d26c0c/error.txt +0 -8
  193. package/.code/agents/2ae40cf5-b4bf-42e2-92bf-7ea350a7755e/error.txt +0 -9
  194. package/.code/agents/2bfc4e1f-ac4b-45a5-b6df-bf89d4dbb54c/error.txt +0 -1
  195. package/.code/agents/2e2e1134-eff0-49be-ba25-8e2c3468a564/error.txt +0 -5
  196. package/.code/agents/3/exec-call_203OC4TNVkLxW7z2HCVEQ1cM.txt +0 -81
  197. package/.code/agents/3/exec-call_SS5T0XSiXB4LSNzUKTl75wkh.txt +0 -610
  198. package/.code/agents/3322c003-ce5e-48e3-a342-f5049c5bf9a2/error.txt +0 -1
  199. package/.code/agents/391e9b08-1ebc-468c-9bcd-6d0cc3193b37/error.txt +0 -1
  200. package/.code/agents/3ab0aa84-b7bb-4054-afa3-40b8fd7d3be0/error.txt +0 -1
  201. package/.code/agents/3bed368d-50fe-477e-aee3-a6707eaa1ab9/error.txt +0 -3
  202. package/.code/agents/3e40b925-db12-442f-8d7a-a25fc69a6672/error.txt +0 -8
  203. package/.code/agents/414d5776-cf58-41f3-9328-a6daed503a50/error.txt +0 -5
  204. package/.code/agents/42687751-4565-4610-b240-67835b17d861/error.txt +0 -1
  205. package/.code/agents/46b98876-1a39-43c9-9e2f-507ca6d47335/error.txt +0 -9
  206. package/.code/agents/4a7d9491-b26f-43dd-850d-2ecdc49b5d1b/error.txt +0 -1
  207. package/.code/agents/4e60f00a-1b3e-447f-87f3-7faf9deddec3/error.txt +0 -13
  208. package/.code/agents/5138fc1c-4d49-4b74-a7da-ccdb3a8e44e7/error.txt +0 -14
  209. package/.code/agents/521cff39-a7a3-42e5-a557-134f0f7daaa0/error.txt +0 -5
  210. package/.code/agents/53302dc5-3857-4413-9a47-9e0f64a51dc4/error.txt +0 -5
  211. package/.code/agents/567c7c2e-6a6f-4761-a08d-d36deeb2e0ac/error.txt +0 -5
  212. package/.code/agents/57b00845-80dc-47c9-953c-3028d16275d6/error.txt +0 -3
  213. package/.code/agents/593d9005-c2a5-48fd-8813-ece0d3f2de96/error.txt +0 -1
  214. package/.code/agents/5a112e66-0e1a-42f9-877c-53af56ea3551/error.txt +0 -1
  215. package/.code/agents/5b05e8ed-7788-4738-b7ee-9faa8180f992/error.txt +0 -5
  216. package/.code/agents/5f888d6f-d7ca-4ac8-be23-9ea1bf753951/error.txt +0 -5
  217. package/.code/agents/607db3ab-e4b0-435b-b497-93e9aa525549/error.txt +0 -8
  218. package/.code/agents/67dcb2a2-900f-4c78-b3fc-80b5213e0ddf/error.txt +0 -8
  219. package/.code/agents/69ad848c-4e98-49b3-b16c-0094ac2d1759/error.txt +0 -5
  220. package/.code/agents/6c9cfc5f-0d0b-445c-b121-9f60082c4f70/error.txt +0 -1
  221. package/.code/agents/6f6f8f77-4ab0-4f6e-9f30-40e8be0bd8f5/error.txt +0 -1
  222. package/.code/agents/72a7cde4-fa8a-4024-9038-27faa550539b/error.txt +0 -1
  223. package/.code/agents/7b48335c-8247-43aa-9949-5f820ba8e199/error.txt +0 -1
  224. package/.code/agents/80944249-bea9-4ac5-87de-a666c4df306e/error.txt +0 -1
  225. package/.code/agents/826099df-1b66-4186-a915-7eb59f9db19d/error.txt +0 -5
  226. package/.code/agents/8291d158-18a8-4a92-b799-4e9a4d9cce88/error.txt +0 -1
  227. package/.code/agents/82fb71a3-20fb-4341-804a-a2fc900f95bc/error.txt +0 -1
  228. package/.code/agents/855790ea-54ee-43e4-8209-a66994e37590/error.txt +0 -1
  229. package/.code/agents/88ce3a2e-04f2-42be-9062-bf97aa798da0/error.txt +0 -3
  230. package/.code/agents/9a17e398-b6ed-4218-bb55-bc64a8d38ce8/error.txt +0 -8
  231. package/.code/agents/9a4f4bfc-a2a6-4f40-a896-9335b41a7ed1/error.txt +0 -1
  232. package/.code/agents/9b633e55-ef84-47d6-94bb-fd3dd172ad97/error.txt +0 -1
  233. package/.code/agents/9b81f3ab-c72b-4a81-9a8f-28a49ddba84a/error.txt +0 -8
  234. package/.code/agents/a35daf29-b2d1-4aef-9b42-dad63a76bd47/error.txt +0 -3
  235. package/.code/agents/a81990cc-69ee-44d2-b907-17403c9bc5d7/error.txt +0 -5
  236. package/.code/agents/ab56260a-4a83-4ad4-9410-f88a23d6520a/error.txt +0 -1
  237. package/.code/agents/ad722c31-2d1d-45f7-bae2-3f02ca455b60/error.txt +0 -1
  238. package/.code/agents/b62e8690-3324-4b97-9309-731bee79416b/error.txt +0 -5
  239. package/.code/agents/baf60a3a-752b-4ad8-99d6-df32423ed2eb/error.txt +0 -1
  240. package/.code/agents/be049042-7dcb-4ac8-9beb-c8f1aea67742/error.txt +0 -14
  241. package/.code/agents/bed1dcb4-bfce-4a9f-8594-0f994962aafd/error.txt +0 -1
  242. package/.code/agents/c324a6cf-e935-4ede-9529-b3ebc18e8d6b/error.txt +0 -5
  243. package/.code/agents/c37c06ff-dfe3-43f2-9bbc-3ec73ec8f41d/error.txt +0 -5
  244. package/.code/agents/c8cd6671-433a-456b-9f88-e51cb2df6bfc/error.txt +0 -11
  245. package/.code/agents/ca2ccb67-2f24-428e-b27d-9365beadd140/error.txt +0 -1
  246. package/.code/agents/cf08c0c8-e7f0-423e-93ba-547e8e818340/error.txt +0 -8
  247. package/.code/agents/d579c74f-874b-40a4-9d56-ced1eb6a701d/error.txt +0 -1
  248. package/.code/agents/df412c98-7378-4deb-8e1e-76c416931181/error.txt +0 -3
  249. package/.code/agents/e5134eb3-2af4-45b0-8998-051cb4afdb45/error.txt +0 -3
  250. package/.code/agents/e6308471-aa45-4e9e-9496-2e9404164d97/error.txt +0 -8
  251. package/.code/agents/e7bd8bc7-23fb-4f46-98dc-b0dcf11b75a1/error.txt +0 -1
  252. package/.code/agents/e92bec35-378d-4fe1-8ac0-6e1bb3c86911/error.txt +0 -5
  253. package/.code/agents/ed918fbf-2dc4-4aa2-bfc5-04b65d9471ea/error.txt +0 -1
  254. package/.code/agents/ef1d756f-b272-48fc-8729-f05c494674f7/error.txt +0 -1
  255. package/.code/agents/ef359853-0249-4e41-a804-c0fc459fe456/error.txt +0 -1
  256. package/.code/agents/effc7b4a-4b90-40a0-8c86-a7a99d2d5fd2/error.txt +0 -1
  257. package/.code/agents/fa15f8d5-8359-4a8b-83a3-2f2056b3ff40/error.txt +0 -3
  258. package/.code/agents/fbef4193-eadf-4c8a-83ff-4878a6310f25/error.txt +0 -8
  259. package/.code/agents/fd0a4b4a-fda4-4964-a6d6-2b8a2da387c6/error.txt +0 -1
  260. package/.gemini/settings.json +0 -8
  261. package/ADOS-2-Module-1-Complete-Manual.md +0 -757
  262. package/WARP.md +0 -245
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'crypto';
2
- import { toMoneyValueFromDecimal, fromMilli, toMilli } from '../../utils/money.js';
2
+ import { toMoneyValue, toMoneyValueFromDecimal, fromMilli } from '../../utils/money.js';
3
3
  const RECOMMENDATION_VERSION = '1.0';
4
4
  const CONFIDENCE = {
5
5
  CREATE_EXACT_MATCH: 0.95,
@@ -33,15 +33,15 @@ function processInsight(insight, context) {
33
33
  }
34
34
  }
35
35
  function createSuggestedMatchRecommendation(match, context) {
36
- const bankTxn = match.bank_transaction;
37
- if (match.ynab_transaction && match.confidence !== 'none') {
36
+ const bankTxn = match.bankTransaction;
37
+ if (match.ynabTransaction && match.confidence !== 'none') {
38
38
  return {
39
39
  id: randomUUID(),
40
40
  action_type: 'review_duplicate',
41
41
  priority: 'high',
42
- confidence: Math.max(0, Math.min(1, match.confidence_score / 100)),
42
+ confidence: Math.max(0, Math.min(1, match.confidenceScore / 100)),
43
43
  message: `Review possible match: ${bankTxn.payee}`,
44
- reason: match.match_reason,
44
+ reason: match.matchReason,
45
45
  estimated_impact: toMoneyValueFromDecimal(0, context.analysis.balance_info.current_cleared.currency),
46
46
  account_id: context.account_id,
47
47
  metadata: {
@@ -49,20 +49,20 @@ function createSuggestedMatchRecommendation(match, context) {
49
49
  created_at: new Date().toISOString(),
50
50
  },
51
51
  parameters: {
52
- candidate_ids: [match.ynab_transaction.id],
52
+ candidate_ids: [match.ynabTransaction.id],
53
53
  bank_transaction: bankTxn,
54
- suggested_match_id: match.ynab_transaction.id,
54
+ suggested_match_id: match.ynabTransaction.id,
55
55
  },
56
56
  };
57
57
  }
58
- const isCombinationMatch = match.match_reason === 'combination_match' || (match.candidates?.length ?? 0) > 1;
58
+ const isCombinationMatch = (match.candidates?.length ?? 0) > 1;
59
59
  if (isCombinationMatch) {
60
60
  return createCombinationReviewRecommendation(match, context);
61
61
  }
62
62
  const parameters = {
63
63
  account_id: context.account_id,
64
64
  date: bankTxn.date,
65
- amount: toMilli(bankTxn.amount),
65
+ amount: bankTxn.amount,
66
66
  payee_name: bankTxn.payee,
67
67
  cleared: 'cleared',
68
68
  approved: true,
@@ -77,7 +77,7 @@ function createSuggestedMatchRecommendation(match, context) {
77
77
  confidence: CONFIDENCE.CREATE_EXACT_MATCH,
78
78
  message: `Create transaction for ${bankTxn.payee}`,
79
79
  reason: `This transaction exactly matches your discrepancy`,
80
- estimated_impact: toMoneyValueFromDecimal(bankTxn.amount, context.analysis.balance_info.current_cleared.currency),
80
+ estimated_impact: toMoneyValue(bankTxn.amount, context.analysis.balance_info.current_cleared.currency),
81
81
  account_id: context.account_id,
82
82
  metadata: {
83
83
  version: RECOMMENDATION_VERSION,
@@ -87,7 +87,7 @@ function createSuggestedMatchRecommendation(match, context) {
87
87
  };
88
88
  }
89
89
  function createCombinationReviewRecommendation(match, context) {
90
- const bankTxn = match.bank_transaction;
90
+ const bankTxn = match.bankTransaction;
91
91
  const candidateIds = match.candidates?.map((candidate) => candidate.ynab_transaction.id) ?? [];
92
92
  const candidateTotalAmount = match.candidates?.reduce((sum, candidate) => {
93
93
  const amount = candidate.ynab_transaction.amount;
@@ -110,7 +110,7 @@ function createCombinationReviewRecommendation(match, context) {
110
110
  metadata: {
111
111
  version: RECOMMENDATION_VERSION,
112
112
  created_at: new Date().toISOString(),
113
- bank_transaction_amount: toMoneyValueFromDecimal(bankTxn.amount, context.analysis.balance_info.current_cleared.currency),
113
+ bank_transaction_amount: toMoneyValue(bankTxn.amount, context.analysis.balance_info.current_cleared.currency),
114
114
  candidate_total_amount: toMoneyValueFromDecimal(candidateTotalAmount, context.analysis.balance_info.current_cleared.currency),
115
115
  candidate_count: match.candidates?.length ?? 0,
116
116
  },
@@ -125,8 +125,8 @@ function createCombinationReviewRecommendation(match, context) {
125
125
  ...candidateIds.map((id) => ({
126
126
  source: 'ynab',
127
127
  id,
128
- description: match.candidates?.find((c) => c.ynab_transaction.id === id)?.ynab_transaction
129
- .payee_name ?? 'Unknown',
128
+ description: match.candidates?.find((c) => c.ynab_transaction.id === id)?.ynab_transaction.payee ??
129
+ 'Unknown',
130
130
  })),
131
131
  ],
132
132
  },
@@ -205,7 +205,11 @@ function processUnmatchedTransactions(context) {
205
205
  for (const bankTxn of context.analysis.unmatched_bank) {
206
206
  recommendations.push(createUnmatchedBankRecommendation(bankTxn, context));
207
207
  }
208
- for (const match of context.analysis.suggested_matches) {
208
+ const matchesForReview = [
209
+ ...context.analysis.suggested_matches,
210
+ ...context.analysis.auto_matches,
211
+ ];
212
+ for (const match of matchesForReview) {
209
213
  recommendations.push(createSuggestedMatchRecommendation(match, context));
210
214
  }
211
215
  for (const ynabTxn of context.analysis.unmatched_ynab) {
@@ -219,7 +223,7 @@ function createUnmatchedBankRecommendation(txn, context) {
219
223
  const parameters = {
220
224
  account_id: context.account_id,
221
225
  date: txn.date,
222
- amount: toMilli(txn.amount),
226
+ amount: txn.amount,
223
227
  payee_name: txn.payee,
224
228
  cleared: 'cleared',
225
229
  approved: true,
@@ -234,7 +238,7 @@ function createUnmatchedBankRecommendation(txn, context) {
234
238
  confidence: CONFIDENCE.UNMATCHED_BANK,
235
239
  message: `Create missing transaction: ${txn.payee}`,
236
240
  reason: 'Transaction appears on bank statement but not in YNAB',
237
- estimated_impact: toMoneyValueFromDecimal(txn.amount, context.analysis.balance_info.current_cleared.currency),
241
+ estimated_impact: toMoneyValue(txn.amount, context.analysis.balance_info.current_cleared.currency),
238
242
  account_id: context.account_id,
239
243
  metadata: {
240
244
  version: RECOMMENDATION_VERSION,
@@ -249,7 +253,7 @@ function createUpdateClearedRecommendation(txn, context) {
249
253
  action_type: 'update_cleared',
250
254
  priority: 'low',
251
255
  confidence: CONFIDENCE.UPDATE_CLEARED,
252
- message: `Mark transaction as cleared: ${txn.payee_name || 'Unknown'}`,
256
+ message: `Mark transaction as cleared: ${txn.payee || 'Unknown'}`,
253
257
  reason: 'Transaction exists in YNAB but not yet cleared',
254
258
  estimated_impact: toMoneyValueFromDecimal(0, context.analysis.balance_info.current_cleared.currency),
255
259
  account_id: context.account_id,
@@ -81,12 +81,13 @@ function formatBankTransactionLine(txn) {
81
81
  return ` ${txn.date} - ${txn.payee.substring(0, 40).padEnd(40)} ${amountStr}`;
82
82
  }
83
83
  function formatSuggestedMatchLine(match) {
84
- const bankTxn = match.bank_transaction;
84
+ const bankTxn = match.bankTransaction;
85
85
  const amountStr = formatAmount(bankTxn.amount);
86
- const confidenceStr = `${match.confidence_score}%`;
86
+ const confidenceStr = `${match.confidenceScore}%`;
87
87
  return ` ${bankTxn.date} - ${bankTxn.payee.substring(0, 35).padEnd(35)} ${amountStr} (${confidenceStr} confidence)`;
88
88
  }
89
- function formatAmount(amount) {
89
+ function formatAmount(amountMilli) {
90
+ const amount = amountMilli / 1000;
90
91
  const sign = amount >= 0 ? '+' : '-';
91
92
  const absAmount = Math.abs(amount);
92
93
  return `${sign}$${absAmount.toFixed(2)}`.padStart(10);
@@ -198,13 +199,13 @@ export function formatTransactionList(transactions, maxItems = 10) {
198
199
  const lines = [];
199
200
  const toShow = transactions.slice(0, maxItems);
200
201
  for (const txn of toShow) {
201
- if ('payee' in txn) {
202
- lines.push(formatBankTransactionLine(txn));
202
+ if ('cleared' in txn) {
203
+ const ynabTxn = txn;
204
+ const payee = ynabTxn.payee_name ?? ynabTxn.payee ?? 'Unknown';
205
+ lines.push(` ${ynabTxn.date} - ${payee.substring(0, 40).padEnd(40)} ${formatAmount(ynabTxn.amount)}`);
203
206
  }
204
207
  else {
205
- const amount = txn.amount / 1000;
206
- const payee = txn.payee_name ?? 'Unknown';
207
- lines.push(` ${txn.date} - ${payee.substring(0, 40).padEnd(40)} ${formatAmount(amount)}`);
208
+ lines.push(formatBankTransactionLine(txn));
208
209
  }
209
210
  }
210
211
  if (transactions.length > maxItems) {
@@ -0,0 +1,2 @@
1
+ import type { BankTransaction, NormalizedYNABTransaction } from '../../types/reconciliation.js';
2
+ export declare function detectSignInversion(bankTransactions: BankTransaction[], ynabTransactions: NormalizedYNABTransaction[]): boolean;
@@ -0,0 +1,54 @@
1
+ export function detectSignInversion(bankTransactions, ynabTransactions) {
2
+ if (bankTransactions.length === 0 || ynabTransactions.length === 0) {
3
+ return false;
4
+ }
5
+ const sampleSize = Math.min(20, bankTransactions.length);
6
+ const sample = bankTransactions.slice(0, sampleSize);
7
+ const matches = [];
8
+ for (const bankTxn of sample) {
9
+ const match = findClosestMatch(bankTxn, ynabTransactions);
10
+ if (match) {
11
+ matches.push(match);
12
+ }
13
+ }
14
+ if (matches.length === 0) {
15
+ return false;
16
+ }
17
+ const oppositeSignCount = matches.filter((m) => m.oppositeSign).length;
18
+ const oppositeSignRatio = oppositeSignCount / matches.length;
19
+ return oppositeSignRatio > 0.5;
20
+ }
21
+ function findClosestMatch(bankTxn, ynabTransactions) {
22
+ const bankDate = new Date(bankTxn.date);
23
+ const bankAbsAmount = Math.abs(bankTxn.amount);
24
+ let bestMatch = null;
25
+ let bestScore = 0;
26
+ for (const ynabTxn of ynabTransactions) {
27
+ const ynabDate = new Date(ynabTxn.date);
28
+ const ynabAbsAmount = Math.abs(ynabTxn.amount);
29
+ const amountDiff = Math.abs(bankAbsAmount - ynabAbsAmount);
30
+ const amountTolerance = 100;
31
+ if (amountDiff > amountTolerance) {
32
+ continue;
33
+ }
34
+ const daysDiff = Math.abs(bankDate.getTime() - ynabDate.getTime()) / (1000 * 60 * 60 * 24);
35
+ if (daysDiff > 7) {
36
+ continue;
37
+ }
38
+ const amountScore = amountDiff === 0 ? 100 : Math.max(0, 100 - amountDiff / 10);
39
+ const dateScore = daysDiff === 0 ? 100 : Math.max(0, 100 - daysDiff * 10);
40
+ const score = amountScore * 0.7 + dateScore * 0.3;
41
+ if (score > bestScore) {
42
+ bestScore = score;
43
+ const bankSign = Math.sign(bankTxn.amount);
44
+ const ynabSign = Math.sign(ynabTxn.amount);
45
+ const oppositeSign = bankSign !== 0 && ynabSign !== 0 && bankSign !== ynabSign;
46
+ bestMatch = {
47
+ bankAmount: bankTxn.amount,
48
+ ynabAmount: ynabTxn.amount,
49
+ oppositeSign,
50
+ };
51
+ }
52
+ }
53
+ return bestMatch;
54
+ }
@@ -1,23 +1,8 @@
1
1
  import type { MoneyValue } from '../../utils/money.js';
2
+ import type { BankTransaction as CanonicalBankTransaction, NormalizedYNABTransaction as CanonicalYNABTransaction } from '../../types/reconciliation.js';
3
+ export type BankTransaction = CanonicalBankTransaction;
4
+ export type YNABTransaction = CanonicalYNABTransaction;
2
5
  export type MatchConfidence = 'high' | 'medium' | 'low' | 'none';
3
- export interface BankTransaction {
4
- id: string;
5
- date: string;
6
- amount: number;
7
- payee: string;
8
- memo?: string;
9
- original_csv_row: number;
10
- }
11
- export interface YNABTransaction {
12
- id: string;
13
- date: string;
14
- amount: number;
15
- payee_name: string | null;
16
- category_name: string | null;
17
- cleared: 'cleared' | 'uncleared' | 'reconciled';
18
- approved: boolean;
19
- memo?: string | null;
20
- }
21
6
  export interface MatchCandidate {
22
7
  ynab_transaction: YNABTransaction;
23
8
  confidence: number;
@@ -25,14 +10,14 @@ export interface MatchCandidate {
25
10
  explanation: string;
26
11
  }
27
12
  export interface TransactionMatch {
28
- bank_transaction: BankTransaction;
29
- ynab_transaction?: YNABTransaction;
13
+ bankTransaction: BankTransaction;
14
+ ynabTransaction?: YNABTransaction;
30
15
  candidates?: MatchCandidate[];
31
16
  confidence: MatchConfidence;
32
- confidence_score: number;
33
- match_reason: string;
34
- top_confidence?: number;
35
- action_hint?: string;
17
+ confidenceScore: number;
18
+ matchReason: string;
19
+ topConfidence?: number;
20
+ actionHint?: string;
36
21
  recommendation?: string;
37
22
  }
38
23
  export interface BalanceInfo {
@@ -90,19 +75,20 @@ export interface ReconciliationAction {
90
75
  metadata?: Record<string, unknown>;
91
76
  }
92
77
  export interface MatchingConfig {
78
+ weights: {
79
+ amount: number;
80
+ date: number;
81
+ payee: number;
82
+ };
83
+ amountToleranceMilliunits: number;
93
84
  dateToleranceDays: number;
94
- amountToleranceCents: number;
95
- descriptionSimilarityThreshold: number;
96
85
  autoMatchThreshold: number;
97
- suggestionThreshold: number;
86
+ suggestedMatchThreshold: number;
87
+ minimumCandidateScore: number;
88
+ exactAmountBonus: number;
89
+ exactDateBonus: number;
90
+ exactPayeeBonus: number;
98
91
  }
99
- export declare const DEFAULT_MATCHING_CONFIG: {
100
- dateToleranceDays: number;
101
- amountToleranceCents: number;
102
- descriptionSimilarityThreshold: number;
103
- autoMatchThreshold: number;
104
- suggestionThreshold: number;
105
- };
106
92
  export interface ParsedCSVData {
107
93
  transactions: BankTransaction[];
108
94
  format_detected: string;
@@ -1,7 +1 @@
1
- export const DEFAULT_MATCHING_CONFIG = {
2
- dateToleranceDays: 2,
3
- amountToleranceCents: 1,
4
- descriptionSimilarityThreshold: 0.8,
5
- autoMatchThreshold: 90,
6
- suggestionThreshold: 60,
7
- };
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import type * as ynab from 'ynab';
2
+ import type { NormalizedYNABTransaction } from '../../types/reconciliation.js';
3
+ export declare function normalizeYNABTransaction(txn: ynab.TransactionDetail): NormalizedYNABTransaction;
4
+ export declare function normalizeYNABTransactions(txns: ynab.TransactionDetail[]): NormalizedYNABTransaction[];
@@ -0,0 +1,15 @@
1
+ export function normalizeYNABTransaction(txn) {
2
+ return {
3
+ id: txn.id,
4
+ date: txn.date,
5
+ amount: txn.amount,
6
+ payee: txn.payee_name ?? null,
7
+ memo: txn.memo ?? null,
8
+ categoryName: txn.category_name ?? null,
9
+ cleared: txn.cleared,
10
+ approved: txn.approved,
11
+ };
12
+ }
13
+ export function normalizeYNABTransactions(txns) {
14
+ return txns.map(normalizeYNABTransaction);
15
+ }
@@ -58,13 +58,13 @@ export type CreateTransactionParams = z.infer<typeof CreateTransactionSchema>;
58
58
  type BulkTransactionInput = Omit<CreateTransactionParams, 'budget_id' | 'dry_run' | 'subtransactions'>;
59
59
  export declare const CreateTransactionsSchema: z.ZodObject<{
60
60
  budget_id: z.ZodString;
61
- transactions: z.ZodArray<z.ZodPipe<z.ZodObject<{
61
+ transactions: z.ZodArray<z.ZodObject<{
62
+ date: z.ZodString;
62
63
  cleared: z.ZodOptional<z.ZodEnum<{
63
64
  cleared: "cleared";
64
65
  uncleared: "uncleared";
65
66
  reconciled: "reconciled";
66
67
  }>>;
67
- date: z.ZodString;
68
68
  amount: z.ZodNumber;
69
69
  memo: z.ZodOptional<z.ZodString>;
70
70
  approved: z.ZodOptional<z.ZodBoolean>;
@@ -81,21 +81,7 @@ export declare const CreateTransactionsSchema: z.ZodObject<{
81
81
  category_id: z.ZodOptional<z.ZodString>;
82
82
  import_id: z.ZodOptional<z.ZodString>;
83
83
  payee_name: z.ZodOptional<z.ZodString>;
84
- subtransactions: z.ZodOptional<z.ZodAny>;
85
- }, z.core.$strict>, z.ZodTransform<BulkTransactionInput, {
86
- date: string;
87
- amount: number;
88
- account_id: string;
89
- cleared?: "cleared" | "uncleared" | "reconciled" | undefined;
90
- memo?: string | undefined;
91
- approved?: boolean | undefined;
92
- flag_color?: "red" | "orange" | "yellow" | "green" | "blue" | "purple" | undefined;
93
- payee_id?: string | undefined;
94
- category_id?: string | undefined;
95
- import_id?: string | undefined;
96
- payee_name?: string | undefined;
97
- subtransactions?: any;
98
- }>>>;
84
+ }, z.core.$strict>>;
99
85
  dry_run: z.ZodOptional<z.ZodBoolean>;
100
86
  }, z.core.$strict>;
101
87
  export type CreateTransactionsParams = z.infer<typeof CreateTransactionsSchema>;
@@ -161,23 +161,7 @@ const BulkTransactionInputSchemaBase = CreateTransactionSchema.pick({
161
161
  flag_color: true,
162
162
  import_id: true,
163
163
  });
164
- const BulkTransactionInputSchema = BulkTransactionInputSchemaBase.extend({
165
- subtransactions: z.any().optional(),
166
- })
167
- .strict()
168
- .superRefine((data, ctx) => {
169
- if (data.subtransactions !== undefined) {
170
- ctx.addIssue({
171
- code: z.ZodIssueCode.custom,
172
- message: 'Subtransactions are not supported in bulk transaction creation',
173
- path: ['subtransactions'],
174
- });
175
- }
176
- })
177
- .transform((data) => {
178
- const { subtransactions, ...rest } = data;
179
- return rest;
180
- });
164
+ const BulkTransactionInputSchema = BulkTransactionInputSchemaBase.strict();
181
165
  export const CreateTransactionsSchema = z
182
166
  .object({
183
167
  budget_id: z.string().min(1, 'Budget ID is required'),
@@ -541,6 +525,10 @@ export async function handleListTransactions(ynabAPI, deltaFetcherOrParams, mayb
541
525
  showing: `First ${preview.length} transactions:`,
542
526
  total_count: transactions.length,
543
527
  estimated_size_kb: Math.round(estimatedSize / 1024),
528
+ cached: cacheHit,
529
+ cache_info: cacheHit
530
+ ? `Data retrieved from cache for improved performance${usedDelta ? ' (delta merge applied)' : ''}`
531
+ : 'Fresh data retrieved from YNAB API',
544
532
  preview_transactions: preview.map((transaction) => ({
545
533
  id: transaction.id,
546
534
  date: transaction.date,
@@ -0,0 +1,24 @@
1
+ export interface BankTransaction {
2
+ id: string;
3
+ date: string;
4
+ amount: number;
5
+ payee: string;
6
+ memo?: string;
7
+ sourceRow: number;
8
+ raw: {
9
+ date: string;
10
+ amount: string;
11
+ description: string;
12
+ };
13
+ warnings?: string[];
14
+ }
15
+ export interface NormalizedYNABTransaction {
16
+ id: string;
17
+ date: string;
18
+ amount: number;
19
+ payee: string | null;
20
+ memo: string | null;
21
+ categoryName: string | null;
22
+ cleared: 'cleared' | 'uncleared' | 'reconciled';
23
+ approved: boolean;
24
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare class BaseError extends Error {
2
+ constructor(message: string);
3
+ }
@@ -0,0 +1,7 @@
1
+ export class BaseError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = this.constructor.name;
5
+ Object.setPrototypeOf(this, new.target.prototype);
6
+ }
7
+ }
@@ -0,0 +1,13 @@
1
+ export { BaseError } from './baseError.js';
2
+ export { ValidationError } from './validationError.js';
3
+ import { BaseError } from './baseError.js';
4
+ export declare class ConfigurationError extends BaseError {
5
+ }
6
+ export declare class AuthenticationError extends BaseError {
7
+ }
8
+ export declare class YNABRequestError extends BaseError {
9
+ status: number;
10
+ statusText: string;
11
+ ynabErrorId?: string | undefined;
12
+ constructor(status: number, statusText: string, ynabErrorId?: string | undefined);
13
+ }
@@ -0,0 +1,15 @@
1
+ export { BaseError } from './baseError.js';
2
+ export { ValidationError } from './validationError.js';
3
+ import { BaseError } from './baseError.js';
4
+ export class ConfigurationError extends BaseError {
5
+ }
6
+ export class AuthenticationError extends BaseError {
7
+ }
8
+ export class YNABRequestError extends BaseError {
9
+ constructor(status, statusText, ynabErrorId) {
10
+ super(`YNAB API request failed: ${status} ${statusText}${ynabErrorId ? ` (Error ID: ${ynabErrorId})` : ''}`);
11
+ this.status = status;
12
+ this.statusText = statusText;
13
+ this.ynabErrorId = ynabErrorId;
14
+ }
15
+ }
@@ -0,0 +1,3 @@
1
+ import { BaseError } from './baseError.js';
2
+ export declare class ValidationError extends BaseError {
3
+ }
@@ -0,0 +1,3 @@
1
+ import { BaseError } from './baseError.js';
2
+ export class ValidationError extends BaseError {
3
+ }