@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.
- package/.claude-plugin/marketplace.json +27 -0
- package/.claude-plugin/plugin.json +18 -0
- package/.env.example +15 -0
- package/.github/workflows/validate.yml +55 -0
- package/CEO_QUICKSTART.md +161 -0
- package/CLAUDE.md +30 -0
- package/CNAME +1 -0
- package/LAUNCH_CONTENT.md +210 -0
- package/LICENSE +21 -0
- package/README.md +196 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +283 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/db.d.ts +6 -0
- package/dist/lib/db.d.ts.map +1 -0
- package/dist/lib/db.js +209 -0
- package/dist/lib/db.js.map +1 -0
- package/dist/lib/llm.d.ts +14 -0
- package/dist/lib/llm.d.ts.map +1 -0
- package/dist/lib/llm.js +110 -0
- package/dist/lib/llm.js.map +1 -0
- package/dist/lib/qbo.d.ts +35 -0
- package/dist/lib/qbo.d.ts.map +1 -0
- package/dist/lib/qbo.js +157 -0
- package/dist/lib/qbo.js.map +1 -0
- package/dist/tools/anomalies.d.ts +3 -0
- package/dist/tools/anomalies.d.ts.map +1 -0
- package/dist/tools/anomalies.js +142 -0
- package/dist/tools/anomalies.js.map +1 -0
- package/dist/tools/approveCategories.d.ts +3 -0
- package/dist/tools/approveCategories.d.ts.map +1 -0
- package/dist/tools/approveCategories.js +41 -0
- package/dist/tools/approveCategories.js.map +1 -0
- package/dist/tools/categorize.d.ts +3 -0
- package/dist/tools/categorize.d.ts.map +1 -0
- package/dist/tools/categorize.js +160 -0
- package/dist/tools/categorize.js.map +1 -0
- package/dist/tools/chaseEmail.d.ts +3 -0
- package/dist/tools/chaseEmail.d.ts.map +1 -0
- package/dist/tools/chaseEmail.js +29 -0
- package/dist/tools/chaseEmail.js.map +1 -0
- package/dist/tools/fetchTransactions.d.ts +3 -0
- package/dist/tools/fetchTransactions.d.ts.map +1 -0
- package/dist/tools/fetchTransactions.js +72 -0
- package/dist/tools/fetchTransactions.js.map +1 -0
- package/dist/tools/learnRules.d.ts +3 -0
- package/dist/tools/learnRules.d.ts.map +1 -0
- package/dist/tools/learnRules.js +72 -0
- package/dist/tools/learnRules.js.map +1 -0
- package/dist/tools/listCompanies.d.ts +3 -0
- package/dist/tools/listCompanies.d.ts.map +1 -0
- package/dist/tools/listCompanies.js +26 -0
- package/dist/tools/listCompanies.js.map +1 -0
- package/dist/tools/monthlyClose.d.ts +3 -0
- package/dist/tools/monthlyClose.d.ts.map +1 -0
- package/dist/tools/monthlyClose.js +92 -0
- package/dist/tools/monthlyClose.js.map +1 -0
- package/dist/tools/reconcile.d.ts +3 -0
- package/dist/tools/reconcile.d.ts.map +1 -0
- package/dist/tools/reconcile.js +86 -0
- package/dist/tools/reconcile.js.map +1 -0
- package/dist/tools/rejectCategories.d.ts +3 -0
- package/dist/tools/rejectCategories.d.ts.map +1 -0
- package/dist/tools/rejectCategories.js +36 -0
- package/dist/tools/rejectCategories.js.map +1 -0
- package/dist/tools/selectCompany.d.ts +3 -0
- package/dist/tools/selectCompany.d.ts.map +1 -0
- package/dist/tools/selectCompany.js +28 -0
- package/dist/tools/selectCompany.js.map +1 -0
- package/dist/tools/syncToQbo.d.ts +3 -0
- package/dist/tools/syncToQbo.d.ts.map +1 -0
- package/dist/tools/syncToQbo.js +53 -0
- package/dist/tools/syncToQbo.js.map +1 -0
- package/dist/tools/undoCategories.d.ts +3 -0
- package/dist/tools/undoCategories.d.ts.map +1 -0
- package/dist/tools/undoCategories.js +50 -0
- package/dist/tools/undoCategories.js.map +1 -0
- package/dist/tools/vendorSearch.d.ts +3 -0
- package/dist/tools/vendorSearch.d.ts.map +1 -0
- package/dist/tools/vendorSearch.js +39 -0
- package/dist/tools/vendorSearch.js.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/index.html +403 -0
- package/install/claude-desktop.json +14 -0
- package/install/cline.json +16 -0
- package/install/continue.json +14 -0
- package/install/cursor.json +14 -0
- package/install/goose.yaml +10 -0
- package/install/lm-studio.json +14 -0
- package/install/vscode-copilot.json +14 -0
- package/install/windsurf.json +14 -0
- package/mcp-server.toml +30 -0
- package/package.json +49 -0
- package/server.json +136 -0
- package/src/index.ts +313 -0
- package/src/lib/db.ts +212 -0
- package/src/lib/llm.ts +140 -0
- package/src/lib/qbo.ts +187 -0
- package/src/tools/anomalies.ts +144 -0
- package/src/tools/approveCategories.ts +46 -0
- package/src/tools/categorize.ts +166 -0
- package/src/tools/chaseEmail.ts +30 -0
- package/src/tools/fetchTransactions.ts +79 -0
- package/src/tools/learnRules.ts +71 -0
- package/src/tools/listCompanies.ts +30 -0
- package/src/tools/monthlyClose.ts +95 -0
- package/src/tools/reconcile.ts +86 -0
- package/src/tools/rejectCategories.ts +43 -0
- package/src/tools/selectCompany.ts +33 -0
- package/src/tools/syncToQbo.ts +56 -0
- package/src/tools/undoCategories.ts +50 -0
- package/src/tools/vendorSearch.ts +32 -0
- package/src/types.ts +11 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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":"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"}
|
|
@@ -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 @@
|
|
|
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 @@
|
|
|
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":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,CAAC,MAAM,aAAa,GAAmB;IAC3C,IAAI,EAAE,sBAAsB;IAC5B,WAAW,EACT,mLAAmL;IACrL,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,eAAe,EAAE;gBACf,IAAI,EAAE,OAAO;gBACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACzB,WAAW,EAAE,0FAA0F;aACxG;SACF;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,GAAG,IAAqC,CAAC;QAClE,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC;QAE5E,IAAI,CAAC,SAAS,CAAC,eAAe,EAAE;YAAE,OAAO,0CAA0C,CAAC;QAEpF,IAAI,IAAW,CAAC;QAChB,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,kHAAkH,IAAI,qDAAqD,CAAC,CAAC;YAC/L,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5F,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,kHAAkH,IAAI,gBAAgB,MAAM,kBAAkB,CAAC,CAAC;YAClL,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC;QAC5B,CAAC;QAED,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC;YACvF,IAAI,CAAC;gBACH,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,SAAS,CAAC,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;wBAC/C,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC;wBACjB,QAAQ,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC;wBACzC,IAAI,EAAE,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,cAAc,MAAM,CAAC,QAAQ,CAAC,MAAM,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;qBAC3K,CAAC,CAAC;gBACL,CAAC;gBACD,EAAE,CAAC,IAAI,CAAC,iFAAiF,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;gBAC5H,MAAM,EAAE,CAAC;YACX,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBAChB,OAAO,6BAA6B,EAAE,KAAK,CAAC,CAAC,OAAO,IAAI,eAAe,YAAY,MAAM,kBAAkB,CAAC;YAC9G,CAAC;QACH,CAAC;QACD,OAAO,UAAU,MAAM,uCAAuC,CAAC;IACjE,CAAC;CACF,CAAC"}
|
|
@@ -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 @@
|
|
|
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"}
|