| name | pm-cli-harness |
| description | CLI for Property Meld work order management. snapcli (pm) is the primary tool for all PM operations — use it first. Nexus API is secondary (reads + maintenance_notes). Manual UI is last resort only if snapcli itself is broken. |
| effort | low |
| triggers | ["pm work-orders","pm assign-tech","meld triage"] |
Property Meld CLI
Tool Hierarchy — Follow This Order
- snapcli (
pm) — primary tool for ALL PM operations. Plain HTTP with captured session cookies. No Playwright, no browser. Use this first.
- Nexus API (
pm-read-melds.py, OAuth2 client credentials) — secondary. Good for bulk meld reads and maintenance_notes writes. Blocked on assignment/merge/chat write operations.
- Manual PM UI — last resort only if snapcli is broken (expired cookies, site layout change). Do NOT fall back here just because Nexus API returns 404.
Setup
pip install -e ~/projects/cli-anything-propertymeld/
Session Capture — When Cookies Expire
Property Meld uses Google Sign-In via Safari. When pm returns 401/403 or the session is otherwise stale, recapture from Safari's binary cookie store. Do NOT use SafariDriver/Selenium/Playwright login automation — Google's sign-in flow breaks under automation.
Method: snapcli.capture.safari parses Safari's binary cookie file directly (no automation, no login flow).
import os
import sys
sys.path.insert(0, os.environ.get('SNAPCLI_PATH', ''))
from snapcli.capture.safari import capture
from pathlib import Path
capture(
"propertymeld.com",
str(Path.home() / ".claude/credentials/property-meld.json"),
cookie_file=str(Path.home() / "Library/Containers/com.apple.Safari/Data/Library/Cookies/Cookies.binarycookies"),
)
Prerequisites:
- David must already be logged into propertymeld.com in Safari (the parser only reads, it does not log in)
- Safari cookie store path:
~/Library/Containers/com.apple.Safari/Data/Library/Cookies/Cookies.binarycookies
- Output: ~14 cookies including the
sessionid cookie
Verify after capture:
pm work-orders list --status open --limit 1 --json
If the parser returns "0 cookies for propertymeld.com", David needs to log in to PM in Safari first, then re-run the capture.
Commands
Work Orders
pm work-orders list --status open --json
pm work-orders list --status pending --json
pm work-orders list --limit 50 --json
pm work-orders get <meld_id> --json
pm work-orders comments <meld_id> --json
pm work-orders send-message <meld_id> "<text>" --json
pm work-orders clone --meld-id <meld_id> --json
pm work-orders merge --meld-id <src> --into <dst> --json
pm work-orders complete --meld-id <id> --json
pm work-orders complete --meld-id <id> --notes "text" --json
pm work-orders cancel --meld-id <id> --json
pm work-orders cancel --meld-id <id> --reason "text" --json
pm work-orders schedule --meld-id <id> --dtstart 2026-04-27T14:00:00-04:00 --hours 2 --json
pm work-orders schedule-vendor --meld-id <id> --vendor-id <vid> --dtstart 2026-04-27T14:00:00-04:00 --hours 2 --json
pm work-orders assign-vendor --meld-id <id> --vendor-id <id> --json
Tenants
pm tenants list --json
pm tenants list --search "Christy" --json
pm tenants list --search "(423) 400" --json
pm tenants get <tenant_id> --json
Properties & Vendors
pm properties list --json
pm vendors list --json
Tech Assignment (in-house)
pm assign-tech --work-order-id <id> --tech Alex --json
Vendor Assignment
pm assign-vendor --work-order-id 12345 --vendor Rogers --json
pm assign-vendor --work-order-id 12345 --vendor "Example Electric" --json
pm work-orders assign-vendor --meld-id 12345 --vendor-id 92504 --json
Result: status changes to PENDING_VENDOR. Vendor name uses partial match against pm vendors list.
External Vendor Assignment
pm work-orders assign-vendor --meld-id <id> --vendor-id <id> --json
pm work-orders assign-vendor --meld-id 12345 --vendor-id 67890 --json
pm work-orders assign-vendor --meld-id 12345 --vendor-id 67890 --account 2 --json
Result: status changes to PENDING_VENDOR with vendor_assignment_request
Projects
pm projects list --json
pm projects list --meld-id 12345 --json
pm projects get <project_id> --json
pm projects create --name "Electrical Rewire" --description "Kitchen and bathrooms" --json
pm projects update <project_id> --name "..." --status active|archived --json
pm projects delete <project_id> --json
Invoices
pm invoices list --json
pm invoices list --meld-id 12345 --json
pm invoices list --status issued --json
pm invoices get <invoice_id> --json
pm invoices create --meld-id 12345 --invoice-number "INV-001" --amount 1500.00 --description "Labor and materials" --json
pm invoices create --meld-id 12345 --invoice-number "INV-001" --amount 1500.00 --due-date 2026-05-15 --json
pm invoices update <invoice_id> --status issued --json
pm invoices link <invoice_id> --meld-id 12345 --json
Receipts
pm receipts list --json
pm receipts list --meld-id 12345 --json
pm receipts get <receipt_id> --json
pm receipts upload --meld-id 12345 --file /path/to/receipt.pdf --json
pm receipts upload --meld-id 12345 --file receipt.jpg --description "Material invoice" --json
pm receipts upload --meld-id 12345 --file receipt.pdf --invoice-id 678 --json
pm receipts link <receipt_id> --invoice-id 678 --json
Vendor Invitations
pm vendor-invites send --meld-id 12345 --vendor-id 999 --json
pm vendor-invites send --meld-id 12345 --vendor-id 999 --message "Please quote for full bathroom remodel" --json
pm vendor-invites list --json
pm vendor-invites list --meld-id 12345 --json
pm vendor-invites list --status pending --json
pm vendor-invites get <invitation_id> --json
pm vendor-invites resend <invitation_id> --json
pm vendor-invites cancel <invitation_id> --json
Health Check
pm probe --json
Backend Notes
| Command | Backend | Auth |
|---|
| work-orders list/get/comments/send-message | snapcli (plain HTTP) | PM_CREDS_PATH cookies |
| work-orders clone/merge/complete/cancel | snapcli (plain HTTP) | PM_CREDS_PATH cookies |
| work-orders schedule/assign-vendor | snapcli (plain HTTP) | PM_CREDS_PATH cookies |
| assign-tech (in-house) | snapcli (plain HTTP) | PM_CREDS_PATH cookies |
| tenants list/get | snapcli (plain HTTP) | PM_CREDS_PATH cookies |
| projects list/get/create/update/delete | snapcli (plain HTTP) | PM_CREDS_PATH cookies |
| invoices list/get/create/update/link | snapcli (plain HTTP) | PM_CREDS_PATH cookies |
| receipts list/get/upload/link | snapcli (plain HTTP) | PM_CREDS_PATH cookies |
| vendor-invites send/list/get/resend/cancel | snapcli (plain HTTP) | PM_CREDS_PATH cookies |
| properties/vendors list | Nexus API | PM_CLIENT_ID/SECRET |
| maintenance_notes PATCH | Nexus API | PM_CLIENT_ID/SECRET |
Notes
complete requires meld to be in PENDING_COMPLETION status. Applies to work by any in-house tech (Alex, Casey, Jordan, or any future in-house assignment).
merge requires both melds to be at the same unit. Source meld gets MANAGER_CANCELED with "(Merged)" prefix.
tenants list --search does client-side filtering (server does not support name/email query params).
Known Gaps (no API or snapcli path)
- Chat message deletion/editing — browser UI only
- Tenant create/update — tenants/{id} PUT is supported by server but not yet wired as a CLI command