@dizzlkheinz/ynab-mcpb 0.13.1 → 0.15.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.
- package/.code/agents/01a13ef4-3f23-4f52-b33b-3585b73cfa60/error.txt +3 -0
- package/.code/agents/084fd32f-e298-4728-9103-a78d7dc39613/error.txt +3 -0
- package/.code/agents/0fed51e1-a943-4b97-a2a8-a6f0f27c844d/status.txt +1 -0
- package/.code/agents/1059b6bd-5ccd-4d83-a12c-7c9d89137399/error.txt +5 -0
- package/.code/agents/110/exec-call_F9BDNG7JfxKkq7Vc8ESAvdft.txt +1569 -0
- package/.code/agents/11ebcef3-b13f-4e44-ad80-d94a866804b7/error.txt +3 -0
- package/.code/agents/1398/exec-call_CjItcWMU1G6JoPshX62QvpaR.txt +2832 -0
- package/.code/agents/1398/exec-call_SUVq2ivmONQ5LMCmd7ngmOqr.txt +2709 -0
- package/.code/agents/1398/exec-call_SdNY4NOffdcC5pRYjVXHjPCK.txt +2832 -0
- package/.code/agents/1398/exec-call_qblJo9et1gsFFB63TtLOiji2.txt +2832 -0
- package/.code/agents/1398/exec-call_zaRrzlGz7GJcNzVfkAmML7Zg.txt +2709 -0
- package/.code/agents/171834fd-5905-42fc-bbcc-2c755145b0fc/status.txt +1 -0
- package/.code/agents/1724/exec-call_HvHQe0w5CCG3T7Q3ULT6MO3g.txt +5217 -0
- package/.code/agents/1724/exec-call_QwUNESVzfxxk78K1frh1Vahb.txt +2594 -0
- package/.code/agents/1724/exec-call_aJ1Xwz71XmIpD4SBxSHERzLe.txt +2594 -0
- package/.code/agents/1d7d7ab7-7473-4b69-8b97-6e914f56056a/result.txt +231 -0
- package/.code/agents/210/exec-call_0tQCsKNJ1WTuIchb8wlcFJpW.txt +2590 -0
- package/.code/agents/210/exec-call_8ZlY9cUc8Ft1twi4ch8UJ6IN.txt +5195 -0
- package/.code/agents/2188/exec-call_5HqayBxIteJtoI8oPTiLWgvJ.txt +286 -0
- package/.code/agents/2188/exec-call_XRbBKBq3adZe6dcppAvQtM7G.txt +218 -0
- package/.code/agents/2188/exec-call_ehA0SjpYtrUi6GJXmibLjp4i.txt +180 -0
- package/.code/agents/21902821-ecaf-4759-bb9d-222b90921af5/error.txt +3 -0
- package/.code/agents/232073be-aa0e-46da-b478-5b64dbf03cf5/status.txt +1 -0
- package/.code/agents/234ff534-2336-4771-a8d9-aa04421a63be/result.txt +747 -0
- package/.code/agents/253e2695-dc36-4022-b436-27655e0fc6c7/status.txt +1 -0
- package/.code/agents/2583/exec-call_M59I4eDjpjlBIWBiSxyS0YlJ.txt +2594 -0
- package/.code/agents/2583/exec-call_usLRGh7OhVHtsRBL4iUwRhjq.txt +2594 -0
- package/.code/agents/292aa3ff-dbab-470f-97c9-e7e8fd65e0db/result.txt +144 -0
- package/.code/agents/3134/exec-call_IgCAMGx19lWfuo8zfYIt5FFC.txt +416 -0
- package/.code/agents/3134/exec-call_IxvLR2Oo7kba2QTsI1gHVko8.txt +2590 -0
- package/.code/agents/3134/exec-call_jYvc8hksZChSiysbzKjl2ZbB.txt +2590 -0
- package/.code/agents/329/exec-call_4QdP3SfSO7HGPCwVcqZIth6s.txt +2590 -0
- package/.code/agents/472/exec-call_4AxzEEcWwkKhpqRB3bE8Ha4L.txt +790 -0
- package/.code/agents/472/exec-call_CB3LPYQA8QIZRi8I6kj4J17A.txt +766 -0
- package/.code/agents/472/exec-call_YeoUWvaFoktay2nqVUsa9KKX.txt +790 -0
- package/.code/agents/472/exec-call_jPWgKVquBBXTg0T3Lks5ZfkK.txt +2594 -0
- package/.code/agents/472/exec-call_qBkvunpGBDEHph2jPmTwtcsb.txt +1000 -0
- package/.code/agents/472/exec-call_v0ffRV1p0kTckBmJPzzHAEy0.txt +3489 -0
- package/.code/agents/472/exec-call_xAX5FXqWIlk02d9WubHbHWh8.txt +766 -0
- package/.code/agents/5346/exec-call_9q0muXUuLaucwEqI51Pt7idT.txt +2594 -0
- package/.code/agents/5346/exec-call_B2el3B79rVkq9LhWTI2VYlz7.txt +2456 -0
- package/.code/agents/5346/exec-call_BfX08f02qkZI9uJD5dvCvuoj.txt +2594 -0
- package/.code/agents/543328d0-61d6-4fd1-a723-bb168656e2e2/error.txt +18 -0
- package/.code/agents/5580c02c-1383-4d18-9cbd-cc8a06e3408d/result.txt +48 -0
- package/.code/agents/60ce1a22-5126-44b2-b977-1d5b56142a7b/status.txt +1 -0
- package/.code/agents/6215d9db-7fa9-4429-aeec-3835c3212291/error.txt +1 -0
- package/.code/agents/6743db55-30e5-4b4e-9366-a8214fc7f714/error.txt +1 -0
- package/.code/agents/6bf9591b-b9c9-422c-b0a5-e968c7d8422a/status.txt +1 -0
- package/.code/agents/7/exec-call_eww3GfdEiJZx61sJEQ9wNmt3.txt +1271 -0
- package/.code/agents/70/exec-call_owUtDMYiVgqDf8vsz1i32PFf.txt +1570 -0
- package/.code/agents/8/exec-call_UtrjAcLbhYLatxR4O97fZgnm.txt +2590 -0
- package/.code/agents/82490bc9-f34e-4b1b-8a8e-bccc2e6254f5/error.txt +3 -0
- package/.code/agents/841/exec-call_7nTNhSBCNjTDUIJv7py6CepO.txt +3299 -0
- package/.code/agents/841/exec-call_TLI0yUdUijuUAvI4o3DXEvHO.txt +3299 -0
- package/.code/agents/9/exec-call_XaABQT1hIlRpnKZ2uyBMWsTC.txt +1882 -0
- package/.code/agents/941/exec-call_GuGHRx7NNXWIDAnxUG2NEWPa.txt +2594 -0
- package/.code/agents/95d9fbab-19a2-48af-83f9-c792566a347f/error.txt +1 -0
- package/.code/agents/b0098cb8-cb32-4ada-9bc4-37c587518896/result.txt +170 -0
- package/.code/agents/b4fe59a4-81df-42e2-a112-0153e504faca/error.txt +1 -0
- package/.code/agents/bf4ce152-f623-49d7-aa52-c18631625c3c/error.txt +3 -0
- package/.code/agents/d7d1db75-d7eb-468e-adea-4ef4d916d187/status.txt +1 -0
- package/.code/agents/e2baa9c8-bac3-49e3-a39d-024333e6a990/status.txt +1 -0
- package/.code/agents/e350b8c3-8483-408c-b2bb-94515f492a11/error.txt +3 -0
- package/.code/agents/e63f9919-719f-4ad0-bccf-01b1a596e1e9/status.txt +1 -0
- package/.code/agents/e71695a8-3044-478d-8f12-ed13d02884c7/status.txt +1 -0
- package/.code/agents/f95b7464-3e25-4897-b153-c8dfd63fd605/error.txt +5 -0
- package/.code/agents/fa3c5ddf-cdf7-47a2-930a-b806c6363689/status.txt +1 -0
- package/.github/workflows/publish.yml +3 -3
- package/.github/workflows/release.yml +4 -0
- package/CHANGELOG.md +75 -0
- package/NUL +1 -0
- package/dist/bundle/index.cjs +65 -42
- package/dist/server/errorHandler.d.ts +2 -0
- package/dist/server/errorHandler.js +49 -5
- package/dist/tools/reconcileAdapter.js +10 -5
- package/dist/tools/reconciliation/analyzer.d.ts +8 -2
- package/dist/tools/reconciliation/analyzer.js +127 -409
- package/dist/tools/reconciliation/csvParser.d.ts +51 -0
- package/dist/tools/reconciliation/csvParser.js +413 -0
- package/dist/tools/reconciliation/executor.d.ts +8 -0
- package/dist/tools/reconciliation/executor.js +204 -58
- package/dist/tools/reconciliation/index.d.ts +7 -7
- package/dist/tools/reconciliation/index.js +115 -39
- package/dist/tools/reconciliation/matcher.d.ts +24 -3
- package/dist/tools/reconciliation/matcher.js +175 -133
- package/dist/tools/reconciliation/recommendationEngine.js +22 -18
- package/dist/tools/reconciliation/reportFormatter.js +9 -8
- package/dist/tools/reconciliation/signDetector.d.ts +2 -0
- package/dist/tools/reconciliation/signDetector.js +54 -0
- package/dist/tools/reconciliation/types.d.ts +20 -34
- package/dist/tools/reconciliation/types.js +1 -7
- package/dist/tools/reconciliation/ynabAdapter.d.ts +4 -0
- package/dist/tools/reconciliation/ynabAdapter.js +15 -0
- package/dist/types/reconciliation.d.ts +24 -0
- package/dist/types/reconciliation.js +1 -0
- package/docs/guides/ARCHITECTURE.md +12 -129
- package/docs/plans/2025-11-21-v014-hardening.md +153 -0
- package/docs/plans/reconciliation-v2-redesign.md +1571 -0
- package/package.json +6 -1
- package/scripts/test-recommendations.ts +1 -1
- package/src/__tests__/tools/reconciliation/csvParser.integration.test.ts +129 -0
- package/src/__tests__/tools/reconciliation/real-world.integration.test.ts +53 -0
- package/src/server/errorHandler.ts +52 -5
- package/src/tools/reconcileAdapter.ts +10 -5
- package/src/tools/reconciliation/__tests__/adapter.test.ts +28 -22
- package/src/tools/reconciliation/__tests__/analyzer.test.ts +114 -180
- package/src/tools/reconciliation/__tests__/csvParser.test.ts +87 -0
- package/src/tools/reconciliation/__tests__/executor.integration.test.ts +1 -1
- package/src/tools/reconciliation/__tests__/executor.test.ts +88 -61
- package/src/tools/reconciliation/__tests__/matcher.test.ts +68 -54
- package/src/tools/reconciliation/__tests__/recommendationEngine.test.ts +37 -30
- package/src/tools/reconciliation/__tests__/reportFormatter.test.ts +6 -5
- package/src/tools/reconciliation/__tests__/scenarios/extremes.scenario.test.ts +30 -11
- package/src/tools/reconciliation/__tests__/scenarios/repeatAmount.scenario.test.ts +50 -15
- package/src/tools/reconciliation/__tests__/signDetector.test.ts +211 -0
- package/src/tools/reconciliation/__tests__/ynabAdapter.test.ts +61 -0
- package/src/tools/reconciliation/analyzer.ts +191 -550
- package/src/tools/reconciliation/csvParser.ts +617 -0
- package/src/tools/reconciliation/executor.ts +249 -66
- package/src/tools/reconciliation/index.ts +148 -54
- package/src/tools/reconciliation/matcher.ts +234 -214
- package/src/tools/reconciliation/recommendationEngine.ts +23 -19
- package/src/tools/reconciliation/reportFormatter.ts +16 -11
- package/src/tools/reconciliation/signDetector.ts +117 -0
- package/src/tools/reconciliation/types.ts +39 -61
- package/src/tools/reconciliation/ynabAdapter.ts +33 -0
- package/src/types/reconciliation.ts +49 -0
- package/test-exports/ynab_since_2025-10-16_account_53298e13_238items_2025-11-28_13-46-20.json +3662 -0
- package/.code/agents/0427d95e-edca-431f-a214-5e53264e29c4/error.txt +0 -8
- package/.code/agents/0d675174-d1e1-41c3-9975-4c2e275819a9/error.txt +0 -3
- package/.code/agents/0d8c5afd-4787-422b-abf8-2e5943fc7e67/error.txt +0 -3
- package/.code/agents/0ec34a70-ed5d-4b9e-bee4-bb0e4cccbc4b/error.txt +0 -1
- package/.code/agents/0ef51a21-1ab1-49d7-9561-0eaa43875ebc/error.txt +0 -12
- package/.code/agents/15db95d7-abad-4b4d-9c3b-8446089cb61d/error.txt +0 -1
- package/.code/agents/19ab9acb-f675-4ff0-902a-09a5476f8149/error.txt +0 -1
- package/.code/agents/1ef7e12d-f6ff-4897-8a9b-152d523d898e/error.txt +0 -5
- package/.code/agents/2465/exec-call_lroN9KKzJVWC7t5423DK1nT9.txt +0 -1453
- package/.code/agents/28edb6fe-95a9-41a0-ae69-aa0100d26c0c/error.txt +0 -8
- package/.code/agents/2ae40cf5-b4bf-42e2-92bf-7ea350a7755e/error.txt +0 -9
- package/.code/agents/2bfc4e1f-ac4b-45a5-b6df-bf89d4dbb54c/error.txt +0 -1
- package/.code/agents/2e2e1134-eff0-49be-ba25-8e2c3468a564/error.txt +0 -5
- package/.code/agents/3/exec-call_203OC4TNVkLxW7z2HCVEQ1cM.txt +0 -81
- package/.code/agents/3/exec-call_SS5T0XSiXB4LSNzUKTl75wkh.txt +0 -610
- package/.code/agents/3322c003-ce5e-48e3-a342-f5049c5bf9a2/error.txt +0 -1
- package/.code/agents/391e9b08-1ebc-468c-9bcd-6d0cc3193b37/error.txt +0 -1
- package/.code/agents/3ab0aa84-b7bb-4054-afa3-40b8fd7d3be0/error.txt +0 -1
- package/.code/agents/3bed368d-50fe-477e-aee3-a6707eaa1ab9/error.txt +0 -3
- package/.code/agents/3e40b925-db12-442f-8d7a-a25fc69a6672/error.txt +0 -8
- package/.code/agents/414d5776-cf58-41f3-9328-a6daed503a50/error.txt +0 -5
- package/.code/agents/42687751-4565-4610-b240-67835b17d861/error.txt +0 -1
- package/.code/agents/46b98876-1a39-43c9-9e2f-507ca6d47335/error.txt +0 -9
- package/.code/agents/4a7d9491-b26f-43dd-850d-2ecdc49b5d1b/error.txt +0 -1
- package/.code/agents/4e60f00a-1b3e-447f-87f3-7faf9deddec3/error.txt +0 -13
- package/.code/agents/5138fc1c-4d49-4b74-a7da-ccdb3a8e44e7/error.txt +0 -14
- package/.code/agents/521cff39-a7a3-42e5-a557-134f0f7daaa0/error.txt +0 -5
- package/.code/agents/53302dc5-3857-4413-9a47-9e0f64a51dc4/error.txt +0 -5
- package/.code/agents/567c7c2e-6a6f-4761-a08d-d36deeb2e0ac/error.txt +0 -5
- package/.code/agents/57b00845-80dc-47c9-953c-3028d16275d6/error.txt +0 -3
- package/.code/agents/593d9005-c2a5-48fd-8813-ece0d3f2de96/error.txt +0 -1
- package/.code/agents/5a112e66-0e1a-42f9-877c-53af56ea3551/error.txt +0 -1
- package/.code/agents/5b05e8ed-7788-4738-b7ee-9faa8180f992/error.txt +0 -5
- package/.code/agents/5f888d6f-d7ca-4ac8-be23-9ea1bf753951/error.txt +0 -5
- package/.code/agents/607db3ab-e4b0-435b-b497-93e9aa525549/error.txt +0 -8
- package/.code/agents/67dcb2a2-900f-4c78-b3fc-80b5213e0ddf/error.txt +0 -8
- package/.code/agents/69ad848c-4e98-49b3-b16c-0094ac2d1759/error.txt +0 -5
- package/.code/agents/6c9cfc5f-0d0b-445c-b121-9f60082c4f70/error.txt +0 -1
- package/.code/agents/6f6f8f77-4ab0-4f6e-9f30-40e8be0bd8f5/error.txt +0 -1
- package/.code/agents/72a7cde4-fa8a-4024-9038-27faa550539b/error.txt +0 -1
- package/.code/agents/7b48335c-8247-43aa-9949-5f820ba8e199/error.txt +0 -1
- package/.code/agents/80944249-bea9-4ac5-87de-a666c4df306e/error.txt +0 -1
- package/.code/agents/826099df-1b66-4186-a915-7eb59f9db19d/error.txt +0 -5
- package/.code/agents/8291d158-18a8-4a92-b799-4e9a4d9cce88/error.txt +0 -1
- package/.code/agents/82fb71a3-20fb-4341-804a-a2fc900f95bc/error.txt +0 -1
- package/.code/agents/855790ea-54ee-43e4-8209-a66994e37590/error.txt +0 -1
- package/.code/agents/88ce3a2e-04f2-42be-9062-bf97aa798da0/error.txt +0 -3
- package/.code/agents/9a17e398-b6ed-4218-bb55-bc64a8d38ce8/error.txt +0 -8
- package/.code/agents/9a4f4bfc-a2a6-4f40-a896-9335b41a7ed1/error.txt +0 -1
- package/.code/agents/9b633e55-ef84-47d6-94bb-fd3dd172ad97/error.txt +0 -1
- package/.code/agents/9b81f3ab-c72b-4a81-9a8f-28a49ddba84a/error.txt +0 -8
- package/.code/agents/a35daf29-b2d1-4aef-9b42-dad63a76bd47/error.txt +0 -3
- package/.code/agents/a81990cc-69ee-44d2-b907-17403c9bc5d7/error.txt +0 -5
- package/.code/agents/ab56260a-4a83-4ad4-9410-f88a23d6520a/error.txt +0 -1
- package/.code/agents/ad722c31-2d1d-45f7-bae2-3f02ca455b60/error.txt +0 -1
- package/.code/agents/b62e8690-3324-4b97-9309-731bee79416b/error.txt +0 -5
- package/.code/agents/baf60a3a-752b-4ad8-99d6-df32423ed2eb/error.txt +0 -1
- package/.code/agents/be049042-7dcb-4ac8-9beb-c8f1aea67742/error.txt +0 -14
- package/.code/agents/bed1dcb4-bfce-4a9f-8594-0f994962aafd/error.txt +0 -1
- package/.code/agents/c324a6cf-e935-4ede-9529-b3ebc18e8d6b/error.txt +0 -5
- package/.code/agents/c37c06ff-dfe3-43f2-9bbc-3ec73ec8f41d/error.txt +0 -5
- package/.code/agents/c8cd6671-433a-456b-9f88-e51cb2df6bfc/error.txt +0 -11
- package/.code/agents/ca2ccb67-2f24-428e-b27d-9365beadd140/error.txt +0 -1
- package/.code/agents/cf08c0c8-e7f0-423e-93ba-547e8e818340/error.txt +0 -8
- package/.code/agents/d579c74f-874b-40a4-9d56-ced1eb6a701d/error.txt +0 -1
- package/.code/agents/df412c98-7378-4deb-8e1e-76c416931181/error.txt +0 -3
- package/.code/agents/e5134eb3-2af4-45b0-8998-051cb4afdb45/error.txt +0 -3
- package/.code/agents/e6308471-aa45-4e9e-9496-2e9404164d97/error.txt +0 -8
- package/.code/agents/e7bd8bc7-23fb-4f46-98dc-b0dcf11b75a1/error.txt +0 -1
- package/.code/agents/e92bec35-378d-4fe1-8ac0-6e1bb3c86911/error.txt +0 -5
- package/.code/agents/ed918fbf-2dc4-4aa2-bfc5-04b65d9471ea/error.txt +0 -1
- package/.code/agents/ef1d756f-b272-48fc-8729-f05c494674f7/error.txt +0 -1
- package/.code/agents/ef359853-0249-4e41-a804-c0fc459fe456/error.txt +0 -1
- package/.code/agents/effc7b4a-4b90-40a0-8c86-a7a99d2d5fd2/error.txt +0 -1
- package/.code/agents/fa15f8d5-8359-4a8b-83a3-2f2056b3ff40/error.txt +0 -3
- package/.code/agents/fbef4193-eadf-4c8a-83ff-4878a6310f25/error.txt +0 -8
- package/.code/agents/fd0a4b4a-fda4-4964-a6d6-2b8a2da387c6/error.txt +0 -1
- package/.gemini/settings.json +0 -8
- package/WARP.md +0 -245
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
|
-
import { toMoneyValueFromDecimal, fromMilli
|
|
2
|
+
import { toMoneyValue, toMoneyValueFromDecimal, fromMilli } from '../../utils/money.js';
|
|
3
3
|
const RECOMMENDATION_VERSION = '1.0';
|
|
4
4
|
const CONFIDENCE = {
|
|
5
5
|
CREATE_EXACT_MATCH: 0.95,
|
|
@@ -33,15 +33,15 @@ function processInsight(insight, context) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
function createSuggestedMatchRecommendation(match, context) {
|
|
36
|
-
const bankTxn = match.
|
|
37
|
-
if (match.
|
|
36
|
+
const bankTxn = match.bankTransaction;
|
|
37
|
+
if (match.ynabTransaction && match.confidence !== 'none') {
|
|
38
38
|
return {
|
|
39
39
|
id: randomUUID(),
|
|
40
40
|
action_type: 'review_duplicate',
|
|
41
41
|
priority: 'high',
|
|
42
|
-
confidence: Math.max(0, Math.min(1, match.
|
|
42
|
+
confidence: Math.max(0, Math.min(1, match.confidenceScore / 100)),
|
|
43
43
|
message: `Review possible match: ${bankTxn.payee}`,
|
|
44
|
-
reason: match.
|
|
44
|
+
reason: match.matchReason,
|
|
45
45
|
estimated_impact: toMoneyValueFromDecimal(0, context.analysis.balance_info.current_cleared.currency),
|
|
46
46
|
account_id: context.account_id,
|
|
47
47
|
metadata: {
|
|
@@ -49,20 +49,20 @@ function createSuggestedMatchRecommendation(match, context) {
|
|
|
49
49
|
created_at: new Date().toISOString(),
|
|
50
50
|
},
|
|
51
51
|
parameters: {
|
|
52
|
-
candidate_ids: [match.
|
|
52
|
+
candidate_ids: [match.ynabTransaction.id],
|
|
53
53
|
bank_transaction: bankTxn,
|
|
54
|
-
suggested_match_id: match.
|
|
54
|
+
suggested_match_id: match.ynabTransaction.id,
|
|
55
55
|
},
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
const isCombinationMatch =
|
|
58
|
+
const isCombinationMatch = (match.candidates?.length ?? 0) > 1;
|
|
59
59
|
if (isCombinationMatch) {
|
|
60
60
|
return createCombinationReviewRecommendation(match, context);
|
|
61
61
|
}
|
|
62
62
|
const parameters = {
|
|
63
63
|
account_id: context.account_id,
|
|
64
64
|
date: bankTxn.date,
|
|
65
|
-
amount:
|
|
65
|
+
amount: bankTxn.amount,
|
|
66
66
|
payee_name: bankTxn.payee,
|
|
67
67
|
cleared: 'cleared',
|
|
68
68
|
approved: true,
|
|
@@ -77,7 +77,7 @@ function createSuggestedMatchRecommendation(match, context) {
|
|
|
77
77
|
confidence: CONFIDENCE.CREATE_EXACT_MATCH,
|
|
78
78
|
message: `Create transaction for ${bankTxn.payee}`,
|
|
79
79
|
reason: `This transaction exactly matches your discrepancy`,
|
|
80
|
-
estimated_impact:
|
|
80
|
+
estimated_impact: toMoneyValue(bankTxn.amount, context.analysis.balance_info.current_cleared.currency),
|
|
81
81
|
account_id: context.account_id,
|
|
82
82
|
metadata: {
|
|
83
83
|
version: RECOMMENDATION_VERSION,
|
|
@@ -87,7 +87,7 @@ function createSuggestedMatchRecommendation(match, context) {
|
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
function createCombinationReviewRecommendation(match, context) {
|
|
90
|
-
const bankTxn = match.
|
|
90
|
+
const bankTxn = match.bankTransaction;
|
|
91
91
|
const candidateIds = match.candidates?.map((candidate) => candidate.ynab_transaction.id) ?? [];
|
|
92
92
|
const candidateTotalAmount = match.candidates?.reduce((sum, candidate) => {
|
|
93
93
|
const amount = candidate.ynab_transaction.amount;
|
|
@@ -110,7 +110,7 @@ function createCombinationReviewRecommendation(match, context) {
|
|
|
110
110
|
metadata: {
|
|
111
111
|
version: RECOMMENDATION_VERSION,
|
|
112
112
|
created_at: new Date().toISOString(),
|
|
113
|
-
bank_transaction_amount:
|
|
113
|
+
bank_transaction_amount: toMoneyValue(bankTxn.amount, context.analysis.balance_info.current_cleared.currency),
|
|
114
114
|
candidate_total_amount: toMoneyValueFromDecimal(candidateTotalAmount, context.analysis.balance_info.current_cleared.currency),
|
|
115
115
|
candidate_count: match.candidates?.length ?? 0,
|
|
116
116
|
},
|
|
@@ -125,8 +125,8 @@ function createCombinationReviewRecommendation(match, context) {
|
|
|
125
125
|
...candidateIds.map((id) => ({
|
|
126
126
|
source: 'ynab',
|
|
127
127
|
id,
|
|
128
|
-
description: match.candidates?.find((c) => c.ynab_transaction.id === id)?.ynab_transaction
|
|
129
|
-
|
|
128
|
+
description: match.candidates?.find((c) => c.ynab_transaction.id === id)?.ynab_transaction.payee ??
|
|
129
|
+
'Unknown',
|
|
130
130
|
})),
|
|
131
131
|
],
|
|
132
132
|
},
|
|
@@ -205,7 +205,11 @@ function processUnmatchedTransactions(context) {
|
|
|
205
205
|
for (const bankTxn of context.analysis.unmatched_bank) {
|
|
206
206
|
recommendations.push(createUnmatchedBankRecommendation(bankTxn, context));
|
|
207
207
|
}
|
|
208
|
-
|
|
208
|
+
const matchesForReview = [
|
|
209
|
+
...context.analysis.suggested_matches,
|
|
210
|
+
...context.analysis.auto_matches,
|
|
211
|
+
];
|
|
212
|
+
for (const match of matchesForReview) {
|
|
209
213
|
recommendations.push(createSuggestedMatchRecommendation(match, context));
|
|
210
214
|
}
|
|
211
215
|
for (const ynabTxn of context.analysis.unmatched_ynab) {
|
|
@@ -219,7 +223,7 @@ function createUnmatchedBankRecommendation(txn, context) {
|
|
|
219
223
|
const parameters = {
|
|
220
224
|
account_id: context.account_id,
|
|
221
225
|
date: txn.date,
|
|
222
|
-
amount:
|
|
226
|
+
amount: txn.amount,
|
|
223
227
|
payee_name: txn.payee,
|
|
224
228
|
cleared: 'cleared',
|
|
225
229
|
approved: true,
|
|
@@ -234,7 +238,7 @@ function createUnmatchedBankRecommendation(txn, context) {
|
|
|
234
238
|
confidence: CONFIDENCE.UNMATCHED_BANK,
|
|
235
239
|
message: `Create missing transaction: ${txn.payee}`,
|
|
236
240
|
reason: 'Transaction appears on bank statement but not in YNAB',
|
|
237
|
-
estimated_impact:
|
|
241
|
+
estimated_impact: toMoneyValue(txn.amount, context.analysis.balance_info.current_cleared.currency),
|
|
238
242
|
account_id: context.account_id,
|
|
239
243
|
metadata: {
|
|
240
244
|
version: RECOMMENDATION_VERSION,
|
|
@@ -249,7 +253,7 @@ function createUpdateClearedRecommendation(txn, context) {
|
|
|
249
253
|
action_type: 'update_cleared',
|
|
250
254
|
priority: 'low',
|
|
251
255
|
confidence: CONFIDENCE.UPDATE_CLEARED,
|
|
252
|
-
message: `Mark transaction as cleared: ${txn.
|
|
256
|
+
message: `Mark transaction as cleared: ${txn.payee || 'Unknown'}`,
|
|
253
257
|
reason: 'Transaction exists in YNAB but not yet cleared',
|
|
254
258
|
estimated_impact: toMoneyValueFromDecimal(0, context.analysis.balance_info.current_cleared.currency),
|
|
255
259
|
account_id: context.account_id,
|
|
@@ -81,12 +81,13 @@ function formatBankTransactionLine(txn) {
|
|
|
81
81
|
return ` ${txn.date} - ${txn.payee.substring(0, 40).padEnd(40)} ${amountStr}`;
|
|
82
82
|
}
|
|
83
83
|
function formatSuggestedMatchLine(match) {
|
|
84
|
-
const bankTxn = match.
|
|
84
|
+
const bankTxn = match.bankTransaction;
|
|
85
85
|
const amountStr = formatAmount(bankTxn.amount);
|
|
86
|
-
const confidenceStr = `${match.
|
|
86
|
+
const confidenceStr = `${match.confidenceScore}%`;
|
|
87
87
|
return ` ${bankTxn.date} - ${bankTxn.payee.substring(0, 35).padEnd(35)} ${amountStr} (${confidenceStr} confidence)`;
|
|
88
88
|
}
|
|
89
|
-
function formatAmount(
|
|
89
|
+
function formatAmount(amountMilli) {
|
|
90
|
+
const amount = amountMilli / 1000;
|
|
90
91
|
const sign = amount >= 0 ? '+' : '-';
|
|
91
92
|
const absAmount = Math.abs(amount);
|
|
92
93
|
return `${sign}$${absAmount.toFixed(2)}`.padStart(10);
|
|
@@ -198,13 +199,13 @@ export function formatTransactionList(transactions, maxItems = 10) {
|
|
|
198
199
|
const lines = [];
|
|
199
200
|
const toShow = transactions.slice(0, maxItems);
|
|
200
201
|
for (const txn of toShow) {
|
|
201
|
-
if ('
|
|
202
|
-
|
|
202
|
+
if ('cleared' in txn) {
|
|
203
|
+
const ynabTxn = txn;
|
|
204
|
+
const payee = ynabTxn.payee_name ?? ynabTxn.payee ?? 'Unknown';
|
|
205
|
+
lines.push(` ${ynabTxn.date} - ${payee.substring(0, 40).padEnd(40)} ${formatAmount(ynabTxn.amount)}`);
|
|
203
206
|
}
|
|
204
207
|
else {
|
|
205
|
-
|
|
206
|
-
const payee = txn.payee_name ?? 'Unknown';
|
|
207
|
-
lines.push(` ${txn.date} - ${payee.substring(0, 40).padEnd(40)} ${formatAmount(amount)}`);
|
|
208
|
+
lines.push(formatBankTransactionLine(txn));
|
|
208
209
|
}
|
|
209
210
|
}
|
|
210
211
|
if (transactions.length > maxItems) {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function detectSignInversion(bankTransactions, ynabTransactions) {
|
|
2
|
+
if (bankTransactions.length === 0 || ynabTransactions.length === 0) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
const sampleSize = Math.min(20, bankTransactions.length);
|
|
6
|
+
const sample = bankTransactions.slice(0, sampleSize);
|
|
7
|
+
const matches = [];
|
|
8
|
+
for (const bankTxn of sample) {
|
|
9
|
+
const match = findClosestMatch(bankTxn, ynabTransactions);
|
|
10
|
+
if (match) {
|
|
11
|
+
matches.push(match);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (matches.length === 0) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
const oppositeSignCount = matches.filter((m) => m.oppositeSign).length;
|
|
18
|
+
const oppositeSignRatio = oppositeSignCount / matches.length;
|
|
19
|
+
return oppositeSignRatio > 0.5;
|
|
20
|
+
}
|
|
21
|
+
function findClosestMatch(bankTxn, ynabTransactions) {
|
|
22
|
+
const bankDate = new Date(bankTxn.date);
|
|
23
|
+
const bankAbsAmount = Math.abs(bankTxn.amount);
|
|
24
|
+
let bestMatch = null;
|
|
25
|
+
let bestScore = 0;
|
|
26
|
+
for (const ynabTxn of ynabTransactions) {
|
|
27
|
+
const ynabDate = new Date(ynabTxn.date);
|
|
28
|
+
const ynabAbsAmount = Math.abs(ynabTxn.amount);
|
|
29
|
+
const amountDiff = Math.abs(bankAbsAmount - ynabAbsAmount);
|
|
30
|
+
const amountTolerance = 100;
|
|
31
|
+
if (amountDiff > amountTolerance) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const daysDiff = Math.abs(bankDate.getTime() - ynabDate.getTime()) / (1000 * 60 * 60 * 24);
|
|
35
|
+
if (daysDiff > 7) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const amountScore = amountDiff === 0 ? 100 : Math.max(0, 100 - amountDiff / 10);
|
|
39
|
+
const dateScore = daysDiff === 0 ? 100 : Math.max(0, 100 - daysDiff * 10);
|
|
40
|
+
const score = amountScore * 0.7 + dateScore * 0.3;
|
|
41
|
+
if (score > bestScore) {
|
|
42
|
+
bestScore = score;
|
|
43
|
+
const bankSign = Math.sign(bankTxn.amount);
|
|
44
|
+
const ynabSign = Math.sign(ynabTxn.amount);
|
|
45
|
+
const oppositeSign = bankSign !== 0 && ynabSign !== 0 && bankSign !== ynabSign;
|
|
46
|
+
bestMatch = {
|
|
47
|
+
bankAmount: bankTxn.amount,
|
|
48
|
+
ynabAmount: ynabTxn.amount,
|
|
49
|
+
oppositeSign,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return bestMatch;
|
|
54
|
+
}
|
|
@@ -1,23 +1,8 @@
|
|
|
1
1
|
import type { MoneyValue } from '../../utils/money.js';
|
|
2
|
+
import type { BankTransaction as CanonicalBankTransaction, NormalizedYNABTransaction as CanonicalYNABTransaction } from '../../types/reconciliation.js';
|
|
3
|
+
export type BankTransaction = CanonicalBankTransaction;
|
|
4
|
+
export type YNABTransaction = CanonicalYNABTransaction;
|
|
2
5
|
export type MatchConfidence = 'high' | 'medium' | 'low' | 'none';
|
|
3
|
-
export interface BankTransaction {
|
|
4
|
-
id: string;
|
|
5
|
-
date: string;
|
|
6
|
-
amount: number;
|
|
7
|
-
payee: string;
|
|
8
|
-
memo?: string;
|
|
9
|
-
original_csv_row: number;
|
|
10
|
-
}
|
|
11
|
-
export interface YNABTransaction {
|
|
12
|
-
id: string;
|
|
13
|
-
date: string;
|
|
14
|
-
amount: number;
|
|
15
|
-
payee_name: string | null;
|
|
16
|
-
category_name: string | null;
|
|
17
|
-
cleared: 'cleared' | 'uncleared' | 'reconciled';
|
|
18
|
-
approved: boolean;
|
|
19
|
-
memo?: string | null;
|
|
20
|
-
}
|
|
21
6
|
export interface MatchCandidate {
|
|
22
7
|
ynab_transaction: YNABTransaction;
|
|
23
8
|
confidence: number;
|
|
@@ -25,14 +10,14 @@ export interface MatchCandidate {
|
|
|
25
10
|
explanation: string;
|
|
26
11
|
}
|
|
27
12
|
export interface TransactionMatch {
|
|
28
|
-
|
|
29
|
-
|
|
13
|
+
bankTransaction: BankTransaction;
|
|
14
|
+
ynabTransaction?: YNABTransaction;
|
|
30
15
|
candidates?: MatchCandidate[];
|
|
31
16
|
confidence: MatchConfidence;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
17
|
+
confidenceScore: number;
|
|
18
|
+
matchReason: string;
|
|
19
|
+
topConfidence?: number;
|
|
20
|
+
actionHint?: string;
|
|
36
21
|
recommendation?: string;
|
|
37
22
|
}
|
|
38
23
|
export interface BalanceInfo {
|
|
@@ -90,19 +75,20 @@ export interface ReconciliationAction {
|
|
|
90
75
|
metadata?: Record<string, unknown>;
|
|
91
76
|
}
|
|
92
77
|
export interface MatchingConfig {
|
|
78
|
+
weights: {
|
|
79
|
+
amount: number;
|
|
80
|
+
date: number;
|
|
81
|
+
payee: number;
|
|
82
|
+
};
|
|
83
|
+
amountToleranceMilliunits: number;
|
|
93
84
|
dateToleranceDays: number;
|
|
94
|
-
amountToleranceCents: number;
|
|
95
|
-
descriptionSimilarityThreshold: number;
|
|
96
85
|
autoMatchThreshold: number;
|
|
97
|
-
|
|
86
|
+
suggestedMatchThreshold: number;
|
|
87
|
+
minimumCandidateScore: number;
|
|
88
|
+
exactAmountBonus: number;
|
|
89
|
+
exactDateBonus: number;
|
|
90
|
+
exactPayeeBonus: number;
|
|
98
91
|
}
|
|
99
|
-
export declare const DEFAULT_MATCHING_CONFIG: {
|
|
100
|
-
dateToleranceDays: number;
|
|
101
|
-
amountToleranceCents: number;
|
|
102
|
-
descriptionSimilarityThreshold: number;
|
|
103
|
-
autoMatchThreshold: number;
|
|
104
|
-
suggestionThreshold: number;
|
|
105
|
-
};
|
|
106
92
|
export interface ParsedCSVData {
|
|
107
93
|
transactions: BankTransaction[];
|
|
108
94
|
format_detected: string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type * as ynab from 'ynab';
|
|
2
|
+
import type { NormalizedYNABTransaction } from '../../types/reconciliation.js';
|
|
3
|
+
export declare function normalizeYNABTransaction(txn: ynab.TransactionDetail): NormalizedYNABTransaction;
|
|
4
|
+
export declare function normalizeYNABTransactions(txns: ynab.TransactionDetail[]): NormalizedYNABTransaction[];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function normalizeYNABTransaction(txn) {
|
|
2
|
+
return {
|
|
3
|
+
id: txn.id,
|
|
4
|
+
date: txn.date,
|
|
5
|
+
amount: txn.amount,
|
|
6
|
+
payee: txn.payee_name ?? null,
|
|
7
|
+
memo: txn.memo ?? null,
|
|
8
|
+
categoryName: txn.category_name ?? null,
|
|
9
|
+
cleared: txn.cleared,
|
|
10
|
+
approved: txn.approved,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function normalizeYNABTransactions(txns) {
|
|
14
|
+
return txns.map(normalizeYNABTransaction);
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface BankTransaction {
|
|
2
|
+
id: string;
|
|
3
|
+
date: string;
|
|
4
|
+
amount: number;
|
|
5
|
+
payee: string;
|
|
6
|
+
memo?: string;
|
|
7
|
+
sourceRow: number;
|
|
8
|
+
raw: {
|
|
9
|
+
date: string;
|
|
10
|
+
amount: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
warnings?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface NormalizedYNABTransaction {
|
|
16
|
+
id: string;
|
|
17
|
+
date: string;
|
|
18
|
+
amount: number;
|
|
19
|
+
payee: string | null;
|
|
20
|
+
memo: string | null;
|
|
21
|
+
categoryName: string | null;
|
|
22
|
+
cleared: 'cleared' | 'uncleared' | 'reconciled';
|
|
23
|
+
approved: boolean;
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
# YNAB MCP Server Architecture
|
|
2
2
|
|
|
3
|
-
This guide explains the
|
|
3
|
+
This guide explains the modular architecture, core components, and architectural patterns.
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
7
|
-
- [
|
|
7
|
+
- [Modular Architecture](#modular-architecture)
|
|
8
8
|
- [Core Components](#core-components)
|
|
9
9
|
- [Dependency Injection Pattern](#dependency-injection-pattern)
|
|
10
|
-
- [Developing Tools
|
|
10
|
+
- [Developing Tools](#developing-tools)
|
|
11
11
|
- [Cache Management](#cache-management)
|
|
12
12
|
- [Service Module Patterns](#service-module-patterns)
|
|
13
|
-
- [Migration from v0.7.x](#migration-from-v07x)
|
|
14
13
|
|
|
15
|
-
##
|
|
14
|
+
## Modular Architecture
|
|
16
15
|
|
|
17
|
-
The
|
|
16
|
+
The server uses a modular architecture that improves maintainability, testability, and performance.
|
|
18
17
|
|
|
19
18
|
### Architecture Overview
|
|
20
19
|
|
|
21
|
-
The
|
|
20
|
+
The architecture consists of several key components working together:
|
|
22
21
|
|
|
23
22
|
```
|
|
24
23
|
┌─────────────────────────────────────────────────────────────┐
|
|
@@ -92,10 +91,10 @@ Focused modules handling specific server concerns:
|
|
|
92
91
|
|
|
93
92
|
## Dependency Injection Pattern
|
|
94
93
|
|
|
95
|
-
The
|
|
94
|
+
The architecture uses explicit dependency injection for better testability and maintainability:
|
|
96
95
|
|
|
97
96
|
```typescript
|
|
98
|
-
//
|
|
97
|
+
// Explicit dependency injection pattern
|
|
99
98
|
class MyService {
|
|
100
99
|
constructor(
|
|
101
100
|
private cacheManager: CacheManager,
|
|
@@ -120,11 +119,11 @@ class MyService {
|
|
|
120
119
|
const myService = new MyService(cacheManager, errorHandler, budgetResolver);
|
|
121
120
|
```
|
|
122
121
|
|
|
123
|
-
## Developing Tools
|
|
122
|
+
## Developing Tools
|
|
124
123
|
|
|
125
124
|
### Tool Development Patterns
|
|
126
125
|
|
|
127
|
-
Creating new tools
|
|
126
|
+
Creating new tools follows the Tool Registry pattern for consistency and maintainability.
|
|
128
127
|
|
|
129
128
|
#### 1. Define Tool Schema
|
|
130
129
|
|
|
@@ -250,7 +249,7 @@ export async function handleMyTool(params: MyToolRequest): Promise<any> {
|
|
|
250
249
|
|
|
251
250
|
### Understanding the Enhanced Cache System
|
|
252
251
|
|
|
253
|
-
The
|
|
252
|
+
The server includes a sophisticated caching system designed for performance and observability.
|
|
254
253
|
|
|
255
254
|
#### Cache Configuration
|
|
256
255
|
|
|
@@ -423,7 +422,7 @@ export async function handleSetDefaultBudget(params: SetDefaultBudgetRequest) {
|
|
|
423
422
|
|
|
424
423
|
### Working with Service Modules
|
|
425
424
|
|
|
426
|
-
The
|
|
425
|
+
The server decomposes functionality into focused service modules.
|
|
427
426
|
|
|
428
427
|
#### Resource Manager
|
|
429
428
|
|
|
@@ -528,122 +527,6 @@ class MyDiagnosticManager extends DiagnosticManager {
|
|
|
528
527
|
}
|
|
529
528
|
```
|
|
530
529
|
|
|
531
|
-
## Migration from v0.7.x
|
|
532
|
-
|
|
533
|
-
### No Breaking Changes for Users
|
|
534
|
-
|
|
535
|
-
**Important:** All v0.7.x tool calls, parameters, and responses work identically in v0.8.x. This section is for developers working with the internal architecture.
|
|
536
|
-
|
|
537
|
-
### Internal API Changes
|
|
538
|
-
|
|
539
|
-
#### Error Handling Migration
|
|
540
|
-
|
|
541
|
-
**v0.7.x Pattern:**
|
|
542
|
-
```typescript
|
|
543
|
-
// Direct error throwing
|
|
544
|
-
if (!budgetId) {
|
|
545
|
-
throw new Error('No budget ID provided');
|
|
546
|
-
}
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
**v0.8.x Pattern:**
|
|
550
|
-
```typescript
|
|
551
|
-
// Centralized error handling with consistent format
|
|
552
|
-
const result = BudgetResolver.resolveBudgetId(providedId, defaultId);
|
|
553
|
-
if (typeof result !== 'string') {
|
|
554
|
-
return result; // Returns properly formatted CallToolResult
|
|
555
|
-
}
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
#### Caching Migration
|
|
559
|
-
|
|
560
|
-
**v0.7.x Pattern:**
|
|
561
|
-
```typescript
|
|
562
|
-
// Manual cache management
|
|
563
|
-
const cached = cacheManager.get(key);
|
|
564
|
-
if (cached && !isExpired(cached)) {
|
|
565
|
-
return cached.data;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
const result = await apiCall();
|
|
569
|
-
cacheManager.set(key, result, ttl);
|
|
570
|
-
return result;
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
**v0.8.x Pattern:**
|
|
574
|
-
```typescript
|
|
575
|
-
// Enhanced cache wrapper with observability
|
|
576
|
-
return cacheManager.wrap(key, {
|
|
577
|
-
ttl: CACHE_TTLS.ACCOUNTS,
|
|
578
|
-
staleWhileRevalidate: 120000,
|
|
579
|
-
loader: () => apiCall()
|
|
580
|
-
});
|
|
581
|
-
```
|
|
582
|
-
|
|
583
|
-
#### Tool Registration Migration
|
|
584
|
-
|
|
585
|
-
**v0.7.x Pattern:**
|
|
586
|
-
```typescript
|
|
587
|
-
// Direct switch statement in handleCallTool
|
|
588
|
-
case 'my_tool':
|
|
589
|
-
return withSecurityWrapper(async () => {
|
|
590
|
-
const validated = MyToolSchema.parse(params);
|
|
591
|
-
return await handleMyTool(validated);
|
|
592
|
-
});
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
**v0.8.x Pattern:**
|
|
596
|
-
```typescript
|
|
597
|
-
// Registry-based registration
|
|
598
|
-
registry.register({
|
|
599
|
-
name: 'my_tool',
|
|
600
|
-
description: 'Tool description',
|
|
601
|
-
inputSchema: MyToolSchema,
|
|
602
|
-
handler: adapt(handleMyTool),
|
|
603
|
-
defaultArgumentResolver: resolveBudgetId()
|
|
604
|
-
});
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
### Testing Pattern Updates
|
|
608
|
-
|
|
609
|
-
**Enhanced Dependency Injection for Testing:**
|
|
610
|
-
|
|
611
|
-
```typescript
|
|
612
|
-
// v0.8.x - Mock individual services
|
|
613
|
-
const mockCacheManager = {
|
|
614
|
-
wrap: vi.fn().mockImplementation((key, options) => options.loader()),
|
|
615
|
-
getStats: vi.fn().mockReturnValue({ hit_rate: 0.5 })
|
|
616
|
-
};
|
|
617
|
-
|
|
618
|
-
const mockErrorHandler = {
|
|
619
|
-
createErrorResponse: vi.fn().mockReturnValue({ success: false })
|
|
620
|
-
};
|
|
621
|
-
|
|
622
|
-
// Test with mocked dependencies
|
|
623
|
-
const service = new MyService(mockCacheManager, mockErrorHandler);
|
|
624
|
-
```
|
|
625
|
-
|
|
626
|
-
### Import Path Updates
|
|
627
|
-
|
|
628
|
-
Most imports remain the same due to barrel exports:
|
|
629
|
-
|
|
630
|
-
```typescript
|
|
631
|
-
// Still works (barrel export)
|
|
632
|
-
import { handleMyTool } from '../tools/myTool.js';
|
|
633
|
-
|
|
634
|
-
// New modular imports available
|
|
635
|
-
import { parseCSV } from '../tools/compareTransactions/parser.js';
|
|
636
|
-
import { findMatches } from '../tools/compareTransactions/matcher.js';
|
|
637
|
-
import { formatResults } from '../tools/compareTransactions/formatter.js';
|
|
638
|
-
```
|
|
639
|
-
|
|
640
|
-
### Performance Improvements to Expect
|
|
641
|
-
|
|
642
|
-
- **Cache Hit Rate**: 60-80% for repeated operations
|
|
643
|
-
- **Initial Load Time**: Faster due to cache warming
|
|
644
|
-
- **Memory Usage**: More efficient with LRU eviction
|
|
645
|
-
- **Error Response Time**: Faster with pre-formatted responses
|
|
646
|
-
|
|
647
530
|
---
|
|
648
531
|
|
|
649
532
|
For practical development patterns and examples, see [`DEVELOPMENT.md`](DEVELOPMENT.md).
|