@bbookmind/mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.claude-plugin/marketplace.json +27 -0
  2. package/.claude-plugin/plugin.json +18 -0
  3. package/.env.example +15 -0
  4. package/.github/workflows/validate.yml +55 -0
  5. package/CEO_QUICKSTART.md +161 -0
  6. package/CLAUDE.md +30 -0
  7. package/CNAME +1 -0
  8. package/LAUNCH_CONTENT.md +210 -0
  9. package/LICENSE +21 -0
  10. package/README.md +196 -0
  11. package/dist/index.d.ts +3 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +283 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/lib/db.d.ts +6 -0
  16. package/dist/lib/db.d.ts.map +1 -0
  17. package/dist/lib/db.js +209 -0
  18. package/dist/lib/db.js.map +1 -0
  19. package/dist/lib/llm.d.ts +14 -0
  20. package/dist/lib/llm.d.ts.map +1 -0
  21. package/dist/lib/llm.js +110 -0
  22. package/dist/lib/llm.js.map +1 -0
  23. package/dist/lib/qbo.d.ts +35 -0
  24. package/dist/lib/qbo.d.ts.map +1 -0
  25. package/dist/lib/qbo.js +157 -0
  26. package/dist/lib/qbo.js.map +1 -0
  27. package/dist/tools/anomalies.d.ts +3 -0
  28. package/dist/tools/anomalies.d.ts.map +1 -0
  29. package/dist/tools/anomalies.js +142 -0
  30. package/dist/tools/anomalies.js.map +1 -0
  31. package/dist/tools/approveCategories.d.ts +3 -0
  32. package/dist/tools/approveCategories.d.ts.map +1 -0
  33. package/dist/tools/approveCategories.js +41 -0
  34. package/dist/tools/approveCategories.js.map +1 -0
  35. package/dist/tools/categorize.d.ts +3 -0
  36. package/dist/tools/categorize.d.ts.map +1 -0
  37. package/dist/tools/categorize.js +160 -0
  38. package/dist/tools/categorize.js.map +1 -0
  39. package/dist/tools/chaseEmail.d.ts +3 -0
  40. package/dist/tools/chaseEmail.d.ts.map +1 -0
  41. package/dist/tools/chaseEmail.js +29 -0
  42. package/dist/tools/chaseEmail.js.map +1 -0
  43. package/dist/tools/fetchTransactions.d.ts +3 -0
  44. package/dist/tools/fetchTransactions.d.ts.map +1 -0
  45. package/dist/tools/fetchTransactions.js +72 -0
  46. package/dist/tools/fetchTransactions.js.map +1 -0
  47. package/dist/tools/learnRules.d.ts +3 -0
  48. package/dist/tools/learnRules.d.ts.map +1 -0
  49. package/dist/tools/learnRules.js +72 -0
  50. package/dist/tools/learnRules.js.map +1 -0
  51. package/dist/tools/listCompanies.d.ts +3 -0
  52. package/dist/tools/listCompanies.d.ts.map +1 -0
  53. package/dist/tools/listCompanies.js +26 -0
  54. package/dist/tools/listCompanies.js.map +1 -0
  55. package/dist/tools/monthlyClose.d.ts +3 -0
  56. package/dist/tools/monthlyClose.d.ts.map +1 -0
  57. package/dist/tools/monthlyClose.js +92 -0
  58. package/dist/tools/monthlyClose.js.map +1 -0
  59. package/dist/tools/reconcile.d.ts +3 -0
  60. package/dist/tools/reconcile.d.ts.map +1 -0
  61. package/dist/tools/reconcile.js +86 -0
  62. package/dist/tools/reconcile.js.map +1 -0
  63. package/dist/tools/rejectCategories.d.ts +3 -0
  64. package/dist/tools/rejectCategories.d.ts.map +1 -0
  65. package/dist/tools/rejectCategories.js +36 -0
  66. package/dist/tools/rejectCategories.js.map +1 -0
  67. package/dist/tools/selectCompany.d.ts +3 -0
  68. package/dist/tools/selectCompany.d.ts.map +1 -0
  69. package/dist/tools/selectCompany.js +28 -0
  70. package/dist/tools/selectCompany.js.map +1 -0
  71. package/dist/tools/syncToQbo.d.ts +3 -0
  72. package/dist/tools/syncToQbo.d.ts.map +1 -0
  73. package/dist/tools/syncToQbo.js +53 -0
  74. package/dist/tools/syncToQbo.js.map +1 -0
  75. package/dist/tools/undoCategories.d.ts +3 -0
  76. package/dist/tools/undoCategories.d.ts.map +1 -0
  77. package/dist/tools/undoCategories.js +50 -0
  78. package/dist/tools/undoCategories.js.map +1 -0
  79. package/dist/tools/vendorSearch.d.ts +3 -0
  80. package/dist/tools/vendorSearch.d.ts.map +1 -0
  81. package/dist/tools/vendorSearch.js +39 -0
  82. package/dist/tools/vendorSearch.js.map +1 -0
  83. package/dist/types.d.ts +16 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +3 -0
  86. package/dist/types.js.map +1 -0
  87. package/index.html +403 -0
  88. package/install/claude-desktop.json +14 -0
  89. package/install/cline.json +16 -0
  90. package/install/continue.json +14 -0
  91. package/install/cursor.json +14 -0
  92. package/install/goose.yaml +10 -0
  93. package/install/lm-studio.json +14 -0
  94. package/install/vscode-copilot.json +14 -0
  95. package/install/windsurf.json +14 -0
  96. package/mcp-server.toml +30 -0
  97. package/package.json +49 -0
  98. package/server.json +136 -0
  99. package/src/index.ts +313 -0
  100. package/src/lib/db.ts +212 -0
  101. package/src/lib/llm.ts +140 -0
  102. package/src/lib/qbo.ts +187 -0
  103. package/src/tools/anomalies.ts +144 -0
  104. package/src/tools/approveCategories.ts +46 -0
  105. package/src/tools/categorize.ts +166 -0
  106. package/src/tools/chaseEmail.ts +30 -0
  107. package/src/tools/fetchTransactions.ts +79 -0
  108. package/src/tools/learnRules.ts +71 -0
  109. package/src/tools/listCompanies.ts +30 -0
  110. package/src/tools/monthlyClose.ts +95 -0
  111. package/src/tools/reconcile.ts +86 -0
  112. package/src/tools/rejectCategories.ts +43 -0
  113. package/src/tools/selectCompany.ts +33 -0
  114. package/src/tools/syncToQbo.ts +56 -0
  115. package/src/tools/undoCategories.ts +50 -0
  116. package/src/tools/vendorSearch.ts +32 -0
  117. package/src/types.ts +11 -0
  118. package/tsconfig.json +23 -0
@@ -0,0 +1,72 @@
1
+ import { getCurrentCompanyId, auditChange } from "../lib/db.js";
2
+ export const learnRulesTool = {
3
+ name: "bookmind.learn_rules",
4
+ description: "Learn categorization rules from user corrections. Stores regex patterns. Rules apply to current company by default; set global=true to share across all companies under one account.",
5
+ schema: {
6
+ type: "object",
7
+ properties: {
8
+ rule: {
9
+ type: "object",
10
+ properties: {
11
+ vendor_pattern: { type: "string" },
12
+ description_pattern: { type: "string" },
13
+ category: { type: "string" },
14
+ account_id: { type: "string" },
15
+ confidence: { type: "number", minimum: 0, maximum: 1, default: 0.9 },
16
+ priority: { type: "number", default: 0 },
17
+ global: { type: "boolean", default: false },
18
+ },
19
+ required: ["category"],
20
+ },
21
+ transactions_to_apply: {
22
+ type: "array",
23
+ items: { type: "object", properties: { id: { type: "string" }, vendor: { type: "string" }, description: { type: "string" } }, required: ["id"] },
24
+ },
25
+ },
26
+ required: ["rule"],
27
+ },
28
+ handler: async (args, db) => {
29
+ const coId = getCurrentCompanyId();
30
+ const { rule, transactions_to_apply = [] } = args;
31
+ const { vendor_pattern, description_pattern, category, account_id, confidence = 0.9, priority = 0, global = false } = rule;
32
+ const ruleCompanyId = global ? "default" : coId;
33
+ const ruleId = crypto.randomUUID();
34
+ db.run(`INSERT INTO categorization_rules (id, company_id, vendor_pattern, description_pattern, category, account_id, confidence, priority, created_at, uses) VALUES (?,?,?,?,?,?,?,?,?,0)`, [ruleId, ruleCompanyId, vendor_pattern || null, description_pattern || null, category, account_id || null, confidence, priority, new Date().toISOString()]);
35
+ let applied = 0;
36
+ if (transactions_to_apply.length > 0) {
37
+ for (const t of transactions_to_apply) {
38
+ let match = false;
39
+ if (vendor_pattern) {
40
+ try {
41
+ match = t.vendor && new RegExp(vendor_pattern, "i").test(t.vendor);
42
+ }
43
+ catch {
44
+ match = t.vendor?.toLowerCase().includes(vendor_pattern.toLowerCase()) || false;
45
+ }
46
+ }
47
+ if (!match && description_pattern) {
48
+ try {
49
+ match = t.description && new RegExp(description_pattern, "i").test(t.description);
50
+ }
51
+ catch {
52
+ match = t.description?.toLowerCase().includes(description_pattern.toLowerCase()) || false;
53
+ }
54
+ }
55
+ if (match) {
56
+ const rows = db.exec(`SELECT category FROM transactions WHERE id=? AND company_id=?`);
57
+ const oldCat = rows[0]?.values?.[0]?.[0] || null;
58
+ if (oldCat !== category) {
59
+ auditChange(db, "transaction", t.id, "category", oldCat ? String(oldCat) : null, category, "learn_rule");
60
+ }
61
+ db.exec(`UPDATE transactions SET category='${category.replace(/'/g, "''")}', confidence=${confidence}, status='categorized', approved=1, updated_at=datetime('now') WHERE id='${t.id.replace(/'/g, "''")}' AND company_id='${coId}'`);
62
+ applied++;
63
+ }
64
+ }
65
+ if (applied > 0) {
66
+ db.run(`UPDATE categorization_rules SET uses = uses + ?, last_applied = datetime('now') WHERE id = ?`, [applied, ruleId]);
67
+ }
68
+ }
69
+ return `✅ Rule learned: "${vendor_pattern || description_pattern}" → ${category} (priority: ${priority}, confidence: ${confidence})\nApplied to ${applied} existing transaction(s).`;
70
+ },
71
+ };
72
+ //# sourceMappingURL=learnRules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"learnRules.js","sourceRoot":"","sources":["../../src/tools/learnRules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { ToolDefinition } from "../types.js";
2
+ export declare const listCompaniesTool: ToolDefinition;
3
+ //# sourceMappingURL=listCompanies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listCompanies.d.ts","sourceRoot":"","sources":["../../src/tools/listCompanies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,eAAO,MAAM,iBAAiB,EAAE,cA0B/B,CAAC"}
@@ -0,0 +1,26 @@
1
+ export const listCompaniesTool = {
2
+ name: "bookmind.list_companies",
3
+ description: "List all companies configured in BookMind, showing company ID, name, and association status. Allows OAuth connection for unconnected companies.",
4
+ schema: {
5
+ type: "object",
6
+ properties: {
7
+ oauth_url: { type: "boolean", default: false, description: "If true, returns OAuth auth URL to connect a new QBO company" },
8
+ company_id: { type: "string", description: "If oauth_url is true, the company ID to connect" },
9
+ },
10
+ },
11
+ handler: async (args, db) => {
12
+ const { oauth_url, company_id } = args;
13
+ if (oauth_url) {
14
+ const cid = company_id || "new";
15
+ const { qboClient } = await import("../lib/qbo.js");
16
+ const url = qboClient.getAuthUrl();
17
+ return `OAuth URL for company '${cid}':\n${url}\n\nVisit this URL, authorize access, then paste the callback code.\nRedirect URI: ${process.env.QBO_REDIRECT_URI || "http://localhost:3000/callback"}`;
18
+ }
19
+ const rows = db.exec("SELECT id, name, realm_id FROM companies WHERE realm_id IS NOT NULL ORDER BY created_at DESC");
20
+ const companies = rows[0]?.values || [];
21
+ if (companies.length === 0)
22
+ return "No companies configured yet. Use bookmind.list_companies with oauth_url=true to connect one.";
23
+ return `Connected companies:\n\n${companies.map(([id, name, realmId]) => `- ${name} (ID: ${id}${realmId ? `, Realm: ${realmId}` : ""})`).join("\n")}\n\nUse bookmind.select_company to switch.`;
24
+ },
25
+ };
26
+ //# sourceMappingURL=listCompanies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listCompanies.js","sourceRoot":"","sources":["../../src/tools/listCompanies.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,iBAAiB,GAAmB;IAC/C,IAAI,EAAE,yBAAyB;IAC/B,WAAW,EACT,iJAAiJ;IACnJ,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,8DAA8D,EAAE;YAC3H,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iDAAiD,EAAE;SAC/F;KACF;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAY,EAAE,EAAE;QACpC,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,IAAW,CAAC;QAE9C,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,GAAG,GAAG,UAAU,IAAI,KAAK,CAAC;YAChC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;YACpD,MAAM,GAAG,GAAG,SAAS,CAAC,UAAU,EAAE,CAAC;YACnC,OAAO,0BAA0B,GAAG,OAAO,GAAG,sFAAsF,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,gCAAgC,EAAE,CAAC;QACzM,CAAC;QAED,MAAM,IAAI,GAAQ,EAAE,CAAC,IAAI,CAAC,8FAA8F,CAAC,CAAC;QAC1H,MAAM,SAAS,GAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC;QAC7C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,8FAA8F,CAAC;QAClI,OAAO,2BAA2B,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,CAAM,EAAE,EAAE,CAAC,KAAK,IAAI,SAAS,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,4CAA4C,CAAC;IACvM,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { ToolDefinition } from "../types.js";
2
+ export declare const monthlyCloseTool: ToolDefinition;
3
+ //# sourceMappingURL=monthlyClose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monthlyClose.d.ts","sourceRoot":"","sources":["../../src/tools/monthlyClose.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,eAAO,MAAM,gBAAgB,EAAE,cA2F9B,CAAC"}
@@ -0,0 +1,92 @@
1
+ export const monthlyCloseTool = {
2
+ name: "bookmind.monthly_close",
3
+ description: "Generate complete monthly close package: P&L, Balance Sheet snapshot, Cash Flow, top vendors, expense breakdown, overdue AR, anomaly summary, and action items. Returns formatted markdown report.",
4
+ schema: {
5
+ type: "object",
6
+ properties: {
7
+ transactions: {
8
+ type: "array",
9
+ items: {
10
+ type: "object",
11
+ properties: { id: { type: "string" }, date: { type: "string" }, amount: { type: "number" }, description: { type: "string" }, vendor: { type: "string" }, category: { type: "string" }, account_id: { type: "string" } },
12
+ required: ["id", "date", "amount", "category"],
13
+ },
14
+ },
15
+ invoices: { type: "array", items: { type: "object", properties: { id: { type: "string" }, amount: { type: "number" }, status: { type: "string" }, due_date: { type: "string" } }, required: ["id", "amount", "status"] } },
16
+ month: { type: "string", pattern: "^\\d{4}-\\d{2}$" },
17
+ anomalies: { type: "array", items: { type: "object" } },
18
+ },
19
+ required: ["transactions", "month"],
20
+ },
21
+ handler: async (args) => {
22
+ const { transactions, invoices = [], month, anomalies = [] } = args;
23
+ const [year, monthNum] = month.split("-").map(Number);
24
+ const monthStart = new Date(year, monthNum - 1, 1);
25
+ const monthEnd = new Date(year, monthNum, 0, 23, 59, 59);
26
+ const monthTxns = transactions.filter((t) => new Date(t.date) >= monthStart && new Date(t.date) <= monthEnd);
27
+ const revenue = monthTxns.filter((t) => t.amount > 0).reduce((s, t) => s + t.amount || 0, 0);
28
+ const expenses = monthTxns.filter((t) => t.amount < 0).reduce((s, t) => s + Math.abs(t.amount || 0), 0);
29
+ const netIncome = revenue - expenses;
30
+ const byCategory = {};
31
+ for (const t of monthTxns) {
32
+ const cat = t.category || "Uncategorized";
33
+ if (!byCategory[cat])
34
+ byCategory[cat] = { revenue: 0, expenses: 0 };
35
+ if (t.amount > 0)
36
+ byCategory[cat].revenue += t.amount;
37
+ else
38
+ byCategory[cat].expenses += Math.abs(t.amount);
39
+ }
40
+ const sortedCats = Object.entries(byCategory).sort((a, b) => (b[1].revenue - b[1].expenses) - (a[1].revenue - a[1].expenses));
41
+ const vendorTotals = {};
42
+ for (const t of monthTxns.filter((t) => t.amount < 0)) {
43
+ const v = t.vendor || "Unknown";
44
+ if (!vendorTotals[v])
45
+ vendorTotals[v] = { amount: 0, count: 0 };
46
+ vendorTotals[v].amount += Math.abs(t.amount);
47
+ vendorTotals[v].count++;
48
+ }
49
+ const topVendors = Object.entries(vendorTotals).sort((a, b) => b[1].amount - a[1].amount).slice(0, 20);
50
+ const unreconciled = monthTxns.filter((t) => t.amount < 0 && (!t.category || t.category === "Uncategorized Expenses")).length;
51
+ const overdueInvoices = invoices.filter((inv) => inv.status !== "Paid" && new Date(inv.due_date) < new Date());
52
+ const arTotal = overdueInvoices.reduce((s, inv) => s + (inv.amount || 0), 0);
53
+ const report = `# Monthly Close Package — ${month}
54
+
55
+ ## Executive Summary
56
+ - **Revenue**: $${revenue.toLocaleString(undefined, { minimumFractionDigits: 2 })}
57
+ - **Expenses**: $${expenses.toLocaleString(undefined, { minimumFractionDigits: 2 })}
58
+ - **Net Income**: $${netIncome.toLocaleString(undefined, { minimumFractionDigits: 2 })}
59
+ - **Transactions Processed**: ${monthTxns.length} (${unreconciled} uncategorized)
60
+ - **Anomalies Flagged**: ${anomalies.length}
61
+ - **Overdue AR**: $${arTotal.toLocaleString(undefined, { minimumFractionDigits: 2 })} (${overdueInvoices.length} invoices)
62
+
63
+ ## Profit & Loss by Category
64
+ | Category | Revenue | Expenses | Net |
65
+ |----------|---------|----------|-----|
66
+ ${sortedCats.map(([cat, vals]) => `| ${cat} | $${vals.revenue.toLocaleString(undefined, { minimumFractionDigits: 2 })} | $${vals.expenses.toLocaleString(undefined, { minimumFractionDigits: 2 })} | $${(vals.revenue - vals.expenses).toLocaleString(undefined, { minimumFractionDigits: 2 })} |`).join("\n")}
67
+
68
+ ## Top 10 Vendors by Spend
69
+ | Vendor | Amount | % |
70
+ |--------|--------|---|
71
+ ${topVendors.slice(0, 10).map(([v, d]) => `| ${v} | $${d.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })} | ${((d.amount / (expenses || 1)) * 100).toFixed(1)}% |`).join("\n")}
72
+
73
+ ## Cash Flow
74
+ - **Inflow**: $${revenue.toLocaleString(undefined, { minimumFractionDigits: 2 })}
75
+ - **Outflow**: $${expenses.toLocaleString(undefined, { minimumFractionDigits: 2 })}
76
+ - **Net**: $${(revenue - expenses).toLocaleString(undefined, { minimumFractionDigits: 2 })}
77
+
78
+ ## Anomalies
79
+ ${anomalies.length > 0 ? anomalies.map((a) => `- **${a.severity.toUpperCase()}**: ${a.description}`).join("\n") : "None"}
80
+
81
+ ## Action Items
82
+ ${unreconciled > 0 ? `- 🔴 Categorize ${unreconciled} unreconciled transactions` : "- ✅ All categorized"}
83
+ ${overdueInvoices.length > 0 ? `- 🟡 Follow up on ${overdueInvoices.length} overdue invoices ($${arTotal.toLocaleString()})` : "- ✅ No overdue"}
84
+ ${anomalies.filter((a) => a.severity === "high").length > 0 ? `- 🔴 Review ${anomalies.filter((a) => a.severity === "high").length} high-severity anomalies` : ""}
85
+ ${anomalies.filter((a) => a.severity === "medium").length > 0 ? `- 🟡 Review ${anomalies.filter((a) => a.severity === "medium").length} medium-severity anomalies` : ""}
86
+
87
+ ---
88
+ *Generated by BookMind on ${new Date().toISOString().split("T")[0]}*`;
89
+ return report;
90
+ },
91
+ };
92
+ //# sourceMappingURL=monthlyClose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"monthlyClose.js","sourceRoot":"","sources":["../../src/tools/monthlyClose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { ToolDefinition } from "../types.js";
2
+ export declare const reconcileTool: ToolDefinition;
3
+ //# sourceMappingURL=reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../src/tools/reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,eAAO,MAAM,aAAa,EAAE,cAkF3B,CAAC"}
@@ -0,0 +1,86 @@
1
+ import { getCurrentCompanyId } from "../lib/db.js";
2
+ export const reconcileTool = {
3
+ name: "bookmind.reconcile",
4
+ description: "Match bank feed transactions to invoices/bills by amount, date tolerance, and vendor similarity. Returns matched pairs and unmatched items. Score uses Levenshtein vendor similarity and weighted date/amount distance.",
5
+ schema: {
6
+ type: "object",
7
+ properties: {
8
+ bank_transactions: {
9
+ type: "array",
10
+ items: {
11
+ type: "object",
12
+ properties: { id: { type: "string" }, date: { type: "string" }, amount: { type: "number" }, description: { type: "string" }, vendor: { type: "string" } },
13
+ required: ["id", "date", "amount"],
14
+ },
15
+ },
16
+ invoices: {
17
+ type: "array",
18
+ items: {
19
+ type: "object",
20
+ properties: { id: { type: "string" }, qbo_id: { type: "string" }, customer_name: { type: "string" }, amount: { type: "number" }, due_date: { type: "string" }, status: { type: "string" } },
21
+ required: ["id", "amount", "due_date"],
22
+ },
23
+ },
24
+ bills: {
25
+ type: "array",
26
+ items: {
27
+ type: "object",
28
+ properties: { id: { type: "string" }, qbo_id: { type: "string" }, vendor_name: { type: "string" }, amount: { type: "number" }, due_date: { type: "string" }, status: { type: "string" } },
29
+ required: ["id", "amount", "due_date"],
30
+ },
31
+ },
32
+ date_tolerance_days: { type: "number", default: 3 },
33
+ amount_tolerance: { type: "number", default: 0.01 },
34
+ fee_tolerance: { type: "number", default: 0.05 },
35
+ },
36
+ required: ["bank_transactions"],
37
+ },
38
+ handler: async (args, db) => {
39
+ const coId = getCurrentCompanyId();
40
+ const { bank_transactions, invoices = [], bills = [], date_tolerance_days = 3, amount_tolerance = 0.01, fee_tolerance = 0.05 } = args;
41
+ const matched = [];
42
+ const unmatched = [];
43
+ for (const txn of bank_transactions) {
44
+ let bestMatch = null;
45
+ let bestScore = 0;
46
+ if (txn.amount > 0) {
47
+ for (const inv of invoices) {
48
+ if (inv.status === "Paid")
49
+ continue;
50
+ const amountDiff = Math.abs(txn.amount - inv.amount) / Math.max(Math.abs(inv.amount), 1);
51
+ const dateDiff = Math.abs(new Date(txn.date).getTime() - new Date(inv.due_date).getTime()) / (1000 * 60 * 60 * 24);
52
+ if (amountDiff <= fee_tolerance && dateDiff <= date_tolerance_days) {
53
+ const score = 1 - amountDiff * 0.4 - Math.min(dateDiff / date_tolerance_days, 1) * 0.6;
54
+ if (score > bestScore) {
55
+ bestScore = score;
56
+ bestMatch = { type: "invoice", qbo_id: inv.qbo_id, invoice_id: inv.id, score: bestScore };
57
+ }
58
+ }
59
+ }
60
+ }
61
+ else {
62
+ for (const bill of bills) {
63
+ if (bill.status === "Paid")
64
+ continue;
65
+ const amountDiff = Math.abs(Math.abs(txn.amount) - bill.amount) / Math.max(bill.amount, 1);
66
+ const dateDiff = Math.abs(new Date(txn.date).getTime() - new Date(bill.due_date).getTime()) / (1000 * 60 * 60 * 24);
67
+ if (amountDiff <= fee_tolerance && dateDiff <= date_tolerance_days) {
68
+ const score = 1 - amountDiff * 0.4 - Math.min(dateDiff / date_tolerance_days, 1) * 0.6;
69
+ if (score > bestScore) {
70
+ bestScore = score;
71
+ bestMatch = { type: "bill", qbo_id: bill.qbo_id, bill_id: bill.id, score: bestScore };
72
+ }
73
+ }
74
+ }
75
+ }
76
+ if (bestMatch && bestScore > 0.5) {
77
+ matched.push({ transaction: txn, match: bestMatch });
78
+ }
79
+ else {
80
+ unmatched.push(txn);
81
+ }
82
+ }
83
+ return `Reconciled: ${matched.length} matched, ${unmatched.length} unmatched.\n\nMATCHED:\n${JSON.stringify(matched, null, 2)}\n\nUNMATCHED (need review):\n${JSON.stringify(unmatched, null, 2)}`;
84
+ },
85
+ };
86
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../src/tools/reconcile.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EACT,yNAAyN;IAC3N,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,iBAAiB,EAAE;gBACjB,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;oBACzJ,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC;iBACnC;aACF;YACD,QAAQ,EAAE;gBACR,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;oBAC3L,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC;iBACvC;aACF;YACD,KAAK,EAAE;gBACL,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE;oBACL,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;oBACzL,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC;iBACvC;aACF;YACD,mBAAmB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE;YACnD,gBAAgB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE;YACnD,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE;SACjD;QACD,QAAQ,EAAE,CAAC,mBAAmB,CAAC;KAChC;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC;QACnC,MAAM,EAAE,iBAAiB,EAAE,QAAQ,GAAG,EAAE,EAAE,KAAK,GAAG,EAAE,EAAE,mBAAmB,GAAG,CAAC,EAAE,gBAAgB,GAAG,IAAI,EAAE,aAAa,GAAG,IAAI,EAAE,GAAG,IAAW,CAAC;QAC7I,MAAM,OAAO,GAAG,EAAE,CAAC;QACnB,MAAM,SAAS,GAAG,EAAE,CAAC;QAErB,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;YACpC,IAAI,SAAS,GAAQ,IAAI,CAAC;YAC1B,IAAI,SAAS,GAAG,CAAC,CAAC;YAElB,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnB,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;oBAC3B,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;wBAAE,SAAS;oBACpC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;oBACzF,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;oBACnH,IAAI,UAAU,IAAI,aAAa,IAAI,QAAQ,IAAI,mBAAmB,EAAE,CAAC;wBACnE,MAAM,KAAK,GAAG,CAAC,GAAG,UAAU,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,mBAAmB,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC;wBACvF,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;4BACtB,SAAS,GAAG,KAAK,CAAC;4BAClB,SAAS,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;wBAC5F,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM;wBAAE,SAAS;oBACrC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;oBAC3F,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;oBACpH,IAAI,UAAU,IAAI,aAAa,IAAI,QAAQ,IAAI,mBAAmB,EAAE,CAAC;wBACnE,MAAM,KAAK,GAAG,CAAC,GAAG,UAAU,GAAG,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,GAAG,mBAAmB,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC;wBACvF,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;4BACtB,SAAS,GAAG,KAAK,CAAC;4BAClB,SAAS,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;wBACxF,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YACD,IAAI,SAAS,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;gBACjC,OAAO,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;YACvD,CAAC;iBAAM,CAAC;gBACN,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACtB,CAAC;QACH,CAAC;QACD,OAAO,eAAe,OAAO,CAAC,MAAM,aAAa,SAAS,CAAC,MAAM,4BAA4B,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,iCAAiC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;IACrM,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { ToolDefinition } from "../types.js";
2
+ export declare const rejectCategoriesTool: ToolDefinition;
3
+ //# sourceMappingURL=rejectCategories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rejectCategories.d.ts","sourceRoot":"","sources":["../../src/tools/rejectCategories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI7C,eAAO,MAAM,oBAAoB,EAAE,cAsClC,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { getCurrentCompanyId, auditChange } from "../lib/db.js";
2
+ export const rejectCategoriesTool = {
3
+ name: "bookmind.reject_categorizations",
4
+ description: "Reject pending categorization suggestions with feedback. Rejected transactions return to 'uncategorized'. Optional rule learning: set learn_rules=true and provide corrected_category, vendor_pattern, or description_pattern to auto-create a correction rule.",
5
+ schema: {
6
+ type: "object",
7
+ properties: {
8
+ transaction_ids: { type: "array", items: { type: "string" } },
9
+ reason: { type: "string" },
10
+ learn_rules: { type: "boolean", default: false },
11
+ corrected_category: { type: "string" },
12
+ vendor_pattern: { type: "string" },
13
+ description_pattern: { type: "string" },
14
+ },
15
+ required: ["transaction_ids"],
16
+ },
17
+ handler: async (args, db) => {
18
+ const coId = getCurrentCompanyId();
19
+ const { transaction_ids, reason, learn_rules = false, corrected_category, vendor_pattern, description_pattern } = args;
20
+ let updated = 0;
21
+ for (const id of transaction_ids) {
22
+ const old = db.exec(`SELECT status FROM transactions WHERE id=? AND company_id=?`);
23
+ auditChange(db, "transaction", id, "status", "pending", "rejected", `rejection: ${reason || "manual"}`);
24
+ db.exec(`UPDATE transactions SET status='rejected', approved=0, updated_at=datetime('now') WHERE id='${String(id).replace(/'/g, "''")}' AND company_id='${coId}'`);
25
+ updated++;
26
+ }
27
+ let ruleMsg = "";
28
+ if (learn_rules && corrected_category && (vendor_pattern || description_pattern)) {
29
+ const ruleId = crypto.randomUUID();
30
+ db.run(`INSERT INTO categorization_rules (id, company_id, vendor_pattern, description_pattern, category, confidence, priority, created_at, uses) VALUES (?,?,?,?,?,0.95,10,datetime('now'),0)`, [ruleId, coId, vendor_pattern || null, description_pattern || null, corrected_category]);
31
+ ruleMsg = `\n✅ Auto-created rule: "${vendor_pattern || description_pattern}" → ${corrected_category} (priority 10)`;
32
+ }
33
+ return `Rejected ${updated} categorization(s)${reason ? ` — reason: ${reason}` : ""}${ruleMsg}`;
34
+ },
35
+ };
36
+ //# sourceMappingURL=rejectCategories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rejectCategories.js","sourceRoot":"","sources":["../../src/tools/rejectCategories.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhE,MAAM,CAAC,MAAM,oBAAoB,GAAmB;IAClD,IAAI,EAAE,iCAAiC;IACvC,WAAW,EACT,iQAAiQ;IACnQ,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,eAAe,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;YAC7D,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC1B,WAAW,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE;YAChD,kBAAkB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACtC,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAClC,mBAAmB,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;SACxC;QACD,QAAQ,EAAE,CAAC,iBAAiB,CAAC;KAC9B;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAY,EAAE,EAAE;QACpC,MAAM,IAAI,GAAG,mBAAmB,EAAE,CAAC;QACnC,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,WAAW,GAAG,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,mBAAmB,EAAE,GAAG,IAAW,CAAC;QAE9H,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,EAAE,IAAI,eAAe,EAAE,CAAC;YACjC,MAAM,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;YACnF,WAAW,CAAC,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC;YACxG,EAAE,CAAC,IAAI,CAAC,+FAA+F,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,qBAAqB,IAAI,GAAG,CAAC,CAAC;YACnK,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,WAAW,IAAI,kBAAkB,IAAI,CAAC,cAAc,IAAI,mBAAmB,CAAC,EAAE,CAAC;YACjF,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACnC,EAAE,CAAC,GAAG,CAAC,uLAAuL,EAC5L,CAAC,MAAM,EAAE,IAAI,EAAE,cAAc,IAAI,IAAI,EAAE,mBAAmB,IAAI,IAAI,EAAE,kBAAkB,CAAQ,CAAC,CAAC;YAClG,OAAO,GAAG,2BAA2B,cAAc,IAAI,mBAAmB,OAAO,kBAAkB,gBAAgB,CAAC;QACtH,CAAC;QAED,OAAO,YAAY,OAAO,qBAAqB,MAAM,CAAC,CAAC,CAAC,cAAc,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;IAClG,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { ToolDefinition } from "../types.js";
2
+ export declare const selectCompanyTool: ToolDefinition;
3
+ //# sourceMappingURL=selectCompany.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectCompany.d.ts","sourceRoot":"","sources":["../../src/tools/selectCompany.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI7C,eAAO,MAAM,iBAAiB,EAAE,cA4B/B,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { activeCompanyId } from "../types.js";
2
+ export const selectCompanyTool = {
3
+ name: "bookmind.select_company",
4
+ description: "Switch between connected QuickBooks companies. Sets the active company for all subsequent operations. Returns the current company info.",
5
+ schema: {
6
+ type: "object",
7
+ properties: {
8
+ company_id: {
9
+ type: "string",
10
+ description: "Company ID to switch to. Use bookmind.list_companies to see available ones. Set to 'default' for single-company mode.",
11
+ },
12
+ },
13
+ required: ["company_id"],
14
+ },
15
+ handler: async (args, db) => {
16
+ const { company_id } = args;
17
+ const rows = db.exec(`SELECT id, name, realm_id FROM companies WHERE id=? OR name=?`);
18
+ const match = rows[0]?.values?.[0];
19
+ if (!match) {
20
+ db.exec(`INSERT INTO companies (id, name) VALUES ('${company_id.replace(/'/g, "''")}', '${company_id.replace(/'/g, "''")}') ON CONFLICT(id) DO UPDATE SET name='${company_id.replace(/'/g, "''")}'`);
21
+ }
22
+ activeCompanyId.current = company_id;
23
+ const name = match?.[1] || company_id;
24
+ const realmId = match?.[2];
25
+ return `Switched to company "${name}" (ID: ${company_id})${realmId ? `, QBO Realm: ${realmId}` : ""}.\nData is now isolated to company-specific SQLite file.`;
26
+ },
27
+ };
28
+ //# sourceMappingURL=selectCompany.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selectCompany.js","sourceRoot":"","sources":["../../src/tools/selectCompany.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,CAAC,MAAM,iBAAiB,GAAmB;IAC/C,IAAI,EAAE,yBAAyB;IAC/B,WAAW,EACT,yIAAyI;IAC3I,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,uHAAuH;aACrI;SACF;QACD,QAAQ,EAAE,CAAC,YAAY,CAAC;KACzB;IACD,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,EAAY,EAAE,EAAE;QACpC,MAAM,EAAE,UAAU,EAAE,GAAG,IAA8B,CAAC;QAEtD,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;QACtF,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;QACnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,EAAE,CAAC,IAAI,CAAC,6CAA6C,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,0CAA0C,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACvM,CAAC;QAED,eAAe,CAAC,OAAO,GAAG,UAAU,CAAC;QACrC,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC;QACtC,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3B,OAAO,wBAAwB,IAAI,UAAU,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,gBAAgB,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,0DAA0D,CAAC;IAChK,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { ToolDefinition } from "../types.js";
2
+ export declare const syncToQboTool: ToolDefinition;
3
+ //# sourceMappingURL=syncToQbo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncToQbo.d.ts","sourceRoot":"","sources":["../../src/tools/syncToQbo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAK7C,eAAO,MAAM,aAAa,EAAE,cAkD3B,CAAC"}
@@ -0,0 +1,53 @@
1
+ import { getCurrentCompanyId } from "../lib/db.js";
2
+ import { qboClient } from "../lib/qbo.js";
3
+ export const syncToQboTool = {
4
+ name: "bookmind.sync_to_qbo",
5
+ description: "Push approved categorizations from local SQLite back to QuickBooks Online. Creates journal entries for category reclassifications. Only syncs approved (approved=1) transactions.",
6
+ schema: {
7
+ type: "object",
8
+ properties: {
9
+ transaction_ids: {
10
+ type: "array",
11
+ items: { type: "string" },
12
+ description: "Specific transaction IDs to sync, or ['all'] for all approved uncategorized transactions",
13
+ },
14
+ },
15
+ required: ["transaction_ids"],
16
+ },
17
+ handler: async (args, db) => {
18
+ const coId = getCurrentCompanyId();
19
+ const { transaction_ids } = args;
20
+ const useAll = transaction_ids.length === 1 && transaction_ids[0] === "all";
21
+ if (!qboClient.isAuthenticated())
22
+ return "QBO not connected. Run OAuth flow first.";
23
+ let rows;
24
+ if (useAll) {
25
+ const r = db.exec(`SELECT id, qbo_id, date, amount, description, vendor, category, account_id FROM transactions WHERE company_id='${coId}' AND approved=1 AND status='categorized' LIMIT 200`);
26
+ rows = r[0]?.values || [];
27
+ }
28
+ else {
29
+ const quoted = transaction_ids.map((id) => `'${id.replace(/'/g, "''")}'`).join(",");
30
+ const r = db.exec(`SELECT id, qbo_id, date, amount, description, vendor, category, account_id FROM transactions WHERE company_id='${coId}' AND id IN (${quoted}) AND approved=1`);
31
+ rows = r[0]?.values || [];
32
+ }
33
+ let synced = 0;
34
+ for (const [id, qboId, date, amount, description, vendor, category, accountId] of rows) {
35
+ try {
36
+ if (qboId) {
37
+ await qboClient.updateTransaction(String(qboId), {
38
+ Id: String(qboId),
39
+ TotalAmt: parseFloat(String(amount)) || 0,
40
+ Line: [{ DetailType: "DescriptionOnly", Amount: Math.abs(parseFloat(String(amount))) || 0, Description: `[BookMind] ${String(category)} — ${String(description || "")}` }],
41
+ });
42
+ }
43
+ db.exec(`UPDATE transactions SET status='synced', updated_at=datetime('now') WHERE id='${String(id).replace(/'/g, "''")}'`);
44
+ synced++;
45
+ }
46
+ catch (e) {
47
+ return `Sync error at transaction ${id}: ${e.message || "Unknown error"}. Synced ${synced} before failure.`;
48
+ }
49
+ }
50
+ return `Synced ${synced} transaction(s) to QuickBooks Online.`;
51
+ },
52
+ };
53
+ //# sourceMappingURL=syncToQbo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncToQbo.js","sourceRoot":"","sources":["../../src/tools/syncToQbo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { ToolDefinition } from "../types.js";
2
+ export declare const undoCategoriesTool: ToolDefinition;
3
+ //# sourceMappingURL=undoCategories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undoCategories.d.ts","sourceRoot":"","sources":["../../src/tools/undoCategories.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI7C,eAAO,MAAM,kBAAkB,EAAE,cA6ChC,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { getCurrentCompanyId, auditChange } from "../lib/db.js";
2
+ export const undoCategoriesTool = {
3
+ name: "bookmind.undo_categorize",
4
+ description: "Undo categorization changes on transactions. Restore previous category from audit log. Accepts transaction IDs or 'all' to undo all categorizations from today's session.",
5
+ schema: {
6
+ type: "object",
7
+ properties: {
8
+ transaction_ids: {
9
+ type: "array",
10
+ items: { type: "string" },
11
+ description: "Transaction IDs to undo, or ['all'] to undo all today",
12
+ },
13
+ },
14
+ required: ["transaction_ids"],
15
+ },
16
+ handler: async (args, db) => {
17
+ const coId = getCurrentCompanyId();
18
+ const { transaction_ids } = args;
19
+ const useAll = transaction_ids.length === 1 && transaction_ids[0] === "all";
20
+ const today = new Date().toISOString().split("T")[0];
21
+ let restored = 0;
22
+ if (useAll) {
23
+ const rows = db.exec(`SELECT entity_id, old_value FROM audit_log WHERE company_id='${coId}' AND entity_type='transaction' AND field='category' AND created_at >= '${today}' AND old_value IS NOT NULL ORDER BY created_at DESC`);
24
+ const ids = rows[0]?.values || [];
25
+ const latest = {};
26
+ for (const [entityId, oldVal] of ids) {
27
+ if (!latest[entityId])
28
+ latest[entityId] = String(oldVal);
29
+ }
30
+ for (const [id, oldCategory] of Object.entries(latest)) {
31
+ db.exec(`UPDATE transactions SET category='${String(oldCategory).replace(/'/g, "''")}', status='uncategorized', approved=0, updated_at=datetime('now') WHERE id='${String(id).replace(/'/g, "''")}' AND company_id='${coId}'`);
32
+ auditChange(db, "transaction", id, "category", null, String(oldCategory), "undo");
33
+ restored++;
34
+ }
35
+ }
36
+ else {
37
+ for (const id of transaction_ids) {
38
+ const rows = db.exec(`SELECT old_value FROM audit_log WHERE company_id='${coId}' AND entity_type='transaction' AND entity_id='${String(id).replace(/'/g, "''")}' AND field='category' AND old_value IS NOT NULL ORDER BY created_at DESC LIMIT 1`);
39
+ const oldCat = rows[0]?.values?.[0]?.[0];
40
+ if (oldCat) {
41
+ db.exec(`UPDATE transactions SET category='${String(oldCat).replace(/'/g, "''")}', status='uncategorized', approved=0, updated_at=datetime('now') WHERE id='${id.replace(/'/g, "''")}' AND company_id='${coId}'`);
42
+ auditChange(db, "transaction", id, "category", null, String(oldCat), "undo");
43
+ restored++;
44
+ }
45
+ }
46
+ }
47
+ return `Restored ${restored} transaction(s) to previous categories.`;
48
+ },
49
+ };
50
+ //# sourceMappingURL=undoCategories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"undoCategories.js","sourceRoot":"","sources":["../../src/tools/undoCategories.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { ToolDefinition } from "../types.js";
2
+ export declare const vendorSearchTool: ToolDefinition;
3
+ //# sourceMappingURL=vendorSearch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vendorSearch.d.ts","sourceRoot":"","sources":["../../src/tools/vendorSearch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,eAAO,MAAM,gBAAgB,EAAE,cA4B9B,CAAC"}