From sell-stuff
End-to-end workflow for listing items for sale on Craigslist and Facebook Marketplace. Use this skill whenever the user wants to sell something, list items online, post to Craigslist or Facebook Marketplace, organize photos of things to sell, create selling listings, manage an inventory of items for sale, or track what they've listed and sold. Also trigger when the user drops photos of items and mentions selling, pricing, or getting rid of stuff. Even casual phrases like "I want to get rid of this", "how much could I get for this", "help me sell my old furniture", or "post this on marketplace" should activate this skill.
How this skill is triggered — by the user, by Claude, or both
Slash command
/sell-stuff:sell-stuffThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
This skill walks you through the complete process of helping someone sell their stuff online. The workflow has been refined through real-world use and includes hard-won workarounds for browser automation quirks on both Craigslist and Facebook Marketplace.
This skill walks you through the complete process of helping someone sell their stuff online. The workflow has been refined through real-world use and includes hard-won workarounds for browser automation quirks on both Craigslist and Facebook Marketplace.
The end-to-end flow:
inbox/ once items are successfully postedWhen the user provides photos of items to sell:
iPhone photos are often in HEIC format. Convert them to JPG for compatibility with listing platforms:
import pillow_heif
from PIL import Image
heif_file = pillow_heif.read_heif("photo.HEIC")
image = Image.frombytes(heif_file.mode, heif_file.size, heif_file.data, "raw")
image.save("photo.jpg", "JPEG", quality=92)
Install the library if needed: pip install pillow-heif --break-system-packages
Note: ImageMagick's convert command often fails with HEIC files on this system — use the Python approach above instead.
Organize photos into numbered folders inside inventory/:
sell-stuff/
├── inventory/
│ ├── 01-item-name/
│ │ ├── IMG_1234.jpg
│ │ └── IMG_1235.jpg
│ ├── 02-another-item/
│ │ └── IMG_1236.jpg
│ └── ...
├── inventory-tracker.xlsx
└── listings.md
Use the next available number. Check what folders already exist to determine the next number. Use lowercase-kebab-case for the item name portion.
Save all listings to listings.md with this format for each item:
## [Number]. [Item Name] — $[Price]
**Title:** [Concise, searchable title — include brand, key specs]
**Description:**
[2-4 paragraphs. Lead with what it is. Include brand, model, key features,
dimensions if furniture. Be upfront about any flaws. End with pickup location
and payment methods.]
**Condition:** [Used — Good | Used — Fair | New In Box | etc.]
**Photos:** [Number] photos (inventory/[folder-name]/)
The goal is to help the user sell quickly at a fair price. Good listings are:
The browser file upload tool does NOT work for uploading photos from the VM filesystem to websites — it returns a permissions error. Instead:
inventory/ subfolders)inventory/09-ikea-anneland-mattress/ folder into the upload area, then let me know when you're done."This is the most reliable approach and has been tested extensively. Do not attempt to use the file_upload tool for listing photos.
Posting URL: Navigate to https://post.craigslist.org/ and select the appropriate city/area.
Category selection: Choose "for sale by owner" → appropriate subcategory (furniture, electronics, etc.)
Form fields (these are the actual HTML field names):
PostingTitle — The listing titleprice — Price in dollars (numbers only, no $ sign)PostingBody — The full description textsale_manufacturer — Brand name (if applicable)sale_model — Model name/number (if applicable)sale_size — Dimensions or size (if applicable)condition — A dropdown select elementCondition dropdown workaround: The form_input tool sometimes doesn't reliably set the Craigslist condition dropdown. Use JavaScript instead:
const sel = document.querySelector('select[name="condition"]');
sel.value = '[VALUE]';
sel.dispatchEvent(new Event('change', {bubbles: true}));
Condition values: 10 = new, 20 = like new, 30 = excellent, 40 = good, 50 = fair, 60 = salvage
Craigslist posting flow:
Posting URL: Navigate to https://www.facebook.com/marketplace/create/item
The description typing problem: Facebook Marketplace uses React-controlled form fields. The regular type action frequently fails partway through with a "Detached while handling command" error, causing text duplication and corruption. This happens because Facebook's React event handlers interfere with synthetic keyboard input on textareas.
The fix — use JavaScript to set text values for descriptions:
const textarea = document.querySelector('textarea');
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLTextAreaElement.prototype, 'value'
).set;
nativeInputValueSetter.call(textarea, `YOUR DESCRIPTION TEXT HERE`);
textarea.dispatchEvent(new Event('input', { bubbles: true }));
textarea.dispatchEvent(new Event('change', { bubbles: true }));
This approach bypasses React's synthetic event system by calling the native HTMLTextAreaElement value setter directly, then dispatching native DOM events that React's reconciler picks up. It works reliably where keyboard simulation doesn't.
For regular input fields (title, price), the normal type action or form_input tool usually works fine — the textarea/description is the problematic one.
Facebook Marketplace posting flow:
Category selection on Facebook: Type the category name into the category search field, wait for the dropdown to appear, then click the matching result. Common categories: "Bedroom Furniture Sets", "Dressers", "Nightstands", "Electronics", "Home Appliances", "Sporting Goods".
Maintain an inventory-tracker.xlsx spreadsheet with these columns:
| Column | Header | Description |
|---|---|---|
| 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 | Asking price |
| G | Photos Folder | Relative path to inventory folder |
| 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 (when sold) |
| M | Sold Price | Actual selling price |
| N | Notes | Any additional notes |
Add summary rows at the bottom with formulas for: Total Estimated Value (SUM of prices), Items Listed (COUNTIF status="Listed"), Items Sold (COUNTIF status="Sold"), Total Revenue (SUM of sold prices).
Use openpyxl to create/update the spreadsheet:
import openpyxl
wb = openpyxl.load_workbook('inventory-tracker.xlsx')
ws = wb.active
# Update status for newly listed items
for row in range(start_row, end_row + 1):
ws.cell(row=row, column=8).value = 'Listed' # Status
ws.cell(row=row, column=9).value = 'Listed' # FB Marketplace
ws.cell(row=row, column=10).value = 'Listed' # Craigslist
ws.cell(row=row, column=11).value = '2026-03-08' # Date Listed
wb.save('inventory-tracker.xlsx')
If the spreadsheet doesn't exist yet, create it with the headers and formatting above.
The inbox/ folder is where users drop raw photos (often as zip files or loose HEIC/JPG files) before processing. Once items have been successfully posted to all requested platforms, clean up the source files from the inbox:
inbox/Photos-march-8-2026.zip contained the mattress and dresser photos).inbox/ — these are the zip archives or raw photo files the user originally dropped in. The converted JPGs in the inventory/ folders are the permanent copies and should NOT be deleted.Photos-march-8-2026.zip from your inbox since all those items are now listed."import os
# After all items from a source file are posted:
inbox_file = 'inbox/Photos-march-8-2026.zip'
if os.path.exists(inbox_file):
os.remove(inbox_file)
print(f"Cleaned up {inbox_file}")
If the inbox file contains photos for multiple items and only some have been posted, do NOT delete it yet — wait until all items from that file are posted.
Not every run needs to hit all five steps. Common variations:
Adapt to what the user needs rather than rigidly following every step.
read_page to inspect the current form structure and adaptCreates, 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