Install
openclaw skills install @voronindenis5/job-offer-comparatorUse when comparing two or more job offers, deciding between a remote and on-site role, weighing a higher salary against a long commute, moving cities for a job, pricing the real value of benefits, or preparing a salary negotiation counter-offer. Computes true total compensation — base + expected bonus + capped retirement match + risk-discounted equity − health premiums − commute cost (km + parking) − cost-of-living adjustment — then effective hourly rate on REAL hours (contracted + overtime + commute), PTO valuation, and the exact break-even base salary the losing offer needs to match the winner. Outputs a negotiation-ready target number.
openclaw skills install @voronindenis5/job-offer-comparatorA $115k on-site offer can lose to a $95k remote one. Salary is the number companies make easiest to see; everything that actually decides your life — commute hours, health premiums, retirement match caps, equity that may never liquify, and the cost of living in the offer's city — hides in attachments and footnotes.
This skill makes offers comparable by computing, per offer: true total compensation (all cash + benefits − deductions, COL-adjusted), effective hourly rate on real hours (including commute), the $ value of PTO, and the break-even base salary — the exact number to say in a negotiation: "I'd need $X base to say yes."
Four commands in scripts/offer_compare.py:
compare — side-by-side table: raw comp lines → deductions →
risk-adjusted → COL-adjusted true comp; then derived metrics (real weekly
hours, effective hourly, PTO value) and a plain-language verdict,
including the marginal hourly rate of the extra hours the money-rich offer
demands. --json for agents.breakeven — for exactly two offers: the base salary the loser needs
to match the winner's true comp (holding its own bonus/match/deductions
constant) — your negotiation target. Also shows the reverse: how far the
winner could drop and still win.annotate — field-by-field guide: typical values and where to find
each number in an offer letter/benefits PDF.example — a filled sample offers.json to copy.breakeven gives the number)Don't use for: tax advice (gross-of-tax model), pre-IPO equity valuation (use a 409A/secondary-price approach), or deciding between job families — it prices offers, it doesn't rank careers.
# 0. Get a filled sample and edit it
python3 scripts/offer_compare.py example > offers.json
# 1. Full comparison (remote $95k vs big-city $115k)
python3 scripts/offer_compare.py compare --file offers.json
# 2. Negotiation target: what base makes the loser match the winner?
python3 scripts/offer_compare.py breakeven --file offers.json
# 3. Inline, no file — two quick offers
python3 scripts/offer_compare.py compare \
--offer '{"name":"Stay","base":90000,"commute_km_each_way":25,"commute_days_per_week":4}' \
--offer '{"name":"Go","base":104000,"commute_km_each_way":0,"col_index":108}'
# 4. Where does each number come from?
python3 scripts/offer_compare.py annotate
# 5. Machine-readable
python3 scripts/offer_compare.py compare --file offers.json --json
gross = base + base×bonus_pct + min(base×match_pct, match_cap)
+ equity_value×(1−equity_risk) + other_benefits×12 + relocation
risk_adj = gross − health_premium×12 − commute_cost
commute_cost = km×2×days×52×cost_per_km + parking×12
TRUE COMP = risk_adj ÷ (col_index/100)
real hours/wk = hours_per_week + overtime + 2×days×(km ÷ 28 km/h)
effective hourly = true comp ÷ (52 × real hours/wk)
PTO value = pto_days × (true comp ÷ 260)
Every assumption is printed above the table, never hidden: 28 km/h
door-to-door commute speed, 52 working weeks / 260 working days, equity risk
default 0.50 for illiquid private-company grants. The retirement match cap
is honored (min(base×pct, cap)) — caps bind more often than people think
once base passes ~$80–100k. Taxes are deliberately out of scope: they shift
with filing status and jurisdiction; the model compares offers, not tax
strategies.
Breakeven solves by bisection on base (handles the match-cap kink where a closed-form would break): find base B such that the losing offer's true comp at B equals the winner's true comp.
| Field | Typical | Where to find it |
|---|---|---|
base | 60k–250k | offer letter headline number |
bonus_pct | 0–0.20 | "annual bonus target, up to X%" — enter the expected value |
equity_annual_value | 0–100k+/yr | annual grant ÷ vest years (RSU $ value/yr) |
equity_risk | 0.0–0.9 | 0–0.2 public RSUs; 0.5 private; 0.9+ early startup |
retirement_match_pct | 0–0.06 | benefits PDF, "401(k) match" |
retirement_match_cap | 0–15k | the small print — often caps the match |
health_premium_monthly | 0–800 | employee share of premiums (paycheck deduction) |
pto_days + holidays | 10–30 / 8–12 | PTO policy page |
hours_per_week + overtime_hours_per_week | 40 / 0–15 | ask the team, trust Glassdoor norm |
commute_km_each_way + commute_days_per_week | 0–60 / 0–5 | map home↔office |
commute_cost_per_km | 0.05–0.50 | default 0.30 (all-in car); transit fare÷km |
monthly_parking_or_transit | 0–400 | parking spot / transit pass price |
col_index | 85–130 | Numbeo/BEA index vs your baseline = 100 |
relocation_bonus | 0–20k | one-time; flagged, first-year only |
example, use annotate for
any unclear field).compare — read the verdict block first, then the table.breakeven — get the counter-offer number for the losing side.col_index before reading the verdict.* where the cap binds.example > offers.json then compare --file runs and prints
assumptions + table + verdictbreakeven prints a target base, and its verification line
("at that base … == target ✓") holdscol_index from 100 → 115 visibly reduces true compretirement_match_cap below base×match% shows the *
capped flagcompare --json output parses and rows include true_comp and
effective_hourlypython3 scripts/test_offer_compare.py → ALL TESTS PASSEDRemote vs office, same city:
python3 scripts/offer_compare.py compare \
--offer '{"name":"Remote","base":95000,"commute_km_each_way":0,"commute_days_per_week":0,"health_premium_monthly":150,"retirement_match_pct":0.04,"retirement_match_cap":10000,"equity_annual_value":12000}' \
--offer '{"name":"Office","base":115000,"commute_km_each_way":30,"commute_days_per_week":5,"monthly_parking_or_transit":250,"health_premium_monthly":600,"bonus_pct":0.15}'
Same job, different cities (COL):
python3 scripts/offer_compare.py compare \
--offer '{"name":"Denver","base":105000,"col_index":100}' \
--offer '{"name":"SF","base":140000,"col_index":165,"commute_km_each_way":40,"commute_days_per_week":3}'
Contractor day-rate vs permanent salary: model the contract as gross = day-rate × billable days (e.g. $650×220 = $143k), zero match/equity/PTO, full health premium, and compare effective hourlies.
python3 scripts/offer_compare.py compare \
--offer '{"name":"Contract","base":143000,"bonus_pct":0,"retirement_match_pct":0,"health_premium_monthly":650,"pto_days":0,"hours_per_week":45}' \
--offer '{"name":"Staff","base":118000,"bonus_pct":0.08,"retirement_match_pct":0.05,"retirement_match_cap":9000,"health_premium_monthly":180,"pto_days":24}'
Decision support, not financial advice. Gross-of-tax model; verify offer terms against the actual documents before negotiating.