@dizzlkheinz/ynab-mcpb 0.17.0 → 0.17.1

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 (142) 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 +11 -2
  7. package/CLAUDE.md +7 -6
  8. package/dist/bundle/index.cjs +52 -52
  9. package/dist/server/YNABMCPServer.d.ts +120 -54
  10. package/dist/server/securityMiddleware.d.ts +37 -8
  11. package/dist/tools/schemas/outputs/index.d.ts +2 -2
  12. package/dist/tools/schemas/outputs/index.js +2 -2
  13. package/dist/tools/schemas/outputs/utilityOutputs.d.ts +0 -15
  14. package/dist/tools/schemas/outputs/utilityOutputs.js +0 -9
  15. package/dist/tools/utilityTools.d.ts +0 -7
  16. package/dist/tools/utilityTools.js +1 -50
  17. package/docs/maintainers/npm-publishing.md +27 -0
  18. package/docs/reference/API.md +15 -70
  19. package/docs/technical/reconciliation-system-architecture.md +2251 -2251
  20. package/package.json +5 -5
  21. package/scripts/analyze-bundle.mjs +41 -41
  22. package/scripts/generate-mcpb.ps1 +95 -95
  23. package/scripts/watch-and-restart.ps1 +49 -49
  24. package/src/__tests__/comprehensive.integration.test.ts +0 -28
  25. package/src/__tests__/performance.test.ts +4 -12
  26. package/src/__tests__/setup.ts +45 -14
  27. package/src/__tests__/workflows.e2e.test.ts +0 -44
  28. package/src/server/__tests__/YNABMCPServer.test.ts +0 -1
  29. package/src/server/__tests__/toolRegistration.test.ts +2 -2
  30. package/src/tools/__tests__/transactionTools.integration.test.ts +63 -3
  31. package/src/tools/__tests__/utilityTools.integration.test.ts +1 -85
  32. package/src/tools/__tests__/utilityTools.test.ts +1 -123
  33. package/src/tools/schemas/outputs/index.ts +0 -3
  34. package/src/tools/schemas/outputs/utilityOutputs.ts +2 -43
  35. package/src/tools/toolCategories.ts +0 -1
  36. package/src/tools/utilityTools.ts +5 -76
  37. package/vitest.config.ts +2 -1
  38. package/.chunkhound.json +0 -11
  39. package/.code/agents/0098661e-0fa3-4990-beb9-c0cbf3f123aa/status.txt +0 -1
  40. package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +0 -3
  41. package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +0 -3
  42. package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +0 -1
  43. package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +0 -5
  44. package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +0 -1569
  45. package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +0 -3
  46. package/.code/agents/1324/exec-call_tIpx9uV1TpARbAMZonRQm8AO.txt +0 -757
  47. package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +0 -2832
  48. package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +0 -2709
  49. package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +0 -2832
  50. package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +0 -2832
  51. package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +0 -2709
  52. package/.code/agents/1572/exec-call_GjVFBFOWcY7lE0idc5nWlLNh.txt +0 -781
  53. package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +0 -1
  54. package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +0 -5217
  55. package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +0 -2594
  56. package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +0 -2594
  57. package/.code/agents/1846/exec-call_1YNAVD18RjrMN7JnfkkQhUP3.txt +0 -766
  58. package/.code/agents/1846/exec-call_lh3lDzE4WJAh1lFiomiiZ73D.txt +0 -766
  59. package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +0 -231
  60. package/.code/agents/2038/exec-call_DYwOukaYsL8VCONWmV2rUW5u.txt +0 -766
  61. package/.code/agents/2038/exec-call_c7fOQ7UrpVcTtvdfGBRM146V.txt +0 -652
  62. package/.code/agents/2038/exec-call_ySNyq9Mm55jWE480s54r5QcA.txt +0 -766
  63. package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +0 -2590
  64. package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +0 -5195
  65. package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +0 -286
  66. package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +0 -218
  67. package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +0 -180
  68. package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +0 -3
  69. package/.code/agents/2256/exec-call_AtPcRWPmFPMcmX6qOFm1fCEY.txt +0 -766
  70. package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +0 -1
  71. package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +0 -747
  72. package/.code/agents/2454/exec-call_aFJpupwjfZeOBm7ixI5Vc8z2.txt +0 -766
  73. package/.code/agents/2454/exec-call_wogZ4HfXTodTEXvdgXlVUBpv.txt +0 -766
  74. package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +0 -1
  75. package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +0 -2594
  76. package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +0 -2594
  77. package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +0 -144
  78. package/.code/agents/2e905864-aa07-4314-bcf9-c5b32277e4ac/result.txt +0 -36
  79. package/.code/agents/3073/exec-call_Peeagc9DxGYLgE6pNdMZhqIE.txt +0 -766
  80. package/.code/agents/3073/exec-call_d2YSE3hXF08KRSoUM3qd8Z3x.txt +0 -766
  81. package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +0 -416
  82. package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +0 -2590
  83. package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +0 -2590
  84. package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +0 -2590
  85. package/.code/agents/335aa031-466d-4fb7-925f-3cd864e264d0/result.txt +0 -191
  86. package/.code/agents/3364/exec-call_NbhIrsM5HhyDZDmJZG5CuCYL.txt +0 -766
  87. package/.code/agents/3364/exec-call_cKtJg0NrXiwXEFwlsE3uPZRA.txt +0 -766
  88. package/.code/agents/36d98414-5cde-4d9d-9a67-a240a18c1f07/result.txt +0 -189
  89. package/.code/agents/4604e866-b7b8-44f5-992f-2f683b0a523b/status.txt +0 -1
  90. package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +0 -790
  91. package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +0 -766
  92. package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +0 -790
  93. package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +0 -2594
  94. package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +0 -1000
  95. package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +0 -3489
  96. package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +0 -766
  97. package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +0 -2594
  98. package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +0 -2456
  99. package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +0 -2594
  100. package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +0 -18
  101. package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +0 -48
  102. package/.code/agents/5f8dc01c-47b3-4163-b0b3-aa31be89fcdc/status.txt +0 -1
  103. package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +0 -1
  104. package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +0 -1
  105. package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +0 -1
  106. package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +0 -1
  107. package/.code/agents/7/exec-call_HltHpkDox0Zm1vGEjdksUgpE.txt +0 -1120
  108. package/.code/agents/7/exec-call_LCATrOPPAgbxW9Q1z0XaVi2E.txt +0 -2646
  109. package/.code/agents/7/exec-call_W8DeRfNG9hvbgVFvf0clBf6R.txt +0 -2646
  110. package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +0 -1271
  111. package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +0 -1570
  112. package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +0 -2590
  113. package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +0 -3
  114. package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +0 -3299
  115. package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +0 -3299
  116. package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +0 -1882
  117. package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +0 -2594
  118. package/.code/agents/94a0ddf3-a304-4ec3-913e-3cceef509948/error.txt +0 -1
  119. package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +0 -1
  120. package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +0 -170
  121. package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +0 -1
  122. package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +0 -3
  123. package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +0 -1
  124. package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +0 -1
  125. package/.code/agents/e2c752b7-711d-423a-af57-f53c809deb84/result.txt +0 -160
  126. package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +0 -3
  127. package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +0 -1
  128. package/.code/agents/e6601719-c31f-4a0e-8c71-d70787d0ab71/status.txt +0 -1
  129. package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +0 -1
  130. package/.code/agents/f250b7ed-5bd5-4036-aa8c-ce63caee7d61/result.txt +0 -20
  131. package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +0 -5
  132. package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +0 -1
  133. package/AGENTS.md +0 -1
  134. package/NUL +0 -0
  135. package/package.json.tmp +0 -105
  136. package/temp-recon.ts +0 -126
  137. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_09-04-53.json +0 -23
  138. package/test-exports/ynab_account_e9ddc2a6_minimal_1items_2025-11-19_10-37-42.json +0 -23
  139. package/test-exports/ynab_account_e9ddc2a6_minimal_4items_2025-11-19_09-02-09.json +0 -44
  140. package/test-exports/ynab_account_e9ddc2a6_minimal_6items_2025-11-19_10-37-52.json +0 -58
  141. package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +0 -3662
  142. 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`);