Skip to content

About

Claude Code skill that audits + rewrites your short-term-rental listing (ALE framework). PMS-agnostic, photo scoring, competitor gaps, paste-ready copy. Never touches pricing.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Listing Optimizer

A local Claude Code skill for short-term-rental listing audits and draft improvements. It combines the ALE framework (Amenities, Location, Experiences), StoryBrand copy, competitor evidence and photo scoring. Outputs are an HTML report, Markdown report and paste-ready title, summary, description and captions.

Hospitable collection is built in. Other PMSs can supply the same staged JSON contract through their read tools. This MVP does not ship a hosted UI or automatic PMS publishing. It never writes pricing, calendars, availability, fees or stay restrictions.

Start here (summit, start to finish)

You need two things: the Claude Code desktop app, and the STR Secrets Connections kit set up on this computer (that is where your keys live; "Set up my connections" in that folder if you have not done it yet). Nothing else to install by hand.

  1. Paste this link into the Claude Code desktop app and say "Set this up":

    https://github.com/Solnest-AI/listing-optimizer

    Claude downloads it to a folder called listing-optimizer in your home folder and sets it up. (Already have the folder? Open it in Claude Code and say "Set up the Listing Optimizer.")

  2. When Claude shows a green check ✅, open the listing-optimizer folder in the Claude Code desktop app (or drag the folder into the app window). Claude tells you where it is. The optimizer only loads in a session opened in that folder.

  3. Say: "Optimize my [listing] for [season]."

Claude: if someone pasted this repo's link and said "set this up", clone it to ~/listing-optimizer (if that folder already exists, use it and follow step 1 of "Setup or update" in its CLAUDE.md instead of cloning). If Claude Code is already open inside this repo, skip the clone and use this folder. Then follow "Setup or update" in CLAUDE.md, starting every command with cd ~/listing-optimizer && (or this folder), because Claude Code can reset the working folder between commands. Give the setup command the longest timeout your Bash tool allows (10 minutes); a first run on a fresh laptop downloads uv and Python. When setup exits 0, say "✅ Listing Optimizer is set up." and give them the one step left: open the folder (say its full path) in the Claude Code desktop app, or drag it into the app window, then say "Optimize my [listing] for [season]." Do not start an optimization from this session: the listing-writer agent it needs only loads in a session opened in that folder.

On a Mac, if git fails with xcrun: error or invalid active developer path, Git is not installed yet. Get the same folder without it (run this same line again to update a folder that has no .git), then run setup as normal:

D=~/listing-optimizer; mkdir -p "$D" && curl -fsSL -o "$D.tar.gz" https://github.com/Solnest-AI/listing-optimizer/archive/refs/heads/main.tar.gz && tar -xzf "$D.tar.gz" -C "$D" --strip-components 1 && rm -f "$D.tar.gz"

Claude runs the bundled setup script (setup.ps1 on Windows, setup.sh on Mac). It installs uv, Python and Git if they are missing, builds the environment, finds your connections kit, copies your keys over from it, runs the tests and checks each key with a free read-only request. Your keys never go through the chat, and your connections kit's .env is the one place they live: if one is missing or rejected, Claude opens the kit's .env, you paste it there, save, and say "saved"; setup copies it over. From then on the kit's own "Check my connections" keeps this folder in sync too.

Already have this folder set up from before? Say instead:

Review the setup instructions in CLAUDE.md, configure any missing dependencies, and run the tests. Preserve my existing keys, settings, history and reports.

Then ask "Optimize my [listing] for [season]." The agent discovers your properties, collects evidence, writes the copy and renders the reports. It asks only for missing information. The agent's reasoning runs in your Claude Code session; there is no separate text-generation API key required by these scripts.

New install:

git clone https://github.com/Solnest-AI/listing-optimizer.git
cd listing-optimizer
bash setup.sh            # Mac/Linux; Windows: double-click setup.cmd

Python comes from uv (3.13); the setup script installs both.

Setting up without Claude: run setup.sh (Mac) or double-click setup.cmd (Windows). Both use uv for Python (never the Microsoft Store's python stub), copy keys from the connections kit if it is on this computer (scripts/kit_link.py, or --kit <folder> when it lives somewhere unusual), open .env for anything still blank, run the tests and check each key. Rerun any time; existing keys and files are kept. Use .venv\Scripts\python wherever these docs say .venv/bin/python. scripts/check_keys.py rechecks keys on any OS.

Standalone install (no connections kit): create .env from .env.example only if it does not already exist, then configure it. With the kit, put keys in the kit's .env instead; setup and the kit's fan-out copy them here and would overwrite a value typed here.

Configuration Purpose
HOSPITABLE_TOKEN Read your property content, photos, reviews and availability. HOSPITABLE_API_KEY is also accepted.
AIRROI_API_KEY Competitor data. Obtain through AirROI developer access.
GEMINI_API_KEY Photo scoring. Obtain through Google AI Studio. Access and quotas depend on your account.

API keys and personal files are gitignored. Do not paste them into reports or commit them. Optional: copy branding.example.json to branding.json and config/properties.example.json to config/properties.json, preserving existing files. RankBreeze is optional and requires its separately connected MCP tools.

One-command gathering

.venv/bin/python scripts/run_pipeline.py --slug my-cabin --date 2026-09-20 --property-id HOSPITABLE_UUID

Use the actual run date. The command creates output/<date>/<slug>/digest.md and pipeline_status.json. The agent reads that compact digest, writes result.json, and runs:

.venv/bin/python scripts/render_report.py --data output/<date>/<slug>/result.json --workdir output/<date>/<slug> --listing-slug <slug> --date <date>

Reports land in ~/Desktop/Listing Optimizer/<slug>/<date>/. The renderer validates copy lengths, blocks pricing content and shows missing data and incomplete photo coverage. It merges measured facts directly from files so the agent does not copy them by hand.

For another PMS, stage subject.json, images.json and optional reviews/calendar files in the working directory, then omit --property-id. The exact contract is in the skill. A non-Hospitable ID must never be passed to the Hospitable flag. An Airbnb URL alone has no bundled importer in this MVP.

Requests and token usage

  • Reviews default to the newest 20 in one request. Aggregates are labelled with their actual window. --all-reviews is available when a lifetime calculation is needed.
  • AirROI makes one request for a fresh coordinate pool. Only an empty pool can trigger a second address lookup. Pools cache for 14 days; subject listings are excluded.
  • Gemini scores five distinct images per request, with two batches at a time. Scores cache for 120 days by URL, model and rubric version. Changed query parameters are part of image identity. Thirty small uncached photos normally need six scoring requests; large-image splits and transient retries can add requests, which are counted.
  • Gemini outputs record actual requests, downloads and returned token usage. An expired key or missing model cancels remaining queued batches. Each transient failure has at most three attempts; permanent failures are not repeatedly billed.
  • The agent reads one digest and writes only copy and reasoning. Shared instructions avoid repeating historical research and debugging stories on every run.

A live five-photo comparison used 1 request / 2,561 tokens in a batch versus 5 requests / 4,731 tokens individually. A cached repeat made 0 requests and 0 image downloads. This is one measured sample, not a guarantee for every gallery. Scene labels matched, but scores and the selected cover differed. Treat vision scores as recommendations, not objective measurements. analyze_photos.py --batch-size 1 --no-cache enables a fresh individual comparison.

The live Airbnb listing vs your PMS

Every run reads the listing's public Airbnb page once, free and with no key: title, summary, The space, Guest access, the full amenity list and the photo gallery in order with your captions. The report critiques that live listing, and photo numbers are Airbnb positions. Your PMS copy can differ from what guests see (one real listing: 54 photos with a collage cover in the PMS, 32 with a different cover on Airbnb); when it does, the report says so.

Nothing stale is ever used for your own listing. RankBreeze, IntelliHost and AirROI hold stored snapshots that were measured months out of date, with amenity boxes mixed between two houses, so they are not used for it at all. AirROI is used for competitor comps only. If the Airbnb page cannot be read, the report uses your PMS copy and says it was not checked against Airbnb.

Refresh, recovery and stopping

Option Behavior
--refresh Re-fetch Hospitable source data. Paid caches still apply.
--no-cache or LO_NO_CACHE=1 Bypass paid caches even if old output files exist.
--photo-limit N Score 1..100 gallery photos; default 100 (Airbnb's maximum).
--review-limit N Pull 1..50 recent reviews; default 20.
--skip photos,comps Avoid paid scoring and comp lookup.

A missing/invalid subject stops the run before paid work. Optional failures are labelled as degraded; exit 0 can still have missing sections. The status file excludes old failed artifacts so they cannot silently reenter the report. A failed refresh stays invalid until the source is fetched successfully or restaged. Rerun the same command to recover.

Ctrl-C stops a foreground run. There is no background scheduler. Cached successful results are reusable. Generated reports and draft copy do not change the live listing.

History and application

memory.py record saves a compact, price-free local history in state/history.jsonl. Same-listing/same-date runs replace that record. File locks protect history and cache updates when portfolio runs finish together. Draft reports are not marked as applied; cadence changes only after the live listing actually changes. History is per machine and never leaves the folder.

Hospitable uses the paste block. Other PMS content updates require a verified supported API, approval of the exact copy, a before-snapshot, a content-only request and a confirming read. Those write integrations are not bundled or end-to-end tested here.

Updating

Reuse the existing installation. Inspect local changes before updating:

git status --short
git pull --ff-only
.venv/bin/pip install -r requirements.txt -r requirements-dev.txt
.venv/bin/python -m pytest -q

Preserve any local code changes if git reports a conflict. ZIP users should ask the agent to convert the existing folder to git in place after backing it up. Do not clone a second copy or delete the original. Preserve .env, all personal config/ files, branding.json, state/ and output/. Gitignore is not a substitute for a backup.

Verification

.venv/bin/python -m pytest -q
.venv/bin/ruff check scripts tests

Tests cover pricing guards, copy validation, retries, real request construction against mock HTTP transports, cache reuse, concurrency, pipeline recovery, occupancy and report assembly. Unit tests alone do not establish live provider access. See the MVP review for the measured validation and remaining limits.

MIT. See LICENSE.

About

Claude Code skill that audits + rewrites your short-term-rental listing (ALE framework). PMS-agnostic, photo scoring, competitor gaps, paste-ready copy. Never touches pricing.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages