From sell-stuff
Manage existing listings across Craigslist and Facebook Marketplace. Use this skill when the user wants to mark items as sold, sync sold status to platforms, remove old listings, revise prices on stale items, check what's still listed, or generally manage their selling inventory. Trigger phrases include: "I sold the...", "mark as sold", "update my listings", "lower the price", "revise prices", "what hasn't sold yet", "remove that listing", "delete the posting", "sync my inventory", or "clean up sold items".
How this skill is triggered — by the user, by Claude, or both
Slash command
/sell-stuff:listing-managerThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill manages existing listings after they've been posted. It complements the `sell-stuff` skill (which handles creating and posting new listings) by handling the lifecycle after posting: syncing sold items across platforms and revising prices on stale listings.
This skill manages existing listings after they've been posted. It complements the sell-stuff skill (which handles creating and posting new listings) by handling the lifecycle after posting: syncing sold items across platforms and revising prices on stale listings.
Two core workflows:
inventory-tracker.xlsx spreadsheet must exist in the workspace with the standard column layout (see below)sell-stuff skill workflow| Col | Header | Key Values |
|---|---|---|
| A | Item # | Sequential number |
| B | Item Name | Descriptive name |
| C | Brand | Brand/manufacturer |
| D | Model | Model name/number |
| E | Condition | Good, Fair, New, etc. |
| F | Suggested Price | Current asking price |
| G | Photos Folder | Relative path (e.g., 04-epson-v370-scanner) |
| H | Status | Draft / Listed / Sold |
| I | FB Marketplace | Listed / Sold / Not Listed |
| J | Craigslist | Listed / Sold / Not Listed |
| K | Date Listed | YYYY-MM-DD |
| L | Date Sold | YYYY-MM-DD |
| M | Sold Price | Actual selling price |
| N | Notes | Change log and details |
The user says something like:
Parse the user's message to extract:
If any detail is missing, ask the user. At minimum you need the item and the sale price.
Look up the item in inventory-tracker.xlsx to confirm it exists and has Status = "Listed". If the item is already "Sold" or "Draft", warn the user.
import openpyxl
from datetime import date
wb = openpyxl.load_workbook('inventory-tracker.xlsx')
ws = wb.active
# Find the item row by Item # or Item Name
target_row = None
for row in range(2, ws.max_row + 1):
item_num = ws.cell(row=row, column=1).value
item_name = ws.cell(row=row, column=2).value
if item_num == ITEM_NUMBER or (item_name and SEARCH_TERM.lower() in str(item_name).lower()):
target_row = row
break
if target_row:
ws.cell(row=target_row, column=8).value = 'Sold' # Status
ws.cell(row=target_row, column=9).value = 'Sold' # FB Marketplace
ws.cell(row=target_row, column=10).value = 'Sold' # Craigslist
ws.cell(row=target_row, column=12).value = date.today().isoformat() # Date Sold
ws.cell(row=target_row, column=13).value = SOLD_PRICE # Sold Price
# Append to Notes
existing_notes = ws.cell(row=target_row, column=14).value or ''
new_note = f"Sold {date.today().isoformat()} for ${SOLD_PRICE}."
ws.cell(row=target_row, column=14).value = (existing_notes + ' ' + new_note).strip()
wb.save('inventory-tracker.xlsx')
Navigate to the user's Craigslist account postings:
https://accounts.craigslist.org/login/homeread_page to find the listing by matching the title from the trackerIf the listing is not found: It may have expired (CL posts expire after ~30 days) or been manually deleted. Log this in Notes: "CL listing not found (may have expired)." and continue — don't fail the whole workflow.
Navigate to the user's Facebook Marketplace listings:
https://www.facebook.com/marketplace/you/sellingread_page or find to locate the listing by titleIf "Mark as Sold" is not available, try deleting the listing instead via the same three-dot menu → "Delete". Either outcome removes it from active listings.
If the listing is not found: Log "FB listing not found (may have been removed)." in Notes and continue.
After both platforms are synced, clean up the photos folder:
inventory/[folder-name]/ since this item is sold. The photos will be permanently removed. Confirm?"import shutil
import os
folder_path = f'inventory/{PHOTOS_FOLDER}'
if os.path.exists(folder_path):
shutil.rmtree(folder_path)
print(f"Deleted {folder_path}")
Summarize what was done:
The user says something like:
Read inventory-tracker.xlsx and find all items where Status = "Listed":
import openpyxl
from datetime import date, datetime
wb = openpyxl.load_workbook('inventory-tracker.xlsx')
ws = wb.active
today = date.today()
stale_items = []
for row in range(2, ws.max_row + 1):
status = ws.cell(row=row, column=8).value
if status != 'Listed':
continue
item_num = ws.cell(row=row, column=1).value
item_name = ws.cell(row=row, column=2).value
current_price = ws.cell(row=row, column=6).value
date_listed_raw = ws.cell(row=row, column=11).value
# Parse date — could be string or datetime object
if isinstance(date_listed_raw, datetime):
date_listed = date_listed_raw.date()
elif isinstance(date_listed_raw, str):
date_listed = datetime.strptime(date_listed_raw, '%Y-%m-%d').date()
else:
continue # Skip if no date
days_listed = (today - date_listed).days
stale_items.append({
'row': row,
'item_num': item_num,
'name': item_name,
'price': current_price,
'date_listed': date_listed,
'days_listed': days_listed,
})
Apply progressive discount tiers based on how long the item has been listed:
| Days Listed | Suggested Discount | Rationale |
|---|---|---|
| 0-6 days | No change | Too early to revise |
| 7-13 days | 10-15% off | First price drop to generate renewed interest |
| 14-20 days | 20-25% off | More aggressive — item is getting stale |
| 21+ days | 30-40% off | Need to move it — consider below-market pricing |
import math
def suggest_new_price(current_price, days_listed):
"""Returns (suggested_price, discount_pct) or None if too fresh."""
if days_listed < 7:
return None # Too fresh
elif days_listed < 14:
discount = 0.12 # ~12% off
elif days_listed < 21:
discount = 0.22 # ~22% off
else:
discount = 0.35 # ~35% off
new_price = math.floor(current_price * (1 - discount))
# Floor: never suggest less than $5 for items originally $10+
if current_price >= 10 and new_price < 5:
new_price = 5
return (new_price, round(discount * 100))
Show all stale items in a clear summary:
Here's what I'd suggest for your unsold items:
| # | Item | Current | Days Listed | Suggested | Drop |
|---|------|---------|-------------|-----------|------|
| 1 | De'Longhi Heater | $30 | 6 days | — (too fresh) | — |
| 2 | B&D Toaster | $8 | 6 days | — (too fresh) | — |
| 4 | Epson Scanner | $40 | 12 days | $35 | 12% |
| 9 | IKEA Mattress | $150 | 0 days | — (too fresh) | — |
Items needing revision: #4 (Epson Scanner)
Want me to update the price on #4 to $35 across Craigslist and Facebook Marketplace?
Bundle awareness: If related items are both stale (e.g., MALM nightstand and MALM chest), mention it: "Items #11 and #12 are both IKEA MALM birch pieces. You could also consider a deeper bundle discount to move them together."
Wait for the user to approve, decline, or set a custom price for each item.
For each approved price revision:
https://accounts.craigslist.org/login/homeprice field and update it:// Use form_input for the price field, or if that fails:
const priceInput = document.querySelector('input[name="price"]');
priceInput.value = '';
priceInput.focus();
Then type the new price, or use form_input with the ref for the price field.
PostingBody)https://www.facebook.com/marketplace/you/sellingform_input if possible, otherwise use JavaScript:// For FB's React-controlled price input:
const priceInput = document.querySelector('input[aria-label="Price"]')
|| document.querySelector('input[placeholder*="Price"]');
if (priceInput) {
const nativeSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype, 'value'
).set;
nativeSetter.call(priceInput, 'NEW_PRICE');
priceInput.dispatchEvent(new Event('input', { bubbles: true }));
priceInput.dispatchEvent(new Event('change', { bubbles: true }));
}
For each revised item:
# Update price
ws.cell(row=target_row, column=6).value = NEW_PRICE # Suggested Price
# Log the change in Notes
existing_notes = ws.cell(row=target_row, column=14).value or ''
revision_note = f"Price revised {date.today().isoformat()} from ${OLD_PRICE} to ${NEW_PRICE} ({DAYS_LISTED} days listed)."
ws.cell(row=target_row, column=14).value = (existing_notes + ' ' + revision_note).strip()
wb.save('inventory-tracker.xlsx')
Price revisions complete:
| # | Item | Was | Now | CL | FB |
|---|------|-----|-----|----|----|
| 4 | Epson Scanner | $40 | $35 | Updated ✓ | Updated ✓ |
| 8 | Dressing Stick | $8 | $5 | Updated ✓ | Updated ✓ |
Items declined: #3 (Keyboard)
Tracker updated with all changes.
If the user just wants to know what's going on with their inventory (e.g., "what's still listed?" or "show me my inventory status"), read the tracker and present a summary without taking any action:
Your inventory status:
Listed (11 items): $296 total asking price
- Items #1-5, #7-12 — listed on both CL and FB
Sold (1 item): $10 revenue
- Item #6 (Resistance Bands) — sold 2026-03-02 for $10
Oldest unsold: Items #1-8 (listed 6 days ago)
Newest: Items #9-12 (listed today)
read_page shows a login form instead of account postings, ask the user to log in first. Don't attempt to enter credentials.read_page to inspect the current page structure and adapt. If you truly can't find the right elements, give the user a direct link to manually update.from datetime import datetime
if isinstance(val, datetime):
d = val.date()
elif isinstance(val, str):
d = datetime.strptime(val, '%Y-%m-%d').date()
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub mohammadshamma/sell-stuff-plugin --plugin sell-stuff