@dizzlkheinz/ynab-mcpb 0.17.0 → 0.18.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 (182) hide show
  1. package/.env.example +33 -33
  2. package/.github/workflows/ci-tests.yml +45 -45
  3. package/.github/workflows/claude-code-review.yml +57 -57
  4. package/.github/workflows/claude.yml +50 -50
  5. package/.github/workflows/full-integration.yml +22 -22
  6. package/.github/workflows/publish.yml +12 -3
  7. package/.github/workflows/release.yml +2 -2
  8. package/CHANGELOG.md +10 -1
  9. package/CLAUDE.md +16 -12
  10. package/README.md +6 -1
  11. package/dist/bundle/index.cjs +49 -49
  12. package/dist/server/YNABMCPServer.d.ts +125 -54
  13. package/dist/server/YNABMCPServer.js +42 -11
  14. package/dist/server/cacheManager.js +6 -5
  15. package/dist/server/completions.d.ts +25 -0
  16. package/dist/server/completions.js +160 -0
  17. package/dist/server/config.d.ts +2 -2
  18. package/dist/server/errorHandler.js +1 -0
  19. package/dist/server/rateLimiter.js +3 -1
  20. package/dist/server/resources.d.ts +1 -0
  21. package/dist/server/resources.js +33 -16
  22. package/dist/server/securityMiddleware.d.ts +38 -8
  23. package/dist/server/securityMiddleware.js +1 -0
  24. package/dist/server/toolRegistry.d.ts +9 -0
  25. package/dist/server/toolRegistry.js +11 -0
  26. package/dist/tools/adapters.d.ts +3 -1
  27. package/dist/tools/adapters.js +1 -0
  28. package/dist/tools/reconciliation/executor.d.ts +2 -0
  29. package/dist/tools/reconciliation/executor.js +26 -1
  30. package/dist/tools/reconciliation/index.d.ts +3 -2
  31. package/dist/tools/reconciliation/index.js +4 -3
  32. package/dist/tools/schemas/outputs/index.d.ts +2 -2
  33. package/dist/tools/schemas/outputs/index.js +2 -2
  34. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +0 -15
  35. package/dist/tools/schemas/outputs/utilityOutputs.js +0 -9
  36. package/dist/tools/utilityTools.d.ts +0 -7
  37. package/dist/tools/utilityTools.js +1 -50
  38. package/docs/maintainers/npm-publishing.md +27 -0
  39. package/docs/reference/API.md +83 -97
  40. package/docs/technical/reconciliation-system-architecture.md +2251 -2251
  41. package/package.json +6 -6
  42. package/scripts/analyze-bundle.mjs +41 -41
  43. package/scripts/generate-mcpb.ps1 +95 -95
  44. package/scripts/watch-and-restart.ps1 +49 -49
  45. package/src/__tests__/comprehensive.integration.test.ts +4 -32
  46. package/src/__tests__/performance.test.ts +5 -14
  47. package/src/__tests__/setup.ts +45 -14
  48. package/src/__tests__/smoke.e2e.test.ts +70 -0
  49. package/src/__tests__/testUtils.ts +2 -113
  50. package/src/server/YNABMCPServer.ts +64 -10
  51. package/src/server/__tests__/YNABMCPServer.test.ts +0 -1
  52. package/src/server/__tests__/completions.integration.test.ts +117 -0
  53. package/src/server/__tests__/completions.test.ts +319 -0
  54. package/src/server/__tests__/resources.template.test.ts +3 -3
  55. package/src/server/__tests__/resources.test.ts +3 -3
  56. package/src/server/__tests__/toolRegistration.test.ts +3 -3
  57. package/src/server/cacheManager.ts +7 -6
  58. package/src/server/completions.ts +279 -0
  59. package/src/server/errorHandler.ts +1 -0
  60. package/src/server/rateLimiter.ts +4 -1
  61. package/src/server/resources.ts +49 -13
  62. package/src/server/securityMiddleware.ts +1 -0
  63. package/src/server/toolRegistry.ts +42 -0
  64. package/src/tools/__tests__/transactionTools.integration.test.ts +63 -3
  65. package/src/tools/__tests__/utilityTools.integration.test.ts +1 -85
  66. package/src/tools/__tests__/utilityTools.test.ts +1 -123
  67. package/src/tools/adapters.ts +22 -1
  68. package/src/tools/reconciliation/__tests__/executor.progress.test.ts +462 -0
  69. package/src/tools/reconciliation/executor.ts +55 -1
  70. package/src/tools/reconciliation/index.ts +7 -3
  71. package/src/tools/schemas/outputs/index.ts +0 -3
  72. package/src/tools/schemas/outputs/utilityOutputs.ts +2 -43
  73. package/src/tools/toolCategories.ts +0 -1
  74. package/src/tools/utilityTools.ts +5 -76
  75. package/vitest.config.ts +4 -1
  76. package/.chunkhound.json +0 -11
  77. package/.code/agents/0098661e-0fa3-4990-beb9-c0cbf3f123aa/status.txt +0 -1
  78. package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +0 -3
  79. package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +0 -3
  80. package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +0 -1
  81. package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +0 -5
  82. package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +0 -1569
  83. package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +0 -3
  84. package/.code/agents/1324/exec-call_tIpx9uV1TpARbAMZonRQm8AO.txt +0 -757
  85. package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +0 -2832
  86. package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +0 -2709
  87. package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +0 -2832
  88. package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +0 -2832
  89. package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +0 -2709
  90. package/.code/agents/1572/exec-call_GjVFBFOWcY7lE0idc5nWlLNh.txt +0 -781
  91. package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +0 -1
  92. package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +0 -5217
  93. package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +0 -2594
  94. package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +0 -2594
  95. package/.code/agents/1846/exec-call_1YNAVD18RjrMN7JnfkkQhUP3.txt +0 -766
  96. package/.code/agents/1846/exec-call_lh3lDzE4WJAh1lFiomiiZ73D.txt +0 -766
  97. package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +0 -231
  98. package/.code/agents/2038/exec-call_DYwOukaYsL8VCONWmV2rUW5u.txt +0 -766
  99. package/.code/agents/2038/exec-call_c7fOQ7UrpVcTtvdfGBRM146V.txt +0 -652
  100. package/.code/agents/2038/exec-call_ySNyq9Mm55jWE480s54r5QcA.txt +0 -766
  101. package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +0 -2590
  102. package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +0 -5195
  103. package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +0 -286
  104. package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +0 -218
  105. package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +0 -180
  106. package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +0 -3
  107. package/.code/agents/2256/exec-call_AtPcRWPmFPMcmX6qOFm1fCEY.txt +0 -766
  108. package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +0 -1
  109. package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +0 -747
  110. package/.code/agents/2454/exec-call_aFJpupwjfZeOBm7ixI5Vc8z2.txt +0 -766
  111. package/.code/agents/2454/exec-call_wogZ4HfXTodTEXvdgXlVUBpv.txt +0 -766
  112. package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +0 -1
  113. package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +0 -2594
  114. package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +0 -2594
  115. package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +0 -144
  116. package/.code/agents/2e905864-aa07-4314-bcf9-c5b32277e4ac/result.txt +0 -36
  117. package/.code/agents/3073/exec-call_Peeagc9DxGYLgE6pNdMZhqIE.txt +0 -766
  118. package/.code/agents/3073/exec-call_d2YSE3hXF08KRSoUM3qd8Z3x.txt +0 -766
  119. package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +0 -416
  120. package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +0 -2590
  121. package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +0 -2590
  122. package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +0 -2590
  123. package/.code/agents/335aa031-466d-4fb7-925f-3cd864e264d0/result.txt +0 -191
  124. package/.code/agents/3364/exec-call_NbhIrsM5HhyDZDmJZG5CuCYL.txt +0 -766
  125. package/.code/agents/3364/exec-call_cKtJg0NrXiwXEFwlsE3uPZRA.txt +0 -766
  126. package/.code/agents/36d98414-5cde-4d9d-9a67-a240a18c1f07/result.txt +0 -189
  127. package/.code/agents/4604e866-b7b8-44f5-992f-2f683b0a523b/status.txt +0 -1
  128. package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +0 -790
  129. package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +0 -766
  130. package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +0 -790
  131. package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +0 -2594
  132. package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +0 -1000
  133. package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +0 -3489
  134. package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +0 -766
  135. package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +0 -2594
  136. package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +0 -2456
  137. package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +0 -2594
  138. package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +0 -18
  139. package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +0 -48
  140. package/.code/agents/5f8dc01c-47b3-4163-b0b3-aa31be89fcdc/status.txt +0 -1
  141. package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +0 -1
  142. package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +0 -1
  143. package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +0 -1
  144. package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +0 -1
  145. package/.code/agents/7/exec-call_HltHpkDox0Zm1vGEjdksUgpE.txt +0 -1120
  146. package/.code/agents/7/exec-call_LCATrOPPAgbxW9Q1z0XaVi2E.txt +0 -2646
  147. package/.code/agents/7/exec-call_W8DeRfNG9hvbgVFvf0clBf6R.txt +0 -2646
  148. package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +0 -1271
  149. package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +0 -1570
  150. package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +0 -2590
  151. package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +0 -3
  152. package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +0 -3299
  153. package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +0 -3299
  154. package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +0 -1882
  155. package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +0 -2594
  156. package/.code/agents/94a0ddf3-a304-4ec3-913e-3cceef509948/error.txt +0 -1
  157. package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +0 -1
  158. package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +0 -170
  159. package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +0 -1
  160. package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +0 -3
  161. package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +0 -1
  162. package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +0 -1
  163. package/.code/agents/e2c752b7-711d-423a-af57-f53c809deb84/result.txt +0 -160
  164. package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +0 -3
  165. package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +0 -1
  166. package/.code/agents/e6601719-c31f-4a0e-8c71-d70787d0ab71/status.txt +0 -1
  167. package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +0 -1
  168. package/.code/agents/f250b7ed-5bd5-4036-aa8c-ce63caee7d61/result.txt +0 -20
  169. package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +0 -5
  170. package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +0 -1
  171. package/AGENTS.md +0 -1
  172. package/NUL +0 -0
  173. package/package.json.tmp +0 -105
  174. package/src/__tests__/delta.performance.test.ts +0 -80
  175. package/src/__tests__/workflows.e2e.test.ts +0 -1702
  176. package/temp-recon.ts +0 -126
  177. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +0 -23
  178. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +0 -23
  179. package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +0 -44
  180. package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +0 -58
  181. package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +0 -3662
  182. package/test-exports/ynab_since_2025-11-01_account_4c18e9f0_minimal_14items_2025-11-16_10-07-10.json +0 -115
@@ -1,416 +0,0 @@
1
- src\types\reconciliation.ts:4: * AMOUNTS ARE IN MILLIUNITS (integers, 1000 = $1.00).
2
- src\types\reconciliation.ts:12: /** Amount in MILLIUNITS (negative = outflow, positive = inflow) */
3
- src\types\reconciliation.ts:36: * AMOUNTS ARE IN MILLIUNITS - same as YNAB API native format.
4
- src\types\reconciliation.ts:42: /** Amount in MILLIUNITS (same as YNAB API) */
5
- src\__tests__\workflows.e2e.test.ts:825: it('should convert amounts between dollars and milliunits', async () => {
6
- src\__tests__\workflows.e2e.test.ts:828: // Convert dollars to milliunits
7
- src\__tests__\workflows.e2e.test.ts:829: const toMilliunitsResult = await executeToolCall(server, 'ynab:convert_amount', {
8
- src\__tests__\workflows.e2e.test.ts:831: to_milliunits: true,
9
- src\__tests__\workflows.e2e.test.ts:833: const milliunits = parseToolResult(toMilliunitsResult);
10
- src\__tests__\workflows.e2e.test.ts:835: expect(milliunits.data?.conversion?.converted_amount).toBe(25500);
11
- src\__tests__\workflows.e2e.test.ts:836: expect(milliunits.data?.conversion?.description).toContain('25500');
12
- src\__tests__\workflows.e2e.test.ts:837: expect(milliunits.data?.conversion?.to_milliunits).toBe(true);
13
- src\__tests__\workflows.e2e.test.ts:839: // Convert milliunits to dollars
14
- src\__tests__\workflows.e2e.test.ts:842: to_milliunits: false,
15
- src\__tests__\workflows.e2e.test.ts:848: expect(dollars.data?.conversion?.to_milliunits).toBe(false);
16
- src\__tests__\workflows.e2e.test.ts:1146: { name: 'ynab:convert_amount', args: { amount: 100, to_milliunits: true } },
17
- src\__tests__\workflows.e2e.test.ts:1629: to_milliunits: true,
18
- src\server\prompts.ts:154:4. Create the transaction with the correct amount in milliunits (multiply by 1000)
19
- src\server\prompts.ts:233:Convert milliunits to dollars for easy reading.`,
20
- src\__tests__\performance.test.ts:629: to_milliunits: true,
21
- src\__tests__\performance.test.ts:718: executeToolCall(server, 'ynab:convert_amount', { amount: i * 10, to_milliunits: true }),
22
- src\__tests__\comprehensive.integration.test.ts:53: convertMilliUnitsToCurrencyAmount: vi.fn(
23
- src\__tests__\comprehensive.integration.test.ts:54: (milliunits: number, currencyDecimalDigits: number = 2) => {
24
- src\__tests__\comprehensive.integration.test.ts:55: const amount = milliunits / 1000;
25
- src\__tests__\comprehensive.integration.test.ts:59: convertCurrencyAmountToMilliUnits: vi.fn((amount: number) => Math.round(amount * 1000)),
26
- src\__tests__\comprehensive.integration.test.ts:68: 'converts milliunits using SDK rounding rules',
27
- src\__tests__\comprehensive.integration.test.ts:73: expect(utils.convertMilliUnitsToCurrencyAmount(123456, 2)).toBe(123.46);
28
- src\__tests__\comprehensive.integration.test.ts:74: expect(utils.convertMilliUnitsToCurrencyAmount(123456, 3)).toBe(123.456);
29
- src\__tests__\comprehensive.integration.test.ts:75: expect(utils.convertMilliUnitsToCurrencyAmount(-98765, 2)).toBe(-98.77);
30
- src\__tests__\comprehensive.integration.test.ts:756: // Test dollar to milliunits conversion
31
- src\__tests__\comprehensive.integration.test.ts:757: const toMilliunitsResult = await executeToolCall(server, 'ynab:convert_amount', {
32
- src\__tests__\comprehensive.integration.test.ts:759: to_milliunits: true,
33
- src\__tests__\comprehensive.integration.test.ts:761: validateToolResult(toMilliunitsResult);
34
- src\__tests__\comprehensive.integration.test.ts:763: const toMilli = parseToolResult(toMilliunitsResult);
35
- src\__tests__\comprehensive.integration.test.ts:765: expect(toMilli.data.conversion.description).toBe('$25.75 = 25750 milliunits');
36
- src\__tests__\comprehensive.integration.test.ts:767: // Test milliunits to dollar conversion
37
- src\__tests__\comprehensive.integration.test.ts:770: to_milliunits: false,
38
- src\__tests__\comprehensive.integration.test.ts:776: expect(dollars.data.conversion.description).toBe('25750 milliunits = $25.75');
39
- src\tools\monthTools.ts:6:import { milliunitsToAmount } from '../utils/amountUtils.js';
40
- src\tools\monthTools.ts:69: income: milliunitsToAmount(month.income),
41
- src\tools\monthTools.ts:70: budgeted: milliunitsToAmount(month.budgeted),
42
- src\tools\monthTools.ts:71: activity: milliunitsToAmount(month.activity),
43
- src\tools\monthTools.ts:72: to_be_budgeted: milliunitsToAmount(month.to_be_budgeted),
44
- src\tools\monthTools.ts:83: budgeted: milliunitsToAmount(category.budgeted),
45
- src\tools\monthTools.ts:84: activity: milliunitsToAmount(category.activity),
46
- src\tools\monthTools.ts:85: balance: milliunitsToAmount(category.balance),
47
- src\tools\monthTools.ts:151: income: milliunitsToAmount(month.income),
48
- src\tools\monthTools.ts:152: budgeted: milliunitsToAmount(month.budgeted),
49
- src\tools\monthTools.ts:153: activity: milliunitsToAmount(month.activity),
50
- src\tools\monthTools.ts:154: to_be_budgeted: milliunitsToAmount(month.to_be_budgeted),
51
- src\tools\toolCategories.ts:123: * - convert_amount: Converts between dollars and milliunits
52
- src\__tests__\testRunner.ts:349:- Milliunits handling
53
- src\__tests__\tools\reconciliation\csvParser.integration.test.ts:18: expect(result.transactions[0].amount).toBe(-45230); // Milliunits!
54
- src\__tests__\tools\reconciliation\csvParser.integration.test.ts:34: expect(result.transactions[0].amount).toBe(-45230); // Debit = negative milliunits
55
- src\__tests__\tools\reconciliation\csvParser.integration.test.ts:35: expect(result.transactions[1].amount).toBe(500000); // Credit = positive milliunits
56
- src\__tests__\tools\reconciliation\csvParser.integration.test.ts:59: expect(result.transactions[0].amount).toBe(1234560); // 1234.56 in milliunits
57
- src\__tests__\tools\reconciliation\csvParser.integration.test.ts:94: // Shell: exact amount match (both -45230 milliunits)
58
- src\__tests__\tools\reconciliation\csvParser.integration.test.ts:98: // Amazon: exact amount match (both -127990 milliunits)
59
- src\__tests__\tools\reconciliation\csvParser.integration.test.ts:108: amount: -45230, // Integer milliunits
60
- src\__tests__\tools\reconciliation\csvParser.integration.test.ts:117: amount: -45230, // Integer milliunits - direct from YNAB API
61
- src\tools\accountTools.ts:6:import { milliunitsToAmount } from '../utils/amountUtils.js';
62
- src\tools\accountTools.ts:108: balance: milliunitsToAmount(account.balance),
63
- src\tools\accountTools.ts:109: cleared_balance: milliunitsToAmount(account.cleared_balance),
64
- src\tools\accountTools.ts:110: uncleared_balance: milliunitsToAmount(account.uncleared_balance),
65
- src\tools\accountTools.ts:172: balance: milliunitsToAmount(account.balance),
66
- src\tools\accountTools.ts:173: cleared_balance: milliunitsToAmount(account.cleared_balance),
67
- src\tools\accountTools.ts:174: uncleared_balance: milliunitsToAmount(account.uncleared_balance),
68
- src\tools\accountTools.ts:242: balance: params.balance ? params.balance * 1000 : 0, // Convert to milliunits
69
- src\tools\accountTools.ts:273: balance: milliunitsToAmount(account.balance),
70
- src\tools\accountTools.ts:274: cleared_balance: milliunitsToAmount(account.cleared_balance),
71
- src\tools\accountTools.ts:275: uncleared_balance: milliunitsToAmount(account.uncleared_balance),
72
- src\tools\compareTransactions\types.ts:11: /** Transaction amount in milliunits (YNAB format) */
73
- src\tools\compareTransactions\types.ts:31: /** Transaction amount in milliunits */
74
- src\__tests__\testUtils.ts:362: * Generate test amounts in milliunits
75
- src\tools\compareTransactions\parser.ts:47: * Convert dollar amount to milliunits
76
- src\tools\compareTransactions\parser.ts:49:export function amountToMilliunits(amountStr: string): Milli {
77
- src\tools\compareTransactions\parser.ts:482: amount = amountToMilliunits(rawAmount);
78
- src\tools\reconciliation\csvParser.ts:193: * IMPORTANT: Amounts are converted to MILLIUNITS (integers) at this boundary.
79
- src\tools\reconciliation\csvParser.ts:194: * This is the ONLY place where float-to-milliunit conversion happens.
80
- src\tools\reconciliation\csvParser.ts:364: let amountMilliunits: number;
81
- src\tools\reconciliation\csvParser.ts:369: amountMilliunits = dollarStringToMilliunits(rawAmount);
82
- src\tools\reconciliation\csvParser.ts:375: const debitMilliunits = dollarStringToMilliunits(debit);
83
- src\tools\reconciliation\csvParser.ts:376: const creditMilliunits = dollarStringToMilliunits(credit);
84
- src\tools\reconciliation\csvParser.ts:379: if (Math.abs(debitMilliunits) > 0 && Math.abs(creditMilliunits) > 0) {
85
- src\tools\reconciliation\csvParser.ts:385: if (Math.abs(debitMilliunits) > 0) {
86
- src\tools\reconciliation\csvParser.ts:386: amountMilliunits = -Math.abs(debitMilliunits); // Debits are outflows (negative)
87
- src\tools\reconciliation\csvParser.ts:387: } else if (Math.abs(creditMilliunits) > 0) {
88
- src\tools\reconciliation\csvParser.ts:388: amountMilliunits = Math.abs(creditMilliunits); // Credits are inflows (positive)
89
- src\tools\reconciliation\csvParser.ts:390: amountMilliunits = 0;
90
- src\tools\reconciliation\csvParser.ts:394: if (debitMilliunits < 0) {
91
- src\tools\reconciliation\csvParser.ts:403: if (!Number.isFinite(amountMilliunits)) {
92
- src\tools\reconciliation\csvParser.ts:415: amountMilliunits *= multiplier;
93
- src\tools\reconciliation\csvParser.ts:423: amount: amountMilliunits,
94
- src\tools\reconciliation\csvParser.ts:555:function dollarStringToMilliunits(str: string): number {
95
- src\tools\reconciliation\csvParser.ts:578: // Convert to milliunits: $1.00 → 1000
96
- src\tools\categoryTools.ts:6:import { milliunitsToAmount } from '../utils/amountUtils.js';
97
- src\tools\categoryTools.ts:44: budgeted: z.number().int('Budgeted amount must be an integer in milliunits'),
98
- src\tools\categoryTools.ts:52: * Convert goal-related monetary fields from milliunits to dollars.
99
- src\tools\categoryTools.ts:58: category.goal_target != null ? milliunitsToAmount(category.goal_target) : undefined,
100
- src\tools\categoryTools.ts:61: ? milliunitsToAmount(category.goal_under_funded)
101
- src\tools\categoryTools.ts:65: ? milliunitsToAmount(category.goal_overall_funded)
102
- src\tools\categoryTools.ts:69: ? milliunitsToAmount(category.goal_overall_left)
103
- src\tools\categoryTools.ts:113: budgeted: milliunitsToAmount(category.budgeted),
104
- src\tools\categoryTools.ts:114: activity: milliunitsToAmount(category.activity),
105
- src\tools\categoryTools.ts:115: balance: milliunitsToAmount(category.balance),
106
- src\tools\categoryTools.ts:191: budgeted: milliunitsToAmount(category.budgeted),
107
- src\tools\categoryTools.ts:192: activity: milliunitsToAmount(category.activity),
108
- src\tools\categoryTools.ts:193: balance: milliunitsToAmount(category.balance),
109
- src\tools\categoryTools.ts:253: budgeted: milliunitsToAmount(params.budgeted),
110
- src\tools\categoryTools.ts:320: budgeted: milliunitsToAmount(category.budgeted),
111
- src\tools\categoryTools.ts:321: activity: milliunitsToAmount(category.activity),
112
- src\tools\categoryTools.ts:322: balance: milliunitsToAmount(category.balance),
113
- src\tools\reconciliation\analyzer.ts:5: * V2 UPDATE: Uses new parser and matcher (milliunits based)
114
- src\tools\reconciliation\analyzer.ts:48: const amount = txn.amount / 1000; // Convert from milliunits to dollars
115
- src\tools\reconciliation\index.ts:170: amountToleranceMilliunits: (params.amount_tolerance_cents ?? 1) * 10,
116
- src\tools\reconcileAdapter.ts:44: bank_statement_balance_milliunits: number;
117
- src\tools\reconcileAdapter.ts:45: ynab_calculated_balance_milliunits: number;
118
- src\tools\reconcileAdapter.ts:46: discrepancy_milliunits: number;
119
- src\tools\reconcileAdapter.ts:54: amount_milliunits: number;
120
- src\tools\reconcileAdapter.ts:123: const discrepancyMilli = analysis.balance_info.discrepancy.value_milliunits;
121
- src\tools\reconcileAdapter.ts:148: bank_statement_balance: toMoneyValue(precision.bank_statement_balance_milliunits, currency),
122
- src\tools\reconcileAdapter.ts:149: ynab_calculated_balance: toMoneyValue(precision.ynab_calculated_balance_milliunits, currency),
123
- src\tools\reconcileAdapter.ts:150: discrepancy: toMoneyValue(precision.discrepancy_milliunits, currency),
124
- src\tools\reconcileAdapter.ts:165: amount: toMoneyValue(cause.amount_milliunits, currency),
125
- src\tools\reconciliation\executor.ts:14: balance: number; // milliunits
126
- src\tools\reconciliation\executor.ts:15: cleared_balance: number; // milliunits
127
- src\tools\reconciliation\executor.ts:16: uncleared_balance: number; // milliunits
128
- src\tools\reconciliation\executor.ts:782: bank_statement_balance_milliunits: bankMilli,
129
- src\tools\reconciliation\executor.ts:783: ynab_calculated_balance_milliunits: ynabMilli,
130
- src\tools\reconciliation\executor.ts:784: discrepancy_milliunits: discrepancy,
131
- src\tools\reconciliation\executor.ts:857: amount_milliunits: number;
132
- src\tools\reconciliation\executor.ts:868: amount_milliunits: discrepancyMilli,
133
- src\tools\reconciliation\executor.ts:957: 'value_milliunits' in value &&
134
- src\tools\reconciliation\executor.ts:958: typeof (value as { value_milliunits: unknown }).value_milliunits === 'number'
135
- src\tools\reconciliation\executor.ts:960: return (value as { value_milliunits: number }).value_milliunits;
136
- src\utils\money.ts:4:export type Milli = number; // integer milliunits (no bigint)
137
- src\utils\money.ts:9: value_milliunits: Milli;
138
- src\utils\money.ts:45: * Convert milliunits to currency amount using proper decimal digits
139
- src\utils\money.ts:47: * @param m - Milliunits value
140
- src\utils\money.ts:51: return ynab.utils.convertMilliUnitsToCurrencyAmount(m, decimalDigits);
141
- src\utils\money.ts:54:export const assertMilli = (m: number, msg = 'Expected safe integer milliunits') => {
142
- src\utils\money.ts:60: if (!Number.isSafeInteger(s)) throw new Error('Milliunit sum overflow');
143
- src\utils\money.ts:84: * Format milliunits as currency string with proper decimal digits
144
- src\utils\money.ts:85: * @param value - Milliunits value
145
- src\utils\money.ts:96: * Convert milliunits to MoneyValue with proper currency format
146
- src\utils\money.ts:97: * @param value - Milliunits value
147
- src\utils\money.ts:106: value_milliunits: value,
148
- src\tools\reconciliation\recommendationEngine.ts:182: amount: toMilli(bankTxn.amount), // Convert dollars to milliunits for create_transaction
149
- src\tools\reconciliation\recommendationEngine.ts:222: // Calculate total amount from candidates for context (convert from milliunits to decimal)
150
- src\tools\reconciliation\recommendationEngine.ts:428: amount: toMilli(txn.amount), // Convert dollars to milliunits for create_transaction
151
- src\tools\reconciliation\reportFormatter.ts:93: const discrepancyMilli = balanceInfo.discrepancy.value_milliunits;
152
- src\tools\reconciliation\reportFormatter.ts:352: const amount = txn.amount / 1000; // Convert milliunits to dollars
153
- src\tools\reconciliation\types.ts:37: /** Amount in milliunits */
154
- src\tools\reconciliation\types.ts:175: // Tolerances (in MILLIUNITS for amount)
155
- src\tools\reconciliation\types.ts:176: amountToleranceMilliunits: number; // Default: 10 (1 cent)
156
- src\tools\transactionTools.ts:10:import { amountToMilliunits, milliunitsToAmount } from '../utils/amountUtils.js';
157
- src\tools\transactionTools.ts:173: amount: z.number().int('Amount must be an integer in milliunits'),
158
- src\tools\transactionTools.ts:188: amount: z.number().int('Subtransaction amount must be an integer in milliunits'),
159
- src\tools\transactionTools.ts:626: amount: z.number().int('Amount must be an integer in milliunits').optional(),
160
- src\tools\transactionTools.ts:651: amount: z.number().int('Amount must be an integer in milliunits').optional(),
161
- src\tools\transactionTools.ts:808: amount: milliunitsToAmount(transaction.amount),
162
- src\tools\transactionTools.ts:832: amount: milliunitsToAmount(transaction.amount),
163
- src\tools\transactionTools.ts:906: amount: milliunitsToAmount(transaction.amount),
164
- src\tools\transactionTools.ts:979: amount: params.amount, // Already validated as integer milliunits
165
- src\tools\transactionTools.ts:1047: amount: milliunitsToAmount(transaction.amount),
166
- src\tools\transactionTools.ts:1066: amount: milliunitsToAmount(subtransaction.amount),
167
- src\tools\transactionTools.ts:1089: subtotal_milliunits: number;
168
- src\tools\transactionTools.ts:1090: tax_milliunits: number;
169
- src\tools\transactionTools.ts:1093: amount_milliunits: number;
170
- src\tools\transactionTools.ts:1123: subtotalMilliunits: number,
171
- src\tools\transactionTools.ts:1124: totalTaxMilliunits: number,
172
- src\tools\transactionTools.ts:1127: if (totalTaxMilliunits === 0) {
173
- src\tools\transactionTools.ts:1128: for (const category of categories) category.tax_milliunits = 0;
174
- src\tools\transactionTools.ts:1132: if (subtotalMilliunits <= 0) {
175
- src\tools\transactionTools.ts:1139: category.tax_milliunits = totalTaxMilliunits - allocated;
176
- src\tools\transactionTools.ts:1142: (totalTaxMilliunits * category.subtotal_milliunits) / subtotalMilliunits,
177
- src\tools\transactionTools.ts:1144: category.tax_milliunits = proportionalTax;
178
- src\tools\transactionTools.ts:1176: amount_milliunits: amountToMilliunits(item.amount),
179
- src\tools\transactionTools.ts:1180: const subtotalMilliunits = items.reduce((sum, item) => sum + item.amount_milliunits, 0);
180
- src\tools\transactionTools.ts:1184: subtotal_milliunits: subtotalMilliunits,
181
- src\tools\transactionTools.ts:1185: tax_milliunits: 0,
182
- src\tools\transactionTools.ts:1190: const subtotalMilliunits = categoryCalculations.reduce(
183
- src\tools\transactionTools.ts:1191: (sum, category) => sum + category.subtotal_milliunits,
184
- src\tools\transactionTools.ts:1195: const declaredSubtotalMilliunits =
185
- src\tools\transactionTools.ts:1196: params.receipt_subtotal !== undefined ? amountToMilliunits(params.receipt_subtotal) : undefined;
186
- src\tools\transactionTools.ts:1198: declaredSubtotalMilliunits !== undefined &&
187
- src\tools\transactionTools.ts:1199: Math.abs(declaredSubtotalMilliunits - subtotalMilliunits) > 1
188
- src\tools\transactionTools.ts:1202: `Categorized items subtotal (${milliunitsToAmount(subtotalMilliunits)}) does not match receipt subtotal (${milliunitsToAmount(declaredSubtotalMilliunits)})`,
189
- src\tools\transactionTools.ts:1206: const taxMilliunits = amountToMilliunits(params.receipt_tax);
190
- src\tools\transactionTools.ts:1207: const totalMilliunits = amountToMilliunits(params.receipt_total);
191
- src\tools\transactionTools.ts:1208: const computedTotal = subtotalMilliunits + taxMilliunits;
192
- src\tools\transactionTools.ts:1209: if (Math.abs(computedTotal - totalMilliunits) > 1) {
193
- src\tools\transactionTools.ts:1211: `Receipt total (${milliunitsToAmount(totalMilliunits)}) does not equal subtotal plus tax (${milliunitsToAmount(computedTotal)})`,
194
- src\tools\transactionTools.ts:1215: distributeTaxProportionally(subtotalMilliunits, taxMilliunits, categoryCalculations);
195
- src\tools\transactionTools.ts:1221: amount: -item.amount_milliunits,
196
- src\tools\transactionTools.ts:1229: category.tax_milliunits > 0
197
- src\tools\transactionTools.ts:1232: amount: -category.tax_milliunits,
198
- src\tools\transactionTools.ts:1243: subtotal: milliunitsToAmount(subtotalMilliunits),
199
- src\tools\transactionTools.ts:1244: tax: milliunitsToAmount(taxMilliunits),
200
- src\tools\transactionTools.ts:1245: total: milliunitsToAmount(totalMilliunits),
201
- src\tools\transactionTools.ts:1252: amount: milliunitsToAmount(item.amount_milliunits),
202
- src\tools\transactionTools.ts:1255: subtotal: milliunitsToAmount(category.subtotal_milliunits),
203
- src\tools\transactionTools.ts:1256: tax: milliunitsToAmount(category.tax_milliunits),
204
- src\tools\transactionTools.ts:1257: total: milliunitsToAmount(category.subtotal_milliunits + category.tax_milliunits),
205
- src\tools\transactionTools.ts:1273: amount: milliunitsToAmount(totalMilliunits),
206
- src\tools\transactionTools.ts:1278: amount: milliunitsToAmount(-subtransaction.amount),
207
- src\tools\transactionTools.ts:1291: amount: -totalMilliunits,
208
- src\tools\transactionTools.ts:1494: amount: milliunitsToAmount(transaction.amount),
209
- src\tools\transactionTools.ts:1733: amount: milliunitsToAmount(transaction.amount),
210
- src\tools\transactionTools.ts:1751: total_amount: milliunitsToAmount(totalAmount),
211
- src\tools\transactionTools.ts:2132: before['amount'] = milliunitsToAmount(currentState.amount);
212
- src\tools\transactionTools.ts:2133: after['amount'] = milliunitsToAmount(transaction.amount);
213
- src\tools\utilityTools.ts:13: to_milliunits: z.boolean(),
214
- src\tools\utilityTools.ts:49: * Converts between dollars and milliunits with integer arithmetic for precision
215
- src\tools\utilityTools.ts:54: const { amount, to_milliunits } = params;
216
- src\tools\utilityTools.ts:59: if (to_milliunits) {
217
- src\tools\utilityTools.ts:60: // Convert from dollars to milliunits
218
- src\tools\utilityTools.ts:63: description = `$${amount.toFixed(2)} = ${result} milliunits`;
219
- src\tools\utilityTools.ts:65: // Convert from milliunits to dollars
220
- src\tools\utilityTools.ts:66: // Assume input amount is in milliunits
221
- src\tools\utilityTools.ts:68: description = `${amount} milliunits = $${result.toFixed(2)}`;
222
- src\tools\utilityTools.ts:79: to_milliunits,
223
- src\tools\reconciliation\__tests__\adapter.causes.test.ts:59: bank_statement_balance_milliunits: -921240,
224
- src\tools\reconciliation\__tests__\adapter.causes.test.ts:60: ynab_calculated_balance_milliunits: -899020,
225
- src\tools\reconciliation\__tests__\adapter.causes.test.ts:61: discrepancy_milliunits: -22220,
226
- src\tools\reconciliation\__tests__\adapter.causes.test.ts:72: amount_milliunits: -1500,
227
- src\tools\reconciliation\__tests__\adapter.causes.test.ts:80: amount_milliunits: 500,
228
- src\tools\reconciliation\__tests__\adapter.test.ts:9: value_milliunits: Math.round(value * 1000),
229
- src\tools\reconciliation\__tests__\adapter.test.ts:149: expect(structured.matches.auto[0].bank_transaction.amount_money.value_milliunits).toBe(-45230);
230
- src\tools\reconciliation\__tests__\adapter.test.ts:183: bank_statement_balance_milliunits: -921240,
231
- src\tools\reconciliation\__tests__\adapter.test.ts:184: ynab_calculated_balance_milliunits: -899020,
232
- src\tools\reconciliation\__tests__\adapter.test.ts:185: discrepancy_milliunits: -22220,
233
- src\tools\reconciliation\__tests__\adapter.test.ts:195: amount_milliunits: 22220,
234
- src\tools\reconciliation\__tests__\adapter.test.ts:224: expect(structured.execution.account_balance.after.cleared_balance.value_milliunits).toBe(
235
- src\tools\reconciliation\matcher.ts:4: * V2 matcher works natively in milliunits using canonical BankTransaction
236
- src\tools\reconciliation\matcher.ts:50: amountToleranceMilliunits: 10, // 1 cent
237
- src\tools\reconciliation\matcher.ts:67: amountToleranceMilliunits:
238
- src\tools\reconciliation\matcher.ts:68: config.amountToleranceMilliunits ?? DEFAULT_CONFIG.amountToleranceMilliunits,
239
- src\tools\reconciliation\matcher.ts:293: if (amountDiff > config.amountToleranceMilliunits) {
240
- src\tools\reconciliation\matcher.ts:338: // Amount score - now using INTEGER comparison (milliunits)
241
- src\tools\reconciliation\matcher.ts:345: } else if (amountDiff <= config.amountToleranceMilliunits) {
242
- src\tools\reconciliation\__tests__\analyzer.test.ts:24: amount: -45230, // milliunits
243
- src\tools\reconciliation\__tests__\analyzer.test.ts:33: amount: -100000, // milliunits
244
- src\tools\reconciliation\ynabAdapter.ts:10: * NOTE: Amount stays in milliunits - no conversion needed since
245
- src\tools\reconciliation\ynabAdapter.ts:11: * YNAB API already uses milliunits natively.
246
- src\tools\reconciliation\ynabAdapter.ts:17: amount: txn.amount, // Already in milliunits - no conversion!
247
- src\server\YNABMCPServer.ts:901: description: 'Convert between dollars and milliunits with integer arithmetic for precision',
248
- src\utils\amountUtils.ts:2: * Utility functions for converting between YNAB milliunits and dollars
249
- src\utils\amountUtils.ts:6: * Converts an amount from milliunits to dollars
250
- src\utils\amountUtils.ts:7: * @param milliunits - Amount in milliunits (1000 milliunits = $1.00)
251
- src\utils\amountUtils.ts:10:export function milliunitsToAmount(milliunits: number): number {
252
- src\utils\amountUtils.ts:11: return Math.round(milliunits) / 1000;
253
- src\utils\amountUtils.ts:15: * Converts an amount from dollars to milliunits
254
- src\utils\amountUtils.ts:17: * @returns Amount in milliunits
255
- src\utils\amountUtils.ts:19:export function amountToMilliunits(amount: number): number {
256
- src\utils\amountUtils.ts:24: * Formats an amount from milliunits to a currency string
257
- src\utils\amountUtils.ts:25: * @param milliunits - Amount in milliunits
258
- src\utils\amountUtils.ts:29:export function formatAmount(milliunits: number, currencySymbol = '$'): string {
259
- src\utils\amountUtils.ts:30: const amount = milliunitsToAmount(milliunits);
260
- src\tools\reconciliation\__tests__\recommendationEngine.test.ts:260: expect(rec.parameters.amount).toBe(-75500); // In milliunits
261
- src\tools\reconciliation\__tests__\recommendationEngine.test.ts:458: expect(rec.parameters.amount).toBe(-45000); // In milliunits
262
- src\tools\reconciliation\__tests__\recommendationEngine.test.ts:548: expect(rec.parameters.amount).toBe(-123450); // In milliunits
263
- src\tools\reconciliation\__tests__\recommendationEngine.test.ts:569: expect(rec.parameters.amount).toBe(500000); // In milliunits
264
- src\tools\reconciliation\__tests__\recommendationEngine.test.ts:594: expect(rec.parameters.amount).toBe(-99990); // In milliunits
265
- src\tools\reconciliation\__tests__\recommendationEngine.test.ts:610: expect(rec.parameters.amount).toBe(0); // Zero in milliunits is still zero
266
- src\tools\reconciliation\__tests__\recommendationEngine.test.ts:921: expect(rec.parameters.amount).toBe(-10); // In milliunits (0.01 * 1000)
267
- src\tools\reconciliation\__tests__\recommendationEngine.test.ts:937: expect(rec.parameters.amount).toBe(-999999990); // In milliunits
268
- src\tools\reconciliation\__tests__\recommendationEngine.integration.test.ts:17: amount: number, // in milliunits
269
- src\tools\reconciliation\__tests__\recommendationEngine.integration.test.ts:87: expect(rec.parameters.amount).toBe(22220); // In milliunits
270
- src\tools\reconciliation\__tests__\recommendationEngine.integration.test.ts:141: expect(evoCarShareRec!.parameters.amount).toBe(22220); // In milliunits
271
- src\tools\reconciliation\__tests__\recommendationEngine.integration.test.ts:552: // 500 dollars in milliunits
272
- src\tools\reconciliation\__tests__\recommendationEngine.integration.test.ts:651: expect(createRec!.parameters.amount).toBe(25500); // In milliunits
273
- src\tools\reconciliation\__tests__\matcher.test.ts:15: amountToleranceMilliunits: 10,
274
- src\tools\reconciliation\__tests__\matcher.test.ts:41: amount: -45230, // milliunits
275
- src\tools\reconciliation\__tests__\matcher.test.ts:339: config.amountToleranceMilliunits = 10; // 1 cent
276
- src\tools\reconciliation\__tests__\matcher.test.ts:532: amountToleranceMilliunits: 100, // 10 cents
277
- src\tools\reconciliation\__tests__\matcher.test.ts:626: amount: -10, // 1 cent in milliunits
278
- src\tools\reconciliation\__tests__\matcher.test.ts:652: amount: -10000000, // $10,000 in milliunits
279
- src\tools\reconciliation\__tests__\reportFormatter.test.ts:20: value_milliunits: Math.round(value * 1000),
280
- src\tools\schemas\outputs\accountOutputs.ts:68: * Represents account data with balances in dollars (converted from YNAB milliunits).
281
- src\tools\__tests__\accountTools.test.ts:401: // Verify the API was called with balance 0 in milliunits
282
- src\tools\__tests__\accountTools.test.ts:411: it('should convert balance to milliunits', async () => {
283
- src\tools\__tests__\accountTools.test.ts:435: balance: 150, // $150 should become 150000 milliunits
284
- src\tools\__tests__\accountTools.test.ts:438: // Verify the API was called with balance converted to milliunits
285
- src\tools\reconciliation\__tests__\scenarios\adapterCurrency.scenario.test.ts:6: value_milliunits: Math.round(value * 1000),
286
- src\tools\reconciliation\__tests__\scenarios\repeatAmount.scenario.test.ts:18: // Three -22.22 transactions in milliunits: one will match YNAB, two will remain unmatched
287
- src\tools\schemas\outputs\categoryOutputs.ts:9: * goal_overall_funded, goal_overall_left) are converted from YNAB API milliunits to
288
- src\tools\schemas\outputs\categoryOutputs.ts:123: /** Goal target amount in dollars (converted from YNAB API milliunits, optional) */
289
- src\tools\schemas\outputs\comparisonOutputs.ts:344: * IMPORTANT: `amount` is the raw YNAB amount in **milliunits** (not dollar amounts).
290
- src\tools\schemas\outputs\comparisonOutputs.ts:346: * For example, $25.50 is represented as 25500 milliunits.
291
- src\tools\schemas\outputs\comparisonOutputs.ts:352: * @see src/tools/exportTransactions.ts:204 - Amount field directly from transaction.amount (milliunits)
292
- src\tools\schemas\outputs\comparisonOutputs.ts:357: amount: z.number(), // Raw YNAB milliunits
293
- src\tools\schemas\outputs\comparisonOutputs.ts:365: amount: z.number(), // Raw YNAB milliunits
294
- src\tools\schemas\outputs\comparisonOutputs.ts:417: * { id: "txn-1", date: "2025-11-15", amount: -25500, payee_name: "Grocery Store", cleared: "cleared" }, // amount is milliunits
295
- src\tools\schemas\outputs\comparisonOutputs.ts:435: * amount: -25500, // Raw milliunits: -$25.50
296
- src\tools\schemas\outputs\comparisonOutputs.ts:592: amount: z.number(), // Raw YNAB milliunits
297
- src\utils\__tests__\money.test.ts:19: it('converts dollars to milliunits correctly', () => {
298
- src\utils\__tests__\money.test.ts:32: it('converts milliunits with 2 decimal digits (USD) by default', () => {
299
- src\utils\__tests__\money.test.ts:38: it('converts milliunits with 0 decimal digits (JPY)', () => {
300
- src\utils\__tests__\money.test.ts:43: it('converts milliunits with 3 decimal digits (BHD)', () => {
301
- src\utils\__tests__\money.test.ts:48: it('converts milliunits with explicit 2 decimal digits', () => {
302
- src\utils\__tests__\money.test.ts:61: expect(() => assertMilli(1.5)).toThrow('Expected safe integer milliunits');
303
- src\utils\__tests__\money.test.ts:63: 'Expected safe integer milliunits',
304
- src\utils\__tests__\money.test.ts:69: it('adds milliunits correctly', () => {
305
- src\utils\__tests__\money.test.ts:75: expect(() => addMilli(Number.MAX_SAFE_INTEGER, 1)).toThrow('Milliunit sum overflow');
306
- src\utils\__tests__\money.test.ts:152: it('formats milliunits into currency strings with default 2 decimals', () => {
307
- src\utils\__tests__\money.test.ts:157: it('formats milliunits with custom currency format', () => {
308
- src\utils\__tests__\money.test.ts:163: it('creates money values from milliunits with default 2 decimals', () => {
309
- src\utils\__tests__\money.test.ts:184: expect(value.value_milliunits).toBe(-45670);
310
- src\tools\schemas\outputs\monthOutputs.ts:128: /** Goal target amount in milliunits (optional) */
311
- src\tools\schemas\outputs\monthOutputs.ts:129: goal_target: z.number().optional().describe('Goal target in milliunits'),
312
- src\server\__tests__\prompts.test.ts:173: expect(text).toContain('Create the transaction with the correct amount in milliunits');
313
- src\server\__tests__\prompts.test.ts:251: expect(text).toContain('Convert milliunits to dollars');
314
- src\tools\schemas\outputs\transactionMutationOutputs.ts:17: * account_balance: 1500000, // Raw milliunits: $1,500.00
315
- src\tools\schemas\outputs\transactionMutationOutputs.ts:18: * account_cleared_balance: 1000000 // Raw milliunits: $1,000.00
316
- src\tools\schemas\outputs\transactionMutationOutputs.ts:77: * IMPORTANT: `account_balance` and `account_cleared_balance` are **raw YNAB milliunits** (not user-facing dollar amounts).
317
- src\tools\schemas\outputs\transactionMutationOutputs.ts:79: * For example, $1,250.50 is represented as 1250500 milliunits.
318
- src\tools\schemas\outputs\transactionMutationOutputs.ts:85: account_balance: z.number().optional(), // Raw YNAB milliunits
319
- src\tools\schemas\outputs\transactionMutationOutputs.ts:86: account_cleared_balance: z.number().optional(), // Raw YNAB milliunits
320
- src\tools\schemas\outputs\transactionMutationOutputs.ts:258: * account_balance: 1500000, // Raw milliunits: $1,500.00
321
- src\tools\schemas\outputs\transactionMutationOutputs.ts:259: * account_cleared_balance: 1000000 // Raw milliunits: $1,000.00
322
- src\tools\schemas\outputs\transactionMutationOutputs.ts:392: * updated_balance: 1450000, // Raw milliunits: $1,450.00
323
- src\tools\schemas\outputs\transactionMutationOutputs.ts:393: * updated_cleared_balance: 950000 // Raw milliunits: $950.00
324
- src\tools\schemas\outputs\transactionMutationOutputs.ts:412: updated_balance: z.number(), // Raw YNAB milliunits
325
- src\tools\schemas\outputs\transactionMutationOutputs.ts:413: updated_cleared_balance: z.number(), // Raw YNAB milliunits
326
- src\tools\schemas\outputs\transactionMutationOutputs.ts:529: updated_balance: z.number(), // Raw YNAB milliunits
327
- src\tools\schemas\outputs\transactionMutationOutputs.ts:530: updated_cleared_balance: z.number(), // Raw YNAB milliunits
328
- src\tools\schemas\outputs\transactionOutputs.ts:98: * Represents full transaction data with amounts in dollars (converted from YNAB milliunits).
329
- src\tools\schemas\outputs\utilityOutputs.ts:56: * Contains the conversion result between dollars and YNAB milliunits.
330
- src\tools\schemas\outputs\utilityOutputs.ts:61: to_milliunits: z.boolean(),
331
- src\tools\schemas\outputs\utilityOutputs.ts:68: * Converts between dollars and YNAB milliunits (1 dollar = 1000 milliunits).
332
- src\tools\schemas\outputs\utilityOutputs.ts:78: * to_milliunits: true,
333
- src\tools\schemas\outputs\utilityOutputs.ts:79: * description: "$25.50 converted to 25500 milliunits"
334
- src\tools\__tests__\categoryTools.integration.test.ts:133: console.warn(` - Budgeted: ${category.budgeted} milliunits`);
335
- src\tools\__tests__\categoryTools.integration.test.ts:134: console.warn(` - Activity: ${category.activity} milliunits`);
336
- src\tools\__tests__\categoryTools.integration.test.ts:135: console.warn(` - Balance: ${category.balance} milliunits`);
337
- src\tools\__tests__\categoryTools.integration.test.ts:170: // Update with a test amount (add 1000 milliunits = $1.00)
338
- src\tools\__tests__\categoryTools.integration.test.ts:204: console.warn(`✅ Successfully updated category budget to ${testBudgetedAmount} milliunits`);
339
- src\tools\__tests__\categoryTools.integration.test.ts:214: console.warn(`✅ Restored original budget amount: ${originalBudgetedAmount} milliunits`);
340
- src\tools\__tests__\categoryTools.integration.test.ts:281: `✅ Successfully set negative budget amount: ${negativeBudgetedAmount} milliunits`,
341
- src\tools\__tests__\categoryTools.integration.test.ts:291: console.warn(`✅ Restored original budget amount: ${originalBudgetedAmount} milliunits`);
342
- src\tools\schemas\outputs\__tests__\comparisonOutputs.test.ts:403: amount: -25500, // Milliunits
343
- src\tools\schemas\outputs\__tests__\comparisonOutputs.test.ts:436: amount: -15000, // Milliunits: -$15.00
344
- src\tools\__tests__\compareTransactions\parser.test.ts:7: amountToMilliunits,
345
- src\tools\__tests__\compareTransactions\parser.test.ts:78: describe('amountToMilliunits', () => {
346
- src\tools\__tests__\compareTransactions\parser.test.ts:80: expect(amountToMilliunits('123.45')).toBe(123450);
347
- src\tools\__tests__\compareTransactions\parser.test.ts:84: expect(amountToMilliunits('-123.45')).toBe(-123450);
348
- src\tools\__tests__\compareTransactions\parser.test.ts:88: expect(amountToMilliunits('(123.45)')).toBe(-123450);
349
- src\tools\__tests__\compareTransactions\parser.test.ts:92: expect(amountToMilliunits('$123.45')).toBe(123450);
350
- src\tools\__tests__\compareTransactions\parser.test.ts:96: expect(amountToMilliunits('1,234.56')).toBe(1234560);
351
- src\tools\__tests__\compareTransactions\parser.test.ts:100: expect(amountToMilliunits('+123.45')).toBe(123450);
352
- src\tools\__tests__\compareTransactions\parser.test.ts:104: expect(amountToMilliunits('0.00')).toBe(0);
353
- src\tools\__tests__\compareTransactions\parser.test.ts:108: expect(amountToMilliunits(' 123.45 ')).toBe(123450);
354
- src\tools\__tests__\compareTransactions\parser.test.ts:112: expect(amountToMilliunits('999999.99')).toBe(999999990);
355
- src\tools\__tests__\utilityTools.test.ts:85: it('should convert dollars to milliunits correctly', async () => {
356
- src\tools\__tests__\utilityTools.test.ts:86: const params = { amount: 10.5, to_milliunits: true };
357
- src\tools\__tests__\utilityTools.test.ts:93: expect(response.conversion.to_milliunits).toBe(true);
358
- src\tools\__tests__\utilityTools.test.ts:94: expect(response.conversion.description).toBe('$10.50 = 10500 milliunits');
359
- src\tools\__tests__\utilityTools.test.ts:97: it('should convert milliunits to dollars correctly', async () => {
360
- src\tools\__tests__\utilityTools.test.ts:98: const params = { amount: 10500, to_milliunits: false };
361
- src\tools\__tests__\utilityTools.test.ts:105: expect(response.conversion.to_milliunits).toBe(false);
362
- src\tools\__tests__\utilityTools.test.ts:106: expect(response.conversion.description).toBe('10500 milliunits = $10.50');
363
- src\tools\__tests__\utilityTools.test.ts:110: const params = { amount: 0, to_milliunits: true };
364
- src\tools\__tests__\utilityTools.test.ts:117: expect(response.conversion.description).toBe('$0.00 = 0 milliunits');
365
- src\tools\__tests__\utilityTools.test.ts:121: const params = { amount: -5.25, to_milliunits: true };
366
- src\tools\__tests__\utilityTools.test.ts:128: expect(response.conversion.description).toBe('$-5.25 = -5250 milliunits');
367
- src\tools\__tests__\utilityTools.test.ts:132: const params = { amount: 0.01, to_milliunits: true };
368
- src\tools\__tests__\utilityTools.test.ts:141: const params = { amount: 999999.99, to_milliunits: true };
369
- src\tools\__tests__\utilityTools.test.ts:149: it('should round to nearest milliunit when converting from dollars', async () => {
370
- src\tools\__tests__\utilityTools.test.ts:150: const params = { amount: 10.5555, to_milliunits: true };
371
- src\tools\__tests__\utilityTools.test.ts:161: const validParams = { amount: 10.5, to_milliunits: true };
372
- src\tools\__tests__\utilityTools.test.ts:171: const invalidParams = { amount: Infinity, to_milliunits: true };
373
- src\tools\__tests__\utilityTools.test.ts:178: const invalidParams = { amount: NaN, to_milliunits: true };
374
- src\tools\__tests__\utilityTools.test.ts:185: const invalidParams = { to_milliunits: true };
375
- src\tools\__tests__\utilityTools.test.ts:191: it('should reject missing to_milliunits parameter', () => {
376
- src\tools\__tests__\utilityTools.test.ts:198: it('should reject non-boolean to_milliunits parameter', () => {
377
- src\tools\__tests__\utilityTools.test.ts:199: const invalidParams = { amount: 10.5, to_milliunits: 'true' };
378
- src\tools\__tests__\compareTransactions.test.ts:78: amount: 100000, // $100.00 in milliunits
379
- src\tools\__tests__\compareTransactions.test.ts:90: amount: -50000, // -$50.00 in milliunits
380
- src\tools\__tests__\transactionTools.test.ts:136: amount: -50000, // $50.00 outflow in milliunits
381
- src\tools\__tests__\transactionTools.test.ts:558: amount: -50000, // $50.00 outflow in milliunits
382
- src\tools\__tests__\transactionTools.test.ts:618: expect(result.error.issues[0].message).toContain('Amount must be an integer in milliunits');
383
- src\tools\__tests__\transactionTools.test.ts:1456: expect(result.error.issues[0].message).toContain('Amount must be an integer in milliunits');
384
- src\tools\__tests__\transactionTools.test.ts:1881: it('should require integer milliunit amounts', () => {
385
- src\tools\__tests__\transactionTools.test.ts:2704: it('should require integer milliunit amounts when provided', () => {
386
- src\tools\__tests__\utilityTools.integration.test.ts:47: 'should convert various dollar amounts to milliunits',
387
- src\tools\__tests__\utilityTools.integration.test.ts:51: { dollars: 1.0, expectedMilliunits: 1000 },
388
- src\tools\__tests__\utilityTools.integration.test.ts:52: { dollars: 0.01, expectedMilliunits: 10 },
389
- src\tools\__tests__\utilityTools.integration.test.ts:53: { dollars: 10.5, expectedMilliunits: 10500 },
390
- src\tools\__tests__\utilityTools.integration.test.ts:54: { dollars: 999.99, expectedMilliunits: 999990 },
391
- src\tools\__tests__\utilityTools.integration.test.ts:55: { dollars: 0, expectedMilliunits: 0 },
392
- src\tools\__tests__\utilityTools.integration.test.ts:56: { dollars: -5.25, expectedMilliunits: -5250 },
393
- src\tools\__tests__\utilityTools.integration.test.ts:62: to_milliunits: true,
394
- src\tools\__tests__\utilityTools.integration.test.ts:66: expect(response.conversion.converted_amount).toBe(testCase.expectedMilliunits);
395
- src\tools\__tests__\utilityTools.integration.test.ts:67: expect(response.conversion.to_milliunits).toBe(true);
396
- src\tools\__tests__\utilityTools.integration.test.ts:70: `${testCase.expectedMilliunits} milliunits`,
397
- src\tools\__tests__\utilityTools.integration.test.ts:77: 'should convert various milliunit amounts to dollars',
398
- src\tools\__tests__\utilityTools.integration.test.ts:81: { milliunits: 1000, expectedDollars: 1.0 },
399
- src\tools\__tests__\utilityTools.integration.test.ts:82: { milliunits: 10, expectedDollars: 0.01 },
400
- src\tools\__tests__\utilityTools.integration.test.ts:83: { milliunits: 10500, expectedDollars: 10.5 },
401
- src\tools\__tests__\utilityTools.integration.test.ts:84: { milliunits: 999990, expectedDollars: 999.99 },
402
- src\tools\__tests__\utilityTools.integration.test.ts:85: { milliunits: 0, expectedDollars: 0 },
403
- src\tools\__tests__\utilityTools.integration.test.ts:86: { milliunits: -5250, expectedDollars: -5.25 },
404
- src\tools\__tests__\utilityTools.integration.test.ts:91: amount: testCase.milliunits,
405
- src\tools\__tests__\utilityTools.integration.test.ts:92: to_milliunits: false,
406
- src\tools\__tests__\utilityTools.integration.test.ts:97: expect(response.conversion.to_milliunits).toBe(false);
407
- src\tools\__tests__\utilityTools.integration.test.ts:98: expect(response.conversion.description).toContain(`${testCase.milliunits} milliunits`);
408
- src\tools\__tests__\utilityTools.integration.test.ts:112: { amount: 0.1 + 0.2, to_milliunits: true }, // Should handle 0.30000000000000004
409
- src\tools\__tests__\utilityTools.integration.test.ts:113: { amount: 1.005, to_milliunits: true }, // Should round correctly
410
- src\tools\__tests__\utilityTools.integration.test.ts:114: { amount: 999.999, to_milliunits: true }, // Should handle near-integer values
411
- src\tools\__tests__\monthTools.integration.test.ts:63: console.warn(` - Income: ${firstMonth.income} milliunits`);
412
- src\tools\__tests__\monthTools.integration.test.ts:64: console.warn(` - Budgeted: ${firstMonth.budgeted} milliunits`);
413
- src\tools\__tests__\monthTools.integration.test.ts:126: console.warn(` - Income: ${month.income} milliunits`);
414
- src\tools\__tests__\monthTools.integration.test.ts:127: console.warn(` - Budgeted: ${month.budgeted} milliunits`);
415
- src\tools\__tests__\monthTools.integration.test.ts:128: console.warn(` - Activity: ${month.activity} milliunits`);
416
- src\tools\__tests__\monthTools.integration.test.ts:129: console.warn(` - To be budgeted: ${month.to_be_budgeted} milliunits`);