Skip to content
SlashClone
Made by@ledgerly

Expense categorizer

@ledgerly/expense-categorizer

Categorize a bank or card CSV export with a rules file you can edit. Writes a categorized CSV, spend by category and by month, and a review list of unknown merchants with suggested rules. Offline and repeatable.

SkillFinancev1.0.0 (latest)Tested
Activity
New: be the first to try it
  1. 1{
  2. 2 "name": "expense-categorizer",
  3. 3 "owner": "ledgerly",
  4. 4 "version": "1.0.0",
  5. 5 "type": "skill",
  6. 6 "description": "Categorize a bank or card CSV export with a rules file you can edit. Writes a categorized CSV, spend by category and by month, and a review list of unknown merchants with suggested rules. Offline and repeatable.",
  7. 7 "category": "finance",
  8. 8 "license": "MIT",
  9. 9 "compat": ["claude-code"],
  10. 10 "permissions": {
  11. 11 "shell": ["node scripts/categorize.mjs"],
  12. 12 "files": { "read": ["./data/**"], "write": ["./data/expense-rules.csv", "./out/expenses*"] }
  13. 13 },
  14. 14 "tests": {
  15. 15 "cases": [
  16. 16 {
  17. 17 "name": "categorizes the example bank export and lists unknowns",
  18. 18 "inputs": {},
  19. 19 "checks": [
  20. 20 { "type": "command_exits_zero", "command": "node scripts/categorize.mjs examples/transactions.csv --rules examples/rules.csv --out out/expenses" },
  21. 21 { "type": "file_exists", "path": "out/expenses-categorized.csv" },
  22. 22 { "type": "file_contains", "path": "out/expenses-summary.md", "text": "| Payroll | $246,701 | 85.5% |" },
  23. 23 { "type": "file_contains", "path": "out/expenses-unknowns.csv", "text": "BLUE BOTTLE COFFEE" },
  24. 24 {
  25. 25 "type": "output_matches_schema",
  26. 26 "path": "out/expenses-summary.json",
  27. 27 "schema": {
  28. 28 "type": "object",
  29. 29 "required": ["transactions", "by_category", "unknown_merchants"],
  30. 30 "properties": {
  31. 31 "transactions": { "const": 38 },
  32. 32 "spend_sign": { "const": "negative" },
  33. 33 "outflow_total": { "const": 288702.99 },
  34. 34 "unknown_merchants": { "const": 4 },
  35. 35 "by_category": { "type": "object", "properties": { "Payroll": { "const": 246701.16 }, "Travel": { "const": 722.5 } } }
  36. 36 }
  37. 37 }
  38. 38 }
  39. 39 ]
  40. 40 },
  41. 41 {
  42. 42 "name": "handles debit and credit columns, regex rules, and first match wins",
  43. 43 "inputs": {
  44. 44 "data/card.csv": "Posted Date,Payee,Debit,Credit\n10/02/2026,FIGMA ANNUAL,900.00,\n10/03/2026,Payment received,,1500.00\n10/04/2026,FIGJAM WORKSHOP,50.00,\n",
  45. 45 "data/rules.csv": "pattern,category,match\nfigma annual,Software (annual),contains\n^fig,Design,regex\n"
  46. 46 },
  47. 47 "checks": [
  48. 48 { "type": "command_exits_zero", "command": "node scripts/categorize.mjs data/card.csv --rules data/rules.csv --out out/expenses" },
  49. 49 {
  50. 50 "type": "output_matches_schema",
  51. 51 "path": "out/expenses-summary.json",
  52. 52 "schema": {
  53. 53 "type": "object",
  54. 54 "properties": {
  55. 55 "outflow_total": { "const": 950 },
  56. 56 "inflow_total": { "const": 1500 },
  57. 57 "by_category": { "type": "object", "properties": { "Software (annual)": { "const": 900 }, "Design": { "const": 50 } } },
  58. 58 "unknown_merchants": { "const": 1 }
  59. 59 }
  60. 60 }
  61. 61 }
  62. 62 ]
  63. 63 }
  64. 64 ]
  65. 65 }