Install
openclaw skills install @himadriganguly/fairsplitSplit group expenses and settle up in the fewest payments.
openclaw skills install @himadriganguly/fairsplitTrack who paid for what in a shared trip, flat, or event, and work out the smallest possible set of payments that settles everyone up. Everything lives in one JSON file per group on disk -- no Splitwise account, no bank linking, no API key, nothing to sign up for. It works just as well for a weekend trip chat as it does for a flatmates' group that has been running for years.
All the actual arithmetic (splitting, currency conversion, and the settlement plan) is done by a tested script, not by the model doing mental math -- money bugs are the kind of bug nobody notices until someone's short a real payment.
Load this skill when the user:
Don't use it for:
terminal tool. Nothing to
pip install -- scripts/ledger.py uses only the standard library.fx command.Run every command through the terminal tool with the bundled script:
python3 ${HERMES_SKILL_DIR}/scripts/ledger.py --data-dir "<fairsplit.data_dir>" <command> [options]
--data-dir is a global flag and goes before the subcommand. The
configured fairsplit.data_dir and fairsplit.default_currency values are
injected into your context when this skill loads, but they are not
passed to the script automatically -- always pass the configured
--data-dir, and pass --currency on init (falling back to
fairsplit.default_currency). Without --data-dir the script uses
$FAIRSPLIT_DATA_DIR, then ~/.hermes/data/fairsplit. A leading ~ is
expanded by the script, so quoting the path is fine.
On Windows, use python instead of python3 if python3 isn't on PATH.
The examples below omit --data-dir for brevity; include it in real calls.
| Command | Purpose |
|---|---|
init | Create a new group with a home currency and members |
add | Record one expense |
add-member | Add a person to an existing group |
balances | Show each member's net position |
settle | Compute the minimum-payment settlement plan |
history | List recorded expenses, optionally filtered |
undo | Delete an expense by id |
export | Write a CSV or Markdown summary to disk |
list-groups | List every group in the data directory |
fx | Best-effort keyless exchange-rate lookup (optional) |
Every command has --help. Full flag reference: references/cli-reference.md.
Ask what the group/trip is called if it isn't obvious from context (a chat title, or something the user already named). Check whether it exists first:
python3 ${HERMES_SKILL_DIR}/scripts/ledger.py list-groups
If it doesn't exist yet, create it. Get the member names and a home
currency (fall back to the configured fairsplit.default_currency if the
user doesn't say -- the script itself defaults to USD, so pass it):
python3 ${HERMES_SKILL_DIR}/scripts/ledger.py init --group "Goa Trip" --currency INR --members "Alice,Bob,Priya"
You don't need every member up front -- add auto-adds anyone new it sees
as a payer or participant, and add-member covers someone joining later
with no expenses yet.
Default to an equal split -- it's what the user means unless they say otherwise:
python3 ${HERMES_SKILL_DIR}/scripts/ledger.py add --group "Goa Trip" \
--payer Alice --amount 1000 --description "Dinner" --split equal
Omit --participants to split across the whole group. For anything other
than a plain even split, ask a specific clarifying question rather than
guessing ("should this be split evenly, or does someone owe a different
share?") and pick the right split type:
--split equal -- default, divides evenly (remainder cents go to
whoever sorts first alphabetically, deterministically)--split exact --detail "Alice:300,Bob:300,Priya:300" -- named amounts
that must add up to the total exactly--split shares --detail "Alice:1,Bob:2,Priya:1" -- proportional
weights (e.g. Bob had two portions)--split percent --detail "Alice:60,Bob:40" -- percentages of the totalReading a receipt photo: if the user shares a photo of a receipt and the
model can see images, read the merchant, line items, tax, and total
directly from the image and propose a split (e.g. itemized, or one add
call per person's items) -- then confirm the numbers with the user before
running add, since a misread digit is a real-money mistake. If the model
can't see images, ask the user to read out the total and any items that
need splitting unevenly.
python3 ${HERMES_SKILL_DIR}/scripts/ledger.py balances --group "Goa Trip"
python3 ${HERMES_SKILL_DIR}/scripts/ledger.py settle --group "Goa Trip"
settle is the useful one to lead with in most replies -- it collapses
everyone's tangled IOUs into the minimum number of actual payments (at most
members - 1), not a naive pairwise list. Report it like "Priya pays Alice
241.66, Priya pays Bob 16.67" rather than dumping raw balances on the user.
python3 ${HERMES_SKILL_DIR}/scripts/ledger.py export --group "Goa Trip" --format md --out /tmp/goa-trip-summary.md
Read the file back and paste the summary into the chat (or hand back the path if the user wants to forward it themselves -- see Pitfalls below for how attachments are and aren't delivered).
ledger.py. Money is stored in
integer cents internally specifically so nothing drifts by a fraction of
a cent across dozens of expenses -- re-deriving numbers by hand
reintroduces exactly that bug.exact split amounts must sum to the total, to the cent. The script
refuses (non-zero exit, clear stderr message) rather than silently
absorbing the difference. If the user's numbers don't add up, say so and
ask which figure is wrong instead of fudging one participant's share.--currency on add requires --fx-rate. The fx
command does a best-effort keyless lookup (Frankfurter/ECB) but needs
outbound network access, which may not be available in every sandbox --
if it fails, just ask the user for the rate (or the converted amount) and
pass --fx-rate directly. Everything downstream (balances, settlement)
always operates in the group's single home currency; the original
amount/currency/rate are kept on the expense record for reference.undo if the group has already been told a
settlement plan based on it.list-groups and ask which one the user means.init --force wipes the group's expense history. Only use it when
the user explicitly wants to reset a group, and confirm first.add rejects zero, negative, NaN, and
non-numeric amounts. There's no "refund" expense type: to reverse an
expense, undo it. To record a settle-up payment (or any money one
person handed back to another), add an expense paid by the person who
sent the money with the recipient as the only participant, e.g. after
"Priya pays Alice 241.66": add --payer Priya --amount 241.66 --participants Alice --description "Settle-up" --category settlement.alice and Alice are two different
people. Reuse the exact spelling init or balances printed.After recording an expense, always show the per-person shares the script printed (it prints them automatically) so the user can catch a mistake immediately, before more expenses stack on top of a wrong split.
Before reporting a settlement plan, sanity-check that it isn't the "warning:
balances do not sum to zero" case balances prints -- that only happens if
the JSON file was hand-edited outside the script, and means the ledger
needs manual repair before the settlement plan can be trusted.
See references/cli-reference.md for the full flag reference (including
history filters and export formats) and references/settlement-algorithm.md
for how the minimum-payment settlement plan is computed and why storage
uses integer minor units. For a recurring split (e.g. monthly rent), wire a
cron job that runs an add command on a schedule -- see the "Recurring
splits" section of the CLI reference for a ready-to-use example.
examples/stress_test.py is a stdlib-only regression test: it generates
200 random expenses across 5 people and asserts balances sum to exactly
zero and settlement never exceeds members - 1 payments. Run it after
changing anything in scripts/ledger.py:
python3 ${HERMES_SKILL_DIR}/examples/stress_test.py