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", download it, do not git clone it. A clone stays linked to Solnest-AI's GitHub, and at the 2026-09-29 summit a student's "save" turned into a push to our repo. This line puts it in ~/listing-optimizer (no Git needed):

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"

If that folder already exists, do not download over it: use it and follow "Setup or update" in its CLAUDE.md. Already cloned it? Setup unlinks an untouched clone. If Claude Code is already open inside this repo, use this folder. Never commit, push, fork or open a pull request for this folder: saving means the files on disk. 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.

Claude runs the bundled setup script (setup.ps1 on Windows, setup.sh on Mac). It installs uv and Python if they are missing (and Git on Windows, because Claude Code uses its Git Bash), unlinks the folder from GitHub, 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 without Claude (download, never git clone):

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"
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 (see the free tier note below).

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.
  • Photos are scored with gemini-3.8-flash (Google closed gemini-2.5-flash to new keys in September 2026). Leave GEMINI_MODEL out of .env unless you have a reason; the built-in default is kept current. Thinking is turned off, so each photo costs only its image and a short answer.
  • If Google refuses the request itself (a retired model, or a setting it no longer accepts), the photo step stops and says what to fix. Those photos are not handed to Claude to score instead, because the same error would come back on every run.

Gemini free tier. A free Gemini key is enough to start, but its limits are small: about 5 requests a minute and a small daily cap on gemini-3.8-flash (Google sets these and changes them; see Google's rate limits). One request scores 5 photos, so a 72-photo gallery needs about 15 requests and a large gallery can use up the day's free requests partway through. Nothing is lost when that happens: scores already made are kept, and the run tells you to rerun after midnight Pacific. Adding billing to the key in Google AI Studio raises the limits. A model with no free tier at all (Pro models, for example) is reported as exactly that, not as a used-up daily quota.

  • 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

Say "update the listing optimizer" in Claude Code, in this folder. Claude runs scripts/update.py, which downloads the latest release from GitHub and replaces the tool's own files, then runs setup (new packages, self-test, key check). It never touches your .env, branding.json, config/properties.json, config/memory.json, state/ (history and caches) or output/ (reports). Every file it replaces is saved in .update-backup/ first, and if you had edited one it tells you which. To go back, say "undo the listing optimizer update" (scripts/update.py --undo).

When a newer release is out, the end of each optimization run says "Update available" (checked at most once a day; LO_NO_UPDATE_CHECK=1 turns the check off). What changed in each release is on the releases page, in plain English.

Your copy is a plain folder, not linked to GitHub: setup unlinks a git clone the first time it runs, so nothing you do here can be pushed to Solnest-AI. Saving means the files on disk, and your reports are already in ~/Desktop/Listing Optimizer. A copy you cloned before this release updates once with git pull --ff-only; the setup run after that unlinks it.

Developers: keep git on a clone with touch .git/str-keep-attached before running setup (or STR_KEEP_GIT=1 for one run), and update with git pull --ff-only. Setup never unlinks a copy with local edits, branches, unpushed commits or stashes.

Releasing (maintainers): bump VERSION, merge, then tag vX.Y.Z on main and publish a release with plain-English notes. Students' copies only see tagged releases, never untagged work on main.

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