Local-first Node.js CLIs that streamline common family-law workflows. These tools help you quickly analyze communications, generate calendars, compute property/equity figures, and now parse paychecks — all on your machine for privacy and speed. Outputs favor simple formats (CSV/Markdown/JSON) that drop easily into exhibits or spreadsheets.
What’s included:
- Messages and evidence prep
- OFW Messages PDF → JSON/CSV summaries with weekly stats and console Markdown
- iMessage text export → per-year JSON with sentiment metrics
- Scheduling and analysis
- Visitation calendar helper with court-style week logic and annotated grids
- Fifth-week analyzer to quantify months with “5th” occurrences of anchor weekdays
- Property and finance calculators
- Moore/Marsden worksheet and apportionment/buyout with credits (Watts/Epstein/fees)
- DissoMaster spousal support calculator with tax calculations and duration guidelines
- Payroll parsing
- Paylocity paycheck PDFs → single CSV (one row per paycheck) with robust field extraction
Design principles:
- Fast, private, and offline by default (no external services)
- Vanilla JavaScript, clear CLIs, and predictable file outputs
- Small, testable modules with focused responsibility
- Requirements: Node.js (use your local
nvmsetup)- Optional (for LLM sentiment post-processing): Ollama installed and running locally
- Pull model once:
ollama pull llama3.1 - Run server:
ollama serve
- Pull model once:
- Optional (for LLM sentiment post-processing): Ollama installed and running locally
- Install:
cd ~/projects/ofw-tools source ~/.nvm/nvm.sh && nvm use npm install
Pass arguments after --.
- Analyze OFW PDF:
npm run ofw:analyze -- /absolute/path/to/OFW_Messages_Report.pdf - Analyze OFW PDF + LLM sentiment (Ollama):
npm run ofw:analyze-ollama -- /absolute/path/to/OFW_Messages_Report.pdf - Rapid-fire clusters from JSON:
npm run ofw:clusters -- /absolute/path/to/OFW_Messages_Report.json - Visitation calendar (YYYY MM):
npm run visitation -- 2024 4 - Fifth-week counter (built-in examples):
npm run nth-week - Moore/Marsden calculation (example values):
npm run moore-marsden - Apportionment & buyout calculator (example values):
npm run apportionment - DissoMaster spousal support calculator:
npm run dissomaster - iMessage parser with sentiment:
npm run imessage -- /absolute/path/to/imessage.txt - Paylocity paychecks → CSV:
npm run paylocity -- /absolute/path/to/folder/of/pdfs- Defaults: writes
./output/paychecks.csvand./output/paychecks_monthly.csvunless--outis provided. - Tips:
--debug-textwrites normalized text to<source>/_debug_text/;--use-txtreads.txtfiles for testing.
- Defaults: writes
- Purpose: Parse an "Our Family Wizard" Messages PDF and compute weekly stats per person: messages sent/read, average read time, total words, and sentiment. Outputs JSON, Markdown to console, and CSV.
- Input: Path to an OFW messages PDF (export with full pages per message). Supports both legacy (metadata after body) and new (metadata at head; values on next line) formats.
- Output:
same-directory/<basename>.json(parsed messages)same-directory/<basename>.csv(weekly stats)- Markdown tables printed to console
- Run:
npm run ofw:analyze -- /absolute/path/to/OFW_Messages_Report_2025-03-04.pdf
- Options:
--no-markdown: Skip writing per-message Markdown file--no-csv: Skip writing weekly CSV--exclude <csv>: Hide names containing any of the given substrings (case-insensitive) in printed tables--ollama: After JSON is written, perform LLM-based sentiment post-processing (requires local Ollama)--ollama-max <n>: Limit how many messages are sent to the LLM (default: 6)
- Display:
- “To:” pseudo-rows (recipient read-time buckets) are hidden in tables but still used for read-time stats.
- Avg sentiment prints 0.00 when no messages were sent for that row/week.
- Page banners and footers are excluded from message bodies.
- Purpose: Enhance sentiment with thread context and detect high-conflict/deceptive language indicators.
- Requirements: Local Ollama with model
llama3.1downloaded and server running.ollama pull llama3.1 ollama serve
- Run:
# Default cap (6 messages sent to LLM) npm run ofw:analyze-ollama -- /absolute/path/to/OFW_Messages_Report.pdf # Override cap node ofw.js /absolute/path/to/OFW_Messages_Report.pdf --ollama --ollama-max 24
- Outputs (written to
./output/alongside the original JSON):<report> - LLM processed.json— same messages with an addedsentiment_ollamafield per message<report> - summary.md— per-thread list of messages and detected indicators
sentiment_ollamaschema (compact JSON):If the model returns non-JSON, the tool attempts to extract JSON; otherwise it stores a minimal object with a{ "sentiment": "positive|neutral|negative|mixed", "conflict_level": "low|medium|high", "deception_risk": "low|medium|high", "flags": [ "insult","threat","gaslighting","darvo","blame-shift","minimization", "legal-threat","coercion","manipulation","profanity","boundary-violation", "inconsistency","false-allegation","exaggerated-absolutes" ], "reason": "Short justification" }rawfield.- Bias testing: The prompt includes baseline heuristic scores (
sentiment,sentiment_natural,tone, and per‑word variants) for the current and prior context messages. This enables observing any systematic drift/bias when the model sees these priors versus when it does not (toggle by editingollama-sentiment.js).
- Purpose: From the JSON produced by the OFW PDF Analyzer, find clusters of back-to-back messages within a time threshold (default 30 minutes) for a given sender.
- Defaults: Looks for sender "José Hernandez" and prints clusters of 3+ messages.
- Input: Path to the JSON file (e.g.,
OFW_Messages_Report_2025-03-04_12-04-15.json). - Output: Console summary lines and a compact ASCII visualization.
- Run:
npm run ofw:clusters -- /absolute/path/to/OFW_Messages_Report_2025-03-04_12-04-15.json
- Flags:
--sender "Name"(default: "José Hernandez")--threshold-min <minutes>(default: 30)--min-messages <n>(default: 3)
- Purpose: Court-style month view where Week 1 is the first calendar week (Sun–Sat) containing the anchor weekday (default: Friday). Labels Wednesday activities (1st/3rd Visit, 2nd/4th Zoom) and weekend visits (2nd/4th).
- Input: Year and month (numeric).
- Output: Console list of week ranges with Wednesday/Weekend details; optional ASCII calendar grid with annotations (V: visit, Z: zoom).
- Run:
npm run visitation -- 2024 4 npm run visitation -- 2024 4 -- --anchor Saturday # anchor Week 1 on Saturdays npm run visitation -- 2024 4 -- --grid # include annotated calendar grid
- Purpose: Quantify how often a month has a “5th week” under common court-style definitions.
- Definition used: Week 1 is the first week that contains the anchor day(s) (e.g., Friday/Saturday). A month has a “5th week” if it contains 5 such anchor days in that month.
- Defaults: Counts Friday (5) and Saturday (6) for current year through current year + 18. Prints all dates and a per-year summary by default.
- Run:
npm run nth-week
- Options (optional; pass flags after
--with npm):--start <year>and--end <year>: Year range (inclusive). Defaults: start = current year; end = start + 18.--weekday <0-6|csv>: Weekday ordinal(s) (0=Sun … 6=Sat)--anchor <name[,name]>: Named weekday(s), e.g.FridayorFriday,Saturday--list: Print each 5th-occurrence date (on by default)--per-year: Show a yearly summary (on by default)
- Examples:
npm run nth-week npm run nth-week -- --start 2024 --end 2026 --anchor Friday npm run nth-week -- --weekday 3 --start 2024 --end 2025
- Purpose: Parse a text export of iMessage conversations, perform sentiment analysis, and emit per-year JSON files.
- Input: Path to a text export (lines grouped as timestamp → sender → content).
- Output:
output/imessage-export-<year>.jsonby default (directory is gitignored). Override with--out-dir. - Run:
npm run imessage -- /absolute/path/to/imessage.txt npm run imessage -- /absolute/path/to/imessage.txt -- --out-dir ./custom_output
- Notes: Uses
sentiment,natural, andpolaritylibraries. Tests mockpolarityfor Jest compatibility.
- Purpose: Compute Separate Property (SP) and Community Property (CP) interests using the classic Moore/Marsden worksheet with show‑your‑work lines and percentages.
- Config: Provide inputs via
source_files/moore-marsden.config.json(gitignored) or pass--config <path>. If no config is provided, neutral defaults are used. - Output: Console worksheet (Lines 1–13) and optional JSON via
--out-jsoncontaining{ inputs, worksheet }.
- Authorities: In re Marriage of Moore (1980) 28 Cal.3d 366; In re Marriage of Marsden (1982) 130 Cal.App.3d 426; Fam. Code §§ 760 (CP presumption), 770 (SP), 2640 (SP reimbursements).
- What this models: Premarital ownership with a single original acquisition loan. Community acquires a pro tanto share of appreciation during marriage based on the community’s principal reduction over the original purchase price (PP).
- What this does not model:
- Joint‑title acquisitions during marriage → typically a §2640 reimbursement regime (no SP slice of appreciation absent transmutation).
- Refinances/HELOCs/capital improvements/transmutations → out of scope for this worksheet and flagged with warnings.
- Community share of appreciation during marriage: ( (\text{CP principal reduction} \div \text{PP}) \times (\text{FMV@Division} - \text{FMV@Marriage}) )
- SP interest: down payment + SP principal + pre‑marital appreciation + SP share of appreciation
- Only principal reduction counts in the ratio; interest, taxes, insurance, and routine maintenance are excluded.
purchasePrice(PP)downPaymentpaymentsWithSeparateFunds— total SP principal reduction (includes premarital SP and any traced SP during marriage)- Optional split SP fields:
spPrincipalPreMarriage,spPrincipalDuringMarriage(if provided, they are summed intopaymentsWithSeparateFundsfor Line 3) paymentsWithCommunityFunds— CP principal reduction during marriagefairMarketAtMarriage(FMV@Marriage)fairMarketAtDivision(FMV@Division)- Optional reconciliation inputs:
originalLoan(L0 implied at purchase),loanAtDivision(L2 at valuation) - Optional context:
acquisitionContext:premaritalOwner(default) |jointTitleDuringMarriage
--summary— print only Lines 12–13 (SP/CP interests)--no-explain— hide explanatory header and citations--out-json <path>— write{ inputs, worksheet }JSON to the given path--context <acquisitionContext>— overrideconfig.acquisitionContext--refi— user hint to print an additional refinance/HELOC scope warning
- Purchase price must be > 0: throws if PP ≤ 0.
- CP proportion clamp 0..1: Moore/Marsden ratio is clamped after computing CP principal ÷ PP to avoid pathological inputs; a separate sanity warning prints if CP principal > PP.
- Negative appreciation during marriage: warns if
FMV@Division < FMV@Marriage; values still compute. - Context warning: if
acquisitionContext === 'jointTitleDuringMarriage', prints that §2640 reimbursement is the proper regime absent a transmutation. - Refi/HELOC scope: warns if
--refiis passed orpurchasePrice < downPaymentis detected. - Loan reconciliation (when
originalLoanandloanAtDivisionare provided):- Warns if provided
originalLoan≠ impliedPP − downPayment. - Warns if equity from worksheet
(downPayment + SP principal + CP principal) + (FMV@Division − PP)does not matchFMV@Division − loanAtDivisionwithin a penny — indicates possible refinance/improvements/context mismatch.
- Warns if provided
{
"purchasePrice": 435000,
"downPayment": 132179.18,
"spPrincipalPreMarriage": 25696.15,
"spPrincipalDuringMarriage": 0,
"paymentsWithCommunityFunds": 9936.09,
"fairMarketAtMarriage": 665000,
"fairMarketAtDivision": 750225.81,
"originalLoan": 302820.82,
"loanAtDivision": 290000,
"acquisitionContext": "premaritalOwner"
}Run:
npm run moore-marsden -- --config ./source_files/moore-marsden.config.json --out-json ./output/moore-marsden.jsonOutput mapping (abbrev):
- Line 7: pre‑marital appreciation = FMV@Marriage − PP
- Line 8: appreciation during marriage = FMV@Division − FMV@Marriage
- Line 9: CP proportion = CP principal ÷ PP
- Line 10: CP share of appreciation = Line 8 × Line 9
- Line 11: SP share of appreciation = Line 8 − Line 10
- Line 12: SP Interest = Down Payment + SP Principal + Line 7 + Line 11
- Line 13: CP Interest = CP Principal + Line 10
Legal note: This worksheet is designed for the Moore/Marsden context (premarital owner, single original loan). Joint‑title acquisitions during marriage are typically handled under Fam. Code §2640 reimbursement rules. Consult counsel for refinances, HELOCs, capital improvements, or transmutation issues.
- Purpose: Compute SP/CP interests using a Moore/Marsden-style allocation tied to the original purchase price (PP), then apply Watts (exclusive-use) and Epstein (post‑separation principal) as separate credit ledgers. Attorney’s fees are not netted into the buyout here.
- Config: Provide a local config at
source_files/apportionment.config.json(gitignored). An example is included atsource_files/apportionment.config.example.json— copy it and edit for your case. - Output: Console breakdown and computed buyout; optionally emit JSON via
--out-json. - Run:
# With defaults (illustrative numbers) npm run apportionment # With your local config and JSON output npm run apportionment -- --config ./source_files/apportionment.config.json --out-json ./output/apportionment.json
- Inputs (JSON):
- Moore/Marsden:
houseValueAtPurchase(PP),appraisedValue(FMV),mortgageAtPurchase(L0),mortgageAtSeparation(L1),principalPaidDuringMarriage(Cp),yourSeparateInterest(Sy),herSeparateInterest(Sh) - Epstein (post‑sep principal):
principalPaidAfterSeparationByYou,principalPaidAfterSeparationByHer - Watts (exclusive use):
fairMonthlyRentalValue,monthsSinceSeparation,occupant('you'|'her') - Watts offsets:
monthlyMortgageInterest,monthlyPropertyTaxes,monthlyInsurance,monthlyNecessaryRepairs - Acquisition context:
acquisitionContextone of:premaritalOwner(default): classic Moore/Marsden allocation; appreciation allocated by PP denominator across CP and traceable SP buckets.jointTitleDuringMarriage: §2640 regime — reimburse traceable SP contributions dollar‑for‑dollar (down payment, SP principal, SP improvements); all appreciation/remainder equity treated as CP absent transmutation.separateTitleDuringMarriage: treat like Moore/Marsden unless you have a written transmutation; fact‑specific.
- Improvements (optional):
cpImpr,spImprYou,spImprHer. For simple use, they’re added to CP/SP buckets. For larger capital improvements, consult counsel about basis adjustments.
- Moore/Marsden:
- Notes:
- CP share of appreciation during marriage is computed as (CP principal reduction ÷ PP) × (FMV − PP). Separate interests get analogous shares using Sy and Sh over PP.
- Post‑separation principal is treated as Epstein reimbursement to the paying spouse; it is not converted into an equity “share.”
- Watts is modeled as ½ FRV × months, net of carrying‑cost offsets paid by the occupant; applied after property interests.
- Data integrity checks: warns if
Cpdiffers fromL0 − L1, throws if PP ≤ 0, and warns if baseline equity doesn’t matchFMV − L2within a penny.
- Input Validation: Comprehensive validation with granular error messages and recovery suggestions
- Dry Run Mode: Use
--dry-runto validate inputs without performing calculations - Enhanced Error Handling: Detailed warnings for edge cases like negative appreciation, refinancing complications, and bucket sum mismatches
- Metadata Output: Results include regime explanation and calculation details
- Edge Case Support: Handles negative appreciation, zero appreciation, and complex scenarios with appropriate warnings
# Dry run validation
npm run apportionment -- --config ./config.json --dry-run
# Skip input validation (not recommended)
npm run apportionment -- --no-validate
# Enhanced help with edge case documentation
npm run apportionment -- --help- Purpose: Comprehensive testing and demo data for the apportionment system, including Moore/Marsden and Family Code §2640 scenarios
- Structure:
mocks/data/: Sample property data files for different legal regimes and edge casesmocks/demos/: Interactive demo scripts and educational walkthroughsmocks/fixtures/: Test data for boundary conditions and performance testingmocks/docs/: Annotated examples and scenario explanations
- Quick Start:
# Interactive regime comparison demo node ./mocks/demos/regime-comparison.js # Step-by-step Moore/Marsden educational walkthrough node ./mocks/demos/step-by-step-walkthrough.js # Test with sample Moore/Marsden data npm run apportionment -- --config ./mocks/data/moore-marsden-basic.json # Test with Family Code §2640 scenario npm run apportionment -- --config ./mocks/data/section-2640-basic.json # Test edge case: negative appreciation npm run apportionment -- --config ./mocks/data/edge-case-negative-appreciation.json --dry-run
- Educational Value: Each mock scenario includes legal citations, factual assumptions, expected results, and limitations
- Legal Context: All scenarios include disclaimers and appropriate case law references
- Purpose: Calculate California guideline spousal support using DissoMaster methodology with comprehensive tax calculations and duration guidelines.
- Features:
- Basic DissoMaster formula: 40% of income gap minus 50% of child support
- Full tax calculations (Federal, CA state, payroll taxes)
- Support duration guidelines based on marriage length
- Payment schedules with step-down provisions
- Input validation and comprehensive disclaimers
- Config: You can supply a local config at
source_files/dissomaster.config.json(gitignored) or pass--config <path>. - Output: Console breakdown with income analysis, support calculation, duration analysis, and optional payment schedule.
- Run:
# With defaults (illustrative numbers) npm run dissomaster # With duration analysis and payment schedule npm run dissomaster -- --duration --schedule # Summary mode only npm run dissomaster -- --summary # With your local config and JSON output npm run dissomaster -- --config ./source_files/dissomaster.config.json --out-json ./output/dissomaster.json
- Important: This calculator provides estimates only for educational purposes. Results should NOT replace certified DissoMaster software or professional legal counsel. Actual support awards are subject to court discretion and many factors not captured in simplified calculations.
- Citations: Family Code §§ 4320 (support factors), 4325 (temporary support), DissoMaster methodology.
- Purpose: Scan a folder of Paylocity paycheck PDFs and produce a single CSV with one row per paycheck. Then, generate a summary CSV with monthly gross analysis, using the 26/12 method.
- Input: Path to a folder containing
.pdfpaystubs exported from Paylocity. - Output:
- Raw data
output/paychecks.csvby default; override with--out <file.csv>. - Monthly analysis filename derived from the raw data CSV: e.g.,
output/paychecks_monthly.csv.
- Raw data
- Run:
npm run paylocity -- /absolute/path/to/paystubs npm run paylocity -- /absolute/path/to/paystubs -- --out /absolute/path/to/output/paychecks.csv npm run paylocity -- /absolute/path/to/paystubs -- --glob 2025 # only files with '2025' in name - Columns:
File, Pay Date, Period Start, Period End, Gross Pay, Net Pay, Total Taxes, Total Deductions, Federal Income Tax, State Income Tax, Social Security, Medicare. - Notes: Parser is resilient to minor label shifts (e.g., "Pay Date" vs. "Check Date"; "Pay Period" range or separate Begin/End). Missing values are left blank.
- Rows that share the same pay date are aggregated before the monthly analysis to avoid duplicate-period entries in the summary.
- Monthly analysis includes a per-period 26/12 projection and a trailing 12-month average computed over a one-year window with small-entry filtering; intended for FL‑150 monthly reporting.
- For guideline child support and income treatment, see Family Code §§ 4055 (guideline formula), 4058 (annual gross income), and 4059 (allowable deductions).
- This tool provides CSVs aligned with common FL‑150 financial disclosure needs; it does not compute support. Combine with your support workflows as appropriate.
Reusable date/time helpers used across tools:
- Constants:
weekdayNames,nameToOrdinal - Month/day:
daysInMonth,getNthOccurrenceDate,getFifthOccurrenceDate - Visitation helpers:
getFirstAnchorOfMonth,getFirstWeekStart - Formatters:
formatDateMMMddYYYY,formatDateMMDDYYYY,formatTimeHHMM - OFW-specific:
getWeekString(Sun–Sat),parseDate(MM/DD/YYYY hh:mm AM/PM),formatDate
Import example:
const { getFifthOccurrenceDate, formatDateMMMddYYYY } = require('./utils/date');- Export OFW Messages as PDF → run
ofw:analyze→ get<basename>.jsonand<basename>.csv. - Optionally analyze rapid-fire clusters with
ofw:clustersusing the JSON from step 1. - Prepare calendar visuals/evidence with
visitationornth-week. - Run
moore-marsdenand/orapportionmentwith your numbers for property division exhibits.
- The OFW parser supports multiple OFW export layouts; if OFW changes formatting again, please open an issue with a sample.
- Sentiment analysis is heuristic and should be treated as supportive, not dispositive.
- Several scripts contain example inputs; update those inline for your case as needed.
This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
This repo is equipped with AI prompt files for enhanced development.
- Location:
.github/prompts/and.github/prompt-snippets/. - Usage: Copy prompts into your AI tool (e.g., GitHub Copilot Chat, Claude, Cursor) or reference them directly.
- Examples: See individual
.mdfiles for task-specific guidance.
You can also use these prompts to guide code generation, reviews, and testing tasks. If you have a generator tool, you may export repo-wide context to a file such as prompts/entire-codebase.md and keep temporary artifacts ignored by Git.
Prompts are governed by PROMPTS_LICENSE.md and may differ from the code license.
This toolkit is for educational and calculation purposes only and does not constitute legal advice. Property division, family law issues, and communication analysis involve complex legal and factual determinations that vary by jurisdiction and individual circumstances. Always consult with a qualified family law attorney before making decisions based on these calculations or analysis. The tools do not account for many factors that may affect legal outcomes, including transmutations, agreements, refinances, improvements, or other legal doctrines.