feat(skills): add real-estate-assistant skill#62203
feat(skills): add real-estate-assistant skill#62203Adonyth wants to merge 3 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR adds two new skills: Confidence Score: 4/5Not safe to merge as-is — both SKILL.md files expose a personal machine path that will produce broken instructions for every contributor who isn't the author. The hardcoded personal path is a clear violation of the documented policy that docs content must use generic placeholders. All other findings are P2 or lower. skills/real-estate-assistant/SKILL.md line 14 and skills/freelance-tracker/SKILL.md line 16 both need the absolute personal path replaced with a repo-relative path. Prompt To Fix All With AIThis is a comment left during a code review.
Path: skills/real-estate-assistant/SKILL.md
Line: 14
Comment:
**Hardcoded personal machine path in docs**
`/Users/chenjiaxuan/openclaw/skills/real-estate-assistant` is a personal filesystem path. Per the project docs policy, content must use generic placeholders — any other contributor will get a broken `cd` command. The same issue exists on line 16 of `skills/freelance-tracker/SKILL.md`.
```suggestion
cd skills/real-estate-assistant
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: skills/real-estate-assistant/scripts/realestate.py
Line: 212
Comment:
**Unhandled `FileNotFoundError` if listing template is missing**
`TEMPLATE_PATH.read_text()` will raise an unhandled exception if the template is absent (e.g. partial clone, moved assets folder). A small guard gives a much friendlier error.
```suggestion
if not TEMPLATE_PATH.exists():
sys.exit(f"Listing template not found: {TEMPLATE_PATH}")
template = TEMPLATE_PATH.read_text()
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "feat(skills): add real-estate-assistant ..." | Re-trigger Greptile |
| ## Quick Start | ||
| 1. **Add a lead:** | ||
| ```bash | ||
| cd /Users/chenjiaxuan/openclaw/skills/real-estate-assistant |
There was a problem hiding this comment.
Hardcoded personal machine path in docs
/Users/chenjiaxuan/openclaw/skills/real-estate-assistant is a personal filesystem path. Per the project docs policy, content must use generic placeholders — any other contributor will get a broken cd command. The same issue exists on line 16 of skills/freelance-tracker/SKILL.md.
| cd /Users/chenjiaxuan/openclaw/skills/real-estate-assistant | |
| cd skills/real-estate-assistant |
Prompt To Fix With AI
This is a comment left during a code review.
Path: skills/real-estate-assistant/SKILL.md
Line: 14
Comment:
**Hardcoded personal machine path in docs**
`/Users/chenjiaxuan/openclaw/skills/real-estate-assistant` is a personal filesystem path. Per the project docs policy, content must use generic placeholders — any other contributor will get a broken `cd` command. The same issue exists on line 16 of `skills/freelance-tracker/SKILL.md`.
```suggestion
cd skills/real-estate-assistant
```
How can I resolve this? If you propose a fix, please make it concise.| features = args.features or "" | ||
| highlights = "\n".join(f"- {f.strip().capitalize()}" for f in features.split(",") if f.strip()) | ||
|
|
||
| template = TEMPLATE_PATH.read_text() |
There was a problem hiding this comment.
Unhandled
FileNotFoundError if listing template is missing
TEMPLATE_PATH.read_text() will raise an unhandled exception if the template is absent (e.g. partial clone, moved assets folder). A small guard gives a much friendlier error.
| template = TEMPLATE_PATH.read_text() | |
| if not TEMPLATE_PATH.exists(): | |
| sys.exit(f"Listing template not found: {TEMPLATE_PATH}") | |
| template = TEMPLATE_PATH.read_text() |
Prompt To Fix With AI
This is a comment left during a code review.
Path: skills/real-estate-assistant/scripts/realestate.py
Line: 212
Comment:
**Unhandled `FileNotFoundError` if listing template is missing**
`TEMPLATE_PATH.read_text()` will raise an unhandled exception if the template is absent (e.g. partial clone, moved assets folder). A small guard gives a much friendlier error.
```suggestion
if not TEMPLATE_PATH.exists():
sys.exit(f"Listing template not found: {TEMPLATE_PATH}")
template = TEMPLATE_PATH.read_text()
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0f6c6dd617
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| # Save invoice | ||
| out_dir = OUT_DIR / args.client | ||
| out_dir.mkdir(parents=True, exist_ok=True) | ||
| out_path = out_dir / f"invoice_{invoice_date}.md" |
There was a problem hiding this comment.
Save invoices with a unique filename per run
cmd_invoice computes an incrementing invoice_number, but the file is always written to invoice_{invoice_date}.md; generating a second invoice for the same client on the same day overwrites the first file while both batches are marked billed, which causes silent data loss in the exported invoice history.
Useful? React with 👍 / 👎.
|
|
||
| def cmd_init_client(args: argparse.Namespace) -> None: | ||
| client = args.init_client | ||
| client_dir = CLIENTS_DIR / client |
There was a problem hiding this comment.
Validate client slug before building filesystem paths
cmd_init_client uses the raw --init-client value directly in path joins, so values containing traversal segments (for example ../) escape the intended clients/ subtree and create/read files outside the skill’s managed directory structure, which is an arbitrary file-write risk if input is malformed or user-controlled.
Useful? React with 👍 / 👎.
| date=date_str, | ||
| ) | ||
|
|
||
| slug = args.address.lower().replace(" ", "-").replace(",", "") |
There was a problem hiding this comment.
Sanitize listing slug to keep writes inside listings dir
The listing filename slug only strips spaces and commas from --address; path separators and .. survive, so out_path can resolve outside LISTINGS_DIR (for example via an address like ../../foo), allowing writes to unintended locations instead of the listings folder.
Useful? React with 👍 / 👎.
0f6c6dd to
3cfa58c
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3cfa58cd2f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| # --------------------------------------------------------------------------- | ||
|
|
||
| def load_config(client: str) -> dict: | ||
| path = CLIENTS_DIR / client / "config.json" |
There was a problem hiding this comment.
Validate --client before joining filesystem paths
--client is used directly in path joins, so absolute paths or traversal values (for example /tmp/x or ../../x) escape the managed clients/ tree; commands like --log and --invoice then read/write config.json, log.json, and invoice files outside ~/openclaw-work/freelance, which is an arbitrary file access risk whenever the CLI input is malformed or user-controlled.
Useful? React with 👍 / 👎.
| leads = load_leads() | ||
| today = dt.date.today().isoformat() | ||
| active = [l for l in leads if l["status"] not in ("closed", "lost")] | ||
| due = [l for l in active if l.get("next_followup") and l["next_followup"] <= today] |
There was a problem hiding this comment.
Parse follow-up dates before due-date comparisons
Follow-up dates are stored as free-form strings and later compared lexicographically (next_followup <= today), so non-normalized inputs like 2026-2-1 are treated as later than 2026-04-07 and never surface as overdue; this silently breaks the --follow-ups reminder workflow for users who provide valid calendar dates in non-zero-padded formats.
Useful? React with 👍 / 👎.
| if lead["id"] == lead_id or lead["id"].startswith(lead_id): | ||
| return lead |
There was a problem hiding this comment.
Reject ambiguous lead ID prefixes during lookup
find_lead returns the first lead whose UUID starts with the supplied prefix, so --update-lead --id <short-prefix> can silently update the wrong record when multiple leads share that prefix; this causes data corruption in status/follow-up/notes updates instead of forcing the user to provide a unique or full ID.
Useful? React with 👍 / 👎.
CI failure is pre-existing — not introduced by this PRThe Verified locally: running the same lint on This PR only adds files under |
Adds a new skill for tracking freelance hours and generating markdown invoices. Supports multiple clients, hourly rate config, time logging, weekly/monthly summaries, and invoice generation with billed-entry tracking. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a skill for real estate agents running OpenClaw. Features: - Lead management with status pipeline (prospect → active → offer → closed/lost) - Daily follow-up surfacing so no client falls through the cracks - Property listing draft generator with professional/warm/luxury tones - Weekly pipeline report with estimated deal value Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3cfa58c to
ca3d5f1
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ca3d5f146a
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| ) | ||
|
|
||
| slug = args.address.lower().replace(" ", "-").replace(",", "") | ||
| out_path = LISTINGS_DIR / f"{slug}_{date_str}.md" |
There was a problem hiding this comment.
Preserve listing drafts with unique output filenames
Save paths are keyed only by normalized address and current date, so generating multiple drafts for the same property on the same day (for example after changing tone or features) silently overwrites the previous file and loses work; include a unique suffix (timestamp or sequence) in the filename to keep each draft.
Useful? React with 👍 / 👎.
| project = rest.strip() | ||
| description = "" | ||
|
|
||
| entry_date = date or dt.date.today().isoformat() |
There was a problem hiding this comment.
Enforce YYYY-MM-DD when accepting backfilled log dates
--date is documented as YYYY-MM-DD, but the value is stored verbatim without validation, which lets malformed dates (for example 2026-4-2 or 2026-99-01) enter log.json; later week/month filters rely on string comparisons, so these entries can be misclassified and produce inaccurate summaries/invoice timelines.
Useful? React with 👍 / 👎.
… and fetch-guard tests Cast buildPendingPayload/buildResolvedResult return values to concrete payload shapes in slack and telegram runtime tests to fix TS18046 'payload is of type unknown' errors. Add curly braces around single-line if bodies in fetch-guard ssrf test to satisfy the eslint curly rule. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 16d4e490d4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if args.week: | ||
| today = dt.date.today() | ||
| week_start = (today - dt.timedelta(days=today.weekday())).isoformat() | ||
| entries = [e for e in entries if e["date"] >= week_start] |
There was a problem hiding this comment.
Restrict --week summaries to the current week window
The --week filter currently keeps entries where date >= week_start, which also includes future-dated logs (for example, a typo like 2026-05-01 when run on 2026-04-07). That inflates the “current week” totals and can mislead billing decisions based on this summary; the filter should bound dates to the intended week range (or at least <= today) after date parsing.
Useful? React with 👍 / 👎.
Summary
real-estate-assistantskill purpose-built for real estate agents using OpenClawprospect → active → offer → closed/lostUsage
Test plan
--add-leadcreates lead with correct fields and UUID--update-leadupdates status, follow-up, and appends notes--follow-upsshows leads due today or overdue, skips closed/lost--pipelinegroups by status and calculates active pipeline value--draft-listinggenerates markdown with correct tone and saves to listings dir--weekfilter works on pipeline🤖 Generated with Claude Code