Install
openclaw skills install simplify-budget-expense-trackerLog, find, update, and delete expenses and income in the Simplify Budget Google Sheet, and answer read-only recurring schedule questions. NEVER use sessions_...
openclaw skills install simplify-budget-expense-trackerCRITICAL EXECUTION RULE: You MUST use the
exectool to run the bash scripts below. Do NOT callsessions_spawn. Do NOT create ACP sessions. These are standalone shell scripts. Resolve script paths relative to this skill directory and run the resulting absolute path withexec.
You are not allowed to answer any question about expenses, income, recurring items, or budget data from memory or conversation history. Ever. Not once.
Every single response must begin by running the appropriate script from the list below. The script output is your only source of truth. Conversation history, prior responses, and your own assumptions are all irrelevant and must be ignored.
| User asks about | Run this script first |
|---|---|
| Today's / a specific date's expenses | find_expenses.sh --date YYYY-MM-DD --limit 20 |
| A specific expense (to find, fix, or delete) | find_expenses.sh "<query>" 10 |
| Monthly spending totals | find_summary.sh --month YYYY-MM |
| Today's / a specific date's income | find_income.sh --date YYYY-MM-DD --limit 20 |
| A specific income entry | find_income.sh "<query>" 10 |
| Recurring items | find_recurring.sh --month YYYY-MM or find_recurring.sh --query "<name>" |
| Logging a new expense | log.sh "<user message>" |
| Logging new income | write_income.sh |
If you have not run the script yet, you have not started your response yet. Run the script. Then respond conversationally based on what it returns.
WRONG (never do this):
"Here are suggested categories: Fuel/Transport, Food & Drink, Medical/Pharmacy. Please confirm."
CORRECT (always do this):
"Here's what I'll log: Shell → Transport 🚙, Baker's Cottage → Groceries 🍎, Klinik → Medical 🩺. Shall I go ahead?"
Rules that must never be broken:
question field from preview_expense.sh verbatimget_categories.sh — the full category list is hardcoded right here:| Formula | Name |
|---|---|
=zategory1 | Housing 🏡 |
=zategory2 | Transport 🚙 |
=zategory3 | Groceries 🍎 |
=zategory4 | Dining Out 🍕 |
=zategory5 | Personal Care ❤️ |
=zategory6 | Shopping 🛍️ |
=zategory7 | Utilities 💡 |
=zategory8 | Fun 🎬 |
=zategory9 | Business 💻️ |
=zategory10 | Other ❓ |
=zategory11 | Donation 🕌 |
=zategory12 | Childcare 🐣 |
=zategory13 | Travel ✈️ |
=zategory14 | Zakat 🌟 |
=zategory15 | Debt Payment 💸 |
=zategory16 | Fitness 💪 |
=zategory17 | Family Support 🏘️ |
=zategory18 | Taxes 💵 |
=zategory19 | Maintenance 🧰 |
=zategory20 | Painting 🎨 |
=zategory21 | TestGround 🤖 |
=zategory22 | Learning 📚 |
=zategory23 | Sports 🏀 |
=zategory24 | Pet 🐶 |
=zategory25 | Gifts 🎁 |
=zategory26 | Special Occasions 🥰 |
=zategory27 | Dress 👚 |
=zategory28 | Hobby 🪂 |
=zategory29 | Insurance 🛡️ |
=zategory30 | Medical 🩺 |
These are the ONLY valid categories. Use the name exactly as shown above (including emoji) when confirming with the user.
Required environment variables:
GOOGLE_SA_FILE — absolute path to the Google service account JSON fileSPREADSHEET_ID — the Simplify Budget spreadsheet IDTRACKER_CURRENCY — the base currency code for the tracker (for example EUR)Optional environment variables:
TRACKER_CURRENCY_SYMBOL — display symbol for the base currency (for example €)If the user says "log a pencil for 10 euro under business" — log it. Do not ask the user to rephrase, reformat, or provide the amount differently. Extract what you need from their message and run the script. This applies to every possible phrasing. The model's job is to parse natural language, not to ask the user to become a CLI.
If a script returns an error, report the actual error message. Never invent an error or silently fail.
TRACKER_CURRENCY is the system of record for stored amounts.TRACKER_CURRENCY."50 MYR" or "12 USD"."10 euro" → --amount "10 euro" ✅"€10" → --amount "€10" ✅"10 euros" → --amount "10 euros" ✅"50 ringgit" → --amount "50 ringgit" ✅"12 dollars" → --amount "12 dollars" ✅"10" → --amount "10" ✅TRACKER_CURRENCY, and store the converted amount in the sheet.[auto-fx] audit line to notes with the original amount, converted amount, rate, and rate date.get_categories.sh during normal operation"Dining Out", "Business", "Transport"). The script resolves it to the correct formula internally. Never construct =zategory{N} yourself.When the user mentions spending money, buying something, paying for something, or asks to log an expense — follow this flow:
Step 1 — Preview:
bash <skill_dir>/scripts/log.sh --preview "<user's message, word-for-word>"
Pass the raw message. Do not reformat. Add --date YYYY-MM-DD if the user names a date, --account "<name>" if they name an account (default is Cash).
The script parses amount, description, and category. It returns JSON with question, category, explicitCategory, and categorySource.
Step 2 — Confirm or write directly:
explicitCategory is true: skip confirmation, go straight to Step 3.categorySource is "builtin" or "learned": relay the question field verbatim and wait for the user's reply.question field (it includes a "best guess" note) and wait.When the user replies:
Step 3 — Write:
bash <skill_dir>/scripts/log.sh "<user's message>" --category "<confirmed category>"
The script writes to the sheet and outputs a REPLY: line. Send everything after REPLY: verbatim. Do not paraphrase.
Examples:
User: i bought a pencil for 10 euro under business category
→ preview detects explicit category → write directly (no confirmation needed)
User: spent 23 on mcdonalds
→ preview suggests Dining Out (builtin match) → ask "Log mcdonalds under Dining Out 🍕?" → on yes, write
User: €12 coffee
→ preview suggests Dining Out → confirm → write
Rules:
DUPLICATE_FOUND:, tell the user and ask if they want to log anyway. If yes, re-run with --skip-duplicate-check.REPLY: lines verbatim — do not paraphrase or confirm from memory.When the user uploads a receipt image or asks you to log a receipt:
description.Examples:
When the user wants to inspect, fix, or delete an expense, resolve it from the sheet first:
bash <skill_dir>/scripts/find_expenses.sh "<query>" 10
description and notes.When the user provides income (amount + name, with optional date/account/source/notes):
Extract from the user's message:
amount — required. If the user mentions a foreign currency, preserve it in the amount string you pass to the script, for example "500 USD" or "1000 MYR". If they give no currency, pass a plain number.name — required. This is the income title, e.g. Salary, BMW Sale, Etoro withdrawaldate — in YYYY-MM-DD format. Default to today if not specified.account — default to Other if not specified.source — default to Other if not specified. Use the user’s wording when it is clear, e.g. Salary, Capital Gains, Remittance, Crypto, MTS.notes — optional supporting context.Write the income:
bash <skill_dir>/scripts/write_income.sh "<amount_or_amount_with_currency>" "<name>" "<YYYY-MM-DD>" "<account>" "<source>" "<notes>"
Confirm to the user in a concise way: "✅ Logged income [name] — [amount] into [account] from [source] on [date]" Include notes only when present.
When the user wants to inspect, fix, or delete income, resolve it from the sheet first:
bash <skill_dir>/scripts/find_income.sh "<query>" 10
name, source, and notes.Triggers: "check my expenses for today", "what are my expenses today", "what did I spend today", "show me today's expenses", "expenses for [date]", "what did I spend on [date]", or any question about spending on a specific day.
Always run find_expenses.sh with a date filter. This is not optional.
bash <skill_dir>/scripts/find_expenses.sh --date <YYYY-MM-DD> --limit 20
[description] — [amount] under [category] ([account])[].find_summary.sh is for month-level questions only. Never use it for today/yesterday/specific-date queries.
Triggers: "what did I spend this month", "what's my income this month", "monthly totals", "how much did I save this month". These are month-level questions with no specific date.
bash <skill_dir>/scripts/find_summary.sh --month YYYY-MM
Rules:
Dontedit is the source of truth for monthly totals.find_summary.sh for today/yesterday or any specific date — that always goes to find_expenses.sh.When the user asks read-only recurring questions like:
what is due this monthwhen is capcut duewhat subscriptions are due nextUse the recurring query script. Do NOT write anything into Expenses or Income.
Examples:
bash <skill_dir>/scripts/find_recurring.sh --month 2026-03
bash <skill_dir>/scripts/find_recurring.sh --query "CapCut" --date 2026-03-28
bash <skill_dir>/scripts/find_recurring.sh --query "CapCut" --mode next --date 2026-03-28
Rules:
Recurring using the same recurrence rules as the existing Apps Script logic.when is X due, prefer --mode next.When the user wants to change or delete a recurring item in the Recurring tab:
find_recurring.sh.Recurring row itself. Never write anything into Expenses or Income for this task.__KEEP__ for unchanged fields and __CLEAR__ for optional end date / notes / source:
bash <skill_dir>/scripts/update_recurring.sh "<recurring_id>" "<YYYY-MM-DD_or___KEEP__>" "<name_or___KEEP__>" "<category_or___KEEP__>" "<expense_or_income_or___KEEP__>" "<Monthly_or_Quarterly_or_Yearly_or___KEEP__>" "<amount_or___KEEP__>" "<account_or___KEEP__>" "<YYYY-MM-DD_or___KEEP___or___CLEAR__>" "<notes_or___KEEP___or___CLEAR__>" "<source_or___KEEP___or___CLEAR__>"
bash <skill_dir>/scripts/delete_recurring.sh "<recurring_id>"
When the user wants to add a recurring expense or recurring income to the Recurring tab:
start_date in YYYY-MM-DDnamecategory must use the live active category list; never invent a categorytype as expense or incomefrequency as Monthly, Quarterly, or Yearlyamountaccount, end_date, notes, sourcebash <skill_dir>/scripts/write_recurring.sh "<YYYY-MM-DD>" "<name>" "<category>" "<expense_or_income>" "<Monthly_or_Quarterly_or_Yearly>" "<amount>" "<account>" "<YYYY-MM-DD_optional_end_date>" "<notes>" "<source>"
Recurring starting from row 6, matching the SB_LIVE hole-reuse behavior.=zategory<stableId> formula — use the Active Categories table above to resolve itIncome 💵When the user wants to change amount, name, date, account, source, or notes for an income row:
find_income.sh. Do NOT trust chat memory as the source of truth.__KEEP__ for unchanged fields and __CLEAR__ to blank notes:
bash <skill_dir>/scripts/update_income.sh "<transaction_id>" "<amount_or_amount_with_currency_or___KEEP__>" "<name_or___KEEP__>" "<YYYY-MM-DD_or___KEEP__>" "<account_or___KEEP__>" "<source_or___KEEP__>" "<notes_or___KEEP___or___CLEAR__>"
If the user asks to undo or delete an income entry:
find_income.sh.bash <skill_dir>/scripts/delete_income.sh "<transaction_id>"
When the user says things like "fix that", "that was wrong", "change the amount", "put that under X instead", "it was 4 not 5", "actually that was yesterday", "actually I bought that on [date]", "wrong date":
find_expenses.sh to resolve the target from the sheet. Do not use chat memory.__KEEP__ for unchanged fields, __CLEAR__ to blank notes. Pass category as plain English name:
bash <skill_dir>/scripts/update_expense.sh --id "<transaction_id>" --amount "<amount_or___KEEP__>" --description "<description_or___KEEP__>" --category "<category_name_or___KEEP__>" --date "<YYYY-MM-DD_or___KEEP__>" --account "<account_or___KEEP__>" --notes "<notes_or___KEEP___or___CLEAR__>"
REPLY: line from the script output word for word.If the user asks to undo or delete an entry:
find_expenses.sh to resolve the target from the sheet.bash <skill_dir>/scripts/delete_expense.sh "<transaction_id>"
REPLY: line from the script output word for word.=zategory{N} yourselfget_categories.sh at runtime — the category table is hardcoded in the scriptsname, account, source, and notes fields.REPLY: lines from scripts verbatim — do not paraphrase or confirm from memoryTRACKER_CURRENCYTRACKER_CURRENCY